App优化 - ANR优化

前言

  1. App优化 – 需要优化哪些?
  2. App优化 – 性能分析工具
  3. App的3种启动方式
  4. App优化 – App启动速度优化
  5. App优化 – 布局优化
  6. App优化 – 消除卡顿优化
  7. App优化 – ANR优化
  8. App优化 – 电池省着用
  9. App优化 – 网络优化

1. ANR长是什么样子?

在开发过程中,可能会遇到这个,

《App优化 - ANR优化》 图片.png

这个就是ANR;

1.1:什么是ANR?

全称是Application Not Responding,即就是 “应用程序无响应”,用户操作一段时间,系统无法处理,就会弹出上边的ANR对话框。

1.2:为什么会产生ANR?

1>:5s没有响应用户输入事件(如:键盘输入、触摸屏幕);
2>:10s没有结束BroadcastReceiver;

造成上边的原因就是:

在主线程(UI线程)做耗时操作,比如:联网请求、读写数据库、读写文件;

1.3:如何避免ANR?

不要在主线程(UI线程)中做耗时操作;

2. ANR分析?

2.1:获取ANR产生的trace文件?

如果发生ANR,系统会生成一个trace.txt文件,放在/data/anr/下边。通过adb命令导入本地:

$adb pull data/anr/traces.txt .

2.2:分析trace.txt?

2.2.1:普通阻塞导致ANR

制造一个ANR,在主线程中做一个耗时操作,使用sleep thread产生一个ANR,

《App优化 - ANR优化》 图片.png

分析trace.txt文件

1>:第一行就是trace信息;
2>:第一行就是发生ANR的进程pid、时间、进程名字(包名);

2.2.2:CPU满载负荷

此时的trace.txt信息可能如下:

《App优化 - ANR优化》 图片.png

由最后一句可知:

1>:CPU占用100%;
2>:绝大多数被I/O操作占用了;
原因可能是在主线程中做了:频繁的文件读写、数据库读写操作;

2.2.3:内存原因

比如点击按钮开启一个Activity,该Activity是用大图片作为背景,可能产生ANR,此时的trace.txt如下:

《App优化 - ANR优化》 图片.png

由最后一句可知:

free内存所剩无几,这种情况可能更多的是OOM;

2.3:ANR如何处理?

1>:针对于主线程阻塞的、CPU满负荷的、I/O阻塞的:
开子线程,来处理耗时操作;
2>:对于内存不够的:
增大VM内存,使用largeHeap属性

3. 深入学习

3.1:哪些地方执行在主线程?

1>:四大组件都是执行在主线程中;
2>:View的post(Runnable)执行在主线程;
3>:AsyncTask除过doInBackground()之外,都是运行在主线程中;
4>:没有使用子线程的looper的Handler的handleMessage()、post(Runnable)是执行在主线程中;

3.2:子线程的几种实现方式?

1>:继承Thread、实现Runnable;

2>:使用AsyncTask,顾名思义就是异步任务;

《App优化 - ANR优化》 图片.png

3>:使用IntentService;

Service运行在主线程,IntentService运行在子线程,使用方式如下:

3.1>:比如在BaseApplication中的onCreate()方法中启动一个IntentService:
/**
 * Email: 2185134304@qq.com
 * Created by JackChen 2018/4/8 16:01
 * Version 1.0
 * Params:
 * Description:
 */
public class BaseApplication extends Application {
    /**
     * 程序启动的时候执行
     */
    @Override
    public void onCreate() {
        Log.d("Application", "onCreate");
        super.onCreate();
        //在子线程中初始化
        InitializeService.start(this);
    }
}
3.2>:自定义一个InitializeService类继承IntentService,代码如下:
/**
 * Email: 2185134304@qq.com
 * Created by JackChen 2018/4/12 15:35
 * Version 1.0
 * Params:
 * Description:
*/
public class InitializeService extends IntentService {

