Android API相容,其他API,UI適配(3)
-- 在application的oncreate方法前
StrictMode.setThreadPolicy(new StrictModel.ThreadPolicy.Builder().detectAll().penaltyLog().build());
執行緒檢測策略; 虛擬機器檢測策略
-- ANR典型的分析情況
1.如果TOTAL的和接近100,有可能是因為當前使用的app佔用的cpu太高,導致系統將你的殺死。
2.如果TOTAL很小,則說明執行緒被阻塞了,主執行緒在等待下條訊息的進入,任務在等待時anr。
3.如果ioWait很高,則說明是io操作導致的
ANR一般有三種類型:
1:KeyDispatchTimeout(5 seconds) --主要型別
按鍵或觸控事件在特定時間內無法得到響應
2:BroadcastTimeout(10 seconds)
BroadcastReceiver在的onRecieve執行在主執行緒中,短時間內無法處理完成導致
3:ServiceTimeout(20 seconds) --小概率型別
Service的各個宣告週期在特定時間內無法處理完成
1.ANR關鍵點:ioWait很高,ContentResolver in AsyncTask onPostExecute
首先看到total中ioWait很高,說明是io操作導致的;
具體原因,可以看到關鍵詞sqlite,ContentResolver
2.ANR關鍵詞OSNetworkSystem.receiveStream,net
3.ANR關鍵詞:VMWAIT,VMRuntime.trackExternalAllocation
-- Service導致的ANR
Service binderService與ANR?
Service 裡面開啟一個執行緒處理網路資料,不要用ipc bindService的方式,用廣播通知資料更新。
問題出在onStartCommand方法中.
Service也是執行在主執行緒的,你在裡邊做耗時操作肯定會anr的 你應該在service 裡邊在開一個子執行緒去做耗時的操作呀.
service anr關鍵方法.
Android O StartService的 anr timeout 流程分析- https://blog.csdn.net/sinat_20059415/article/details/80997425
binderService需要context上下文;跨程序時,只能用StartService(),因為上下文物件對其無用?
寫native的binder service- https://github.com/cloudchou/NativeBinderTest
行內函數模版。
startForegroundService是同步的(阻塞),startService是非同步的(命令一次性下發,不阻塞)。
-- AppOpsManager.checkOpNoThrow()
判斷應用在前後臺的方法-https://github.com/wenmingvs/AndroidProcess
AppLock應用鎖,保護你的隱私- https://github.com/lizixian18/AppLock
使用UsageStatsManager需要獲取許可權相關程式碼:
/**
* 判斷是否已經獲取 有權檢視使用情況的應用程式 許可權
*
* @param context
* @return
*/
public static boolean isStatAccessPermissionSet(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
PackageManager packageManager = context.getPackageManager();
ApplicationInfo info = packageManager.getApplicationInfo(context.getPackageName(), 0);
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, info.uid, info.packageName);
return appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, info.uid, info.packageName) == AppOpsManager.MODE_ALLOWED;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else {
return false;
}
}
/**
* 檢視是存在檢視使用情況的應用程式介面
*
* @return
*/
public static boolean isNoOption(Context context) {
PackageManager packageManager = context.getPackageManager();
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
/**
* 轉跳到 有權檢視使用情況的應用程式 介面
*
* @param context
*/
public static void startActionUsageAccessSettings(Context context) {
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
context.startActivity(intent);
}
獲取棧頂包名的方法有好幾個,根據不同的android版本方法也不一樣,在android5.0以上,推薦使用UsageStatsManager來獲取,具體方法:
public String getLauncherTopApp(Context context, ActivityManager activityManager) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
List<ActivityManager.RunningTaskInfo> appTasks = activityManager.getRunningTasks(1);
if (null != appTasks && !appTasks.isEmpty()) {
return appTasks.get(0).topActivity.getPackageName();
}
} else {
//5.0以後需要用這方法
UsageStatsManager sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long endTime = System.currentTimeMillis();
long beginTime = endTime - 10000;
String result = "";
UsageEvents.Event event = new UsageEvents.Event();
UsageEvents usageEvents = sUsageStatsManager.queryEvents(beginTime, endTime);
while (usageEvents.hasNextEvent()) {
usageEvents.getNextEvent(event);
if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
result = event.getPackageName();
}
}
if (!android.text.TextUtils.isEmpty(result)) {
return result;
}
}
return "";
}
-- Android 5.0 應用使用情況統計資訊
要使用android.app.usage API ,首先必須要在AndroidManifest.xml中宣告許可權,如下:
<uses-permission Android:name="android.permission.PACKAGE_USAGE_STATS" />
然後需要開啟允許檢視使用情況的應用介面,引導使用者授權,如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try { startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)); } catch (Exception e) { e.printStackTrace(); }}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static boolean checkUsagePermission(Context context) {
AppOpsManager appOpsManager = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(),ontext.getPackageName());
return mode == AppOpsManager.MODE_ALLOWED;
}
-- Android許可權管理與AppOpsManager,在SDK 19中Google引入了AppOpsManager
值得一提的是這個api是在19新加入的,所以要注意加個判斷,其實 Android 官方一直有這個設定許可權的入口,Setting---Security---AppOps 只是一直被google隱藏了。
Android許可權管理與AppOpsManager- https://blog.csdn.net/u012526436/article/details/72819034
知許可權管理的功能AppOpsManager,資訊的儲存在“data/system/appops.xml”檔案中
if (Build.VERSION.SDK_INT >= 19){}
而AppOps所管理的是所有可能涉及使用者隱私和安全的操作,包括 access notification, keep weak lock, activate vpn, display toast 等等,有些操作是不需要Manifest裡申請許可權的。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //KITKAT 19
appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int checkResult = appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_FINE_LOCATION, Binder.getCallingUid(), context.getPackageName());
if(checkResult == AppOpsManager.MODE_ALLOWED){
Toast.makeText(context,"有許可權",Toast.LENGTH_LONG).show();
Log.e("jijiaxin","有許可權");
}else if(checkResult == AppOpsManager.MODE_IGNORED){
// TODO: 只需要依此方法判斷退出就可以了,這時是沒有許可權的。
Toast.makeText(context,"被禁止了",Toast.LENGTH_LONG).show();
Log.e("jijiaxin","被禁止了");
}else if(checkResult == AppOpsManager.MODE_ERRORED){
Toast.makeText(context,"出錯了",Toast.LENGTH_LONG).show();
Log.e("jijiaxin","出錯了");
}else if(checkResult == 4){
Toast.makeText(context,"許可權需要詢問",Toast.LENGTH_LONG).show();
Log.e("jijiaxin","許可權需要詢問");
}
}
public boolean selfPermissionGranted(Context context, String permission) {
boolean ret = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (targetSdkVersion >= Build.VERSION_CODES.M) {
ret = context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
} else {
ret = PermissionChecker.checkSelfPermission(context, permission) == PermissionChecker.PERMISSION_GRANTED;
}
}
return ret;
}
在Android5.0,即Lollipop(api level 21)之前,大家都幸福的使用如下程式碼來獲得當前執行的app,即所謂的top Activity:
ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = activityManager.getRunningTasks(1).get(0).topActivity;
Marshmallow(api level 23),M = 23;
//檢測使用者是否對本app開啟了“Apps with usage access”許可權
private boolean hasPermission() {
AppOpsManager appOps = (AppOpsManager)
getSystemService(Context.APP_OPS_SERVICE);
int mode = 0;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), getPackageName());
}
return mode == AppOpsManager.MODE_ALLOWED;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), context.getPackageName());
if (mode == AppOpsManager.MODE_ALLOWED) {
return true;
} else {
if (viewWrapper != null) {
openPermissonActivity(viewWrapper);
}
}
}
獲取棧頂Activity-https://github.com/apkkids/GetTopActivity
在實際開發中,經常需要在程式中開啟一些物理資源,如資料庫連線,網路連線,磁碟檔案等,開啟這些資源之後必須顯示關閉,否則將會引起資源洩露。 JVM不是提供了垃圾回收機制嗎?JVM的垃圾回收機制不會回收這些資源嗎?答案是不會,垃圾回收機制屬於Java記憶體管理的一部分,它只是負責回收堆記憶體中分配出來的記憶體,至於程式中開啟的物理資源,垃圾回收機制是無能為力的。
IO流關閉順序。
一般情況下是:先開啟的後關閉,後開啟的先關閉
另一種情況:看依賴關係,如果流a依賴流b,應該先關閉流a,再關閉流b,例如處理流a依賴節點流b,應該先關閉處理流a,再關閉節點流b
其實Java方法的返回值,跟引數的傳遞一樣,都是基本型別返回值,而非基本型別,則返回引用.
GradientDrawable.setColor
private void setGradientDrawableColor(int[] colors) {
GradientDrawable drawable = null;
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
drawable = new GradientDrawable();
//倆設定方法其實就是對應著帶參構造引數的那倆引數
drawable.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);
drawable.setColors(colors);
}else{
drawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors);
setBackgroundDrawable(drawable);
}
}
GradientDrawable gd = (GradientDrawable) getBackground();
int[] colors = {0xFFFF0000, 0xFFCC0099};
if (android.os.Build.VERSION.SDK_INT >= 16) {
gd = gd.mutate(); // For safe resource handling
gd.setColors(colors);
} else {
// Fallback for APIs under 16.
GradientDrawable ngd = new GradientDrawable(/* Orientation variable */, colors);
// You may have to set other qualities of `ngd` here to make it match.
setBackgroundDrawable(ngd);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metric);
LogUtil.d(TAG, "####### dumpScreenInfo RealMetrics (width, height) = ( "
+ metric.widthPixels + " , " + metric.heightPixels + " )");
}
-- synchronized(Lock)與Lock.notify()
在併發量比較小的情況下,使用synchronized是個不錯的選擇,但是在併發量比較高的情況下,其效能下降很嚴重,此時ReentrantLock是個不錯的方案。
需要注意的是,wait()和notify()必須在synchronized程式碼塊中呼叫。
--
Android4.0之後開始支援WifiDirect技術,即Wifi直連,做為一種通訊方式,它的優勢在於傳輸速度快傳輸距離遠。
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
setBackgroundDrawable( null );
}
else {
setBackground( null );
}
int space = 0;
int columWidth = 0;
int newSpace = 0;
int newColumWidth = 0;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
space = gridView.getVerticalSpacing(); // 統計所有子項的總高度
} else {
space = 0;
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
columWidth = gridView.getColumnWidth();
} else{
Field field = GridView.class.getDeclaredField("mColumnWidth");
field.setAccessible(true);
Integer value = (Integer) field.get(this);
field.setAccessible(false);
columWidth = value.intValue();
}
} catch (Exception ex) {
newSpace = space;
newColumWidth = columWidth;
}
-- Android SDK 升級到 23 之後,getDrawable和getColor方法提示過時。
getResources().getColor 替換成 ContextCompat.getColor
getResources().getDrawable 替換成 ContextCompat.getDrawable
例子如下:
int colorInt = getResources().getColor(R.color.colorAccent);//返回的是color的int型別值:-49023
int colorInt2 = ContextCompat.getColor(this, R.color.colorAccent);//返回的是color的int型別值:-49023
Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher);
Drawable drawable2 = ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
-- BigDecimal的建構函式 public BigDecimal(double val) 損失了double 引數的精度.使用BigDecimal的以String為引數的建構函式:public BigDecimal(String val) 來替代。
-- Handler延遲
if (isPausedBy50() && mRecorderHandler != null) {
mRecorderHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 300);
}