-- 在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 流程分析-  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- 
 知权限管理的功能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类型值:-49023Drawable 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);
         }