    private static final String ACTION_INIT = "initApplication";

    public static void start(Context context) {
        Intent intent = new Intent(context, InitializeService.class);
        intent.setAction(ACTION_INIT);
        context.startService(intent);
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     * Used to name the worker thread, important only for debugging.
     */
    /*public InitializeService(String name) {
        super(name);
    }*/

    public InitializeService(){
        super("InitializeService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_INIT.equals(action)) {
                initApplication();
            }
        }
    }

    private void initApplication() {
        initBugly();                                    //初始化腾讯bug管理平台
//        BaseConfig.INSTANCE.initConfig();               //初始化配置信息
        LogUtils.logDebug = true;                       //开启日志
    }

    /**
     * 初始化鸿洋大神网络请求框架
     * 项目刚开始时用鸿洋大神的框架【正式项目中用这个】,后来慢慢改成Retrofit
     */
    /*private void initOkHttpUtils() {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                //.addInterceptor(new BaseInterceptor())
                //.addInterceptor(interceptor)
                .addInterceptor(new LogInterceptor("TAG_APP",true))          //打印请求网络数据日志,上线版本就关掉
                .connectTimeout(50000L, TimeUnit.MILLISECONDS)                  //连接超时时间
                .readTimeout(10000L, TimeUnit.MILLISECONDS)                     //读数据超时时间
                .writeTimeout(10000L,TimeUnit.MICROSECONDS)                     //写数据超时时间
                .build();
        OkHttpUtils.initClient(okHttpClient);
    }*/


    /**
     * 初始化腾讯bug管理平台
     */
    private void initBugly() {
        /* Bugly SDK初始化
        * 参数1:上下文对象
        * 参数2:APPID,平台注册时得到,注意替换成你的appId
        * 参数3:是否开启调试模式,调试模式下会输出'CrashReport'tag的日志
        * 注意:如果您之前使用过Bugly SDK,请将以下这句注释掉。
        */
        CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(getApplicationContext());
        strategy.setAppVersion(AppUtils.getAppVersionName());
        strategy.setAppPackageName(AppUtils.getAppPackageName());
        strategy.setAppReportDelay(20000);                          //Bugly会在启动20s后联网同步数据

        /*  第三个参数为SDK调试模式开关,调试模式的行为特性如下:
            输出详细的Bugly SDK的Log;
            每一条Crash都会被立即上报;
            自定义日志将会在Logcat中输出。
            建议在测试阶段建议设置成true,发布时设置为false。*/

        CrashReport.initCrashReport(getApplicationContext(), "4ae3b64456", true ,strategy);

        Log.e("TAG" , "初始化bugly111") ;
    }
}

我这里使用初始化 腾讯的bugly来做演示;

3>:HandlerThread

默认情况下Handler的handleMessage是执行在主线程中,如果给这个Handler传入一个 子线程的looper,handleMessage就会执行在子线程中;

《App优化 - ANR优化》 图片.png

4>:Loader

Android3.0引入数据加载器,支持异步加载数据,可监测数据源发生变化时传递新的结果,常用的是CursorLoader,用于加载数据库数据;

《App优化 - ANR优化》 图片.png

注意:

1>:使用Thread、HandlerThread时,建议把 Thread的优先级设置低一点:

Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);

2>:如果不设置优先级,那么主线程和自己创建的子线程优先级一样,可能会导致主线程阻塞,从而导致ANR;

结语

个人建议:对于ANR还是以预防为主,要有良好的编码习惯,并且在开发前期一定要考虑好所有会发生ANR的地方、哪些地方可能导致ANR、哪些代码可能会导致ANR等等这些情况,这些是我们需要考虑的,要在它还没有发生的时候就考虑的所有的情况,不给它发生的机会和场景,这才是一个优秀的程序员应该做的。

    原文作者:世道无情
    原文地址: https://www.jianshu.com/p/d229f5a87eaf
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