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);
        }
 

猜你喜欢

转载自blog.csdn.net/ShareUs/article/details/85132757
今日推荐