前言
- App优化 - 需要优化哪些?
- App优化 - 性能分析工具
- App的3种启动方式
- App优化 - App启动速度优化
- App优化 - 布局优化
- App优化 - 消除卡顿优化
- App优化 - ANR优化
- App优化 - 电池省着用
- App优化 - 网络优化
1. ANR长是什么样子?
在开发过程中,可能会遇到这个,
这个就是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,
分析trace.txt文件
1>:第一行就是trace信息;
2>:第一行就是发生ANR的进程pid、时间、进程名字(包名);
2.2.2:CPU满载负荷
此时的trace.txt信息可能如下:
由最后一句可知:
1>:CPU占用100%;
2>:绝大多数被I/O操作占用了;
原因可能是在主线程中做了:频繁的文件读写、数据库读写操作;
2.2.3:内存原因
比如点击按钮开启一个Activity,该Activity是用大图片作为背景,可能产生ANR,此时的trace.txt如下:
由最后一句可知:
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,顾名思义就是异步任务;
3>:使用IntentService;
Service运行在主线程,IntentService运行在子线程,使用方式如下:
3.1>:比如在BaseApplication中的onCreate()方法中启动一个IntentService:
/**
* Email: [email protected]
* 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: [email protected]
* 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就会执行在子线程中;
4>:Loader
Android3.0引入数据加载器,支持异步加载数据,可监测数据源发生变化时传递新的结果,常用的是CursorLoader,用于加载数据库数据;
注意:
1>:使用Thread、HandlerThread时,建议把 Thread的优先级设置低一点:
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
2>:如果不设置优先级,那么主线程和自己创建的子线程优先级一样,可能会导致主线程阻塞,从而导致ANR;
结语
个人建议:对于ANR还是以预防为主,要有良好的编码习惯,并且在开发前期一定要考虑好所有会发生ANR的地方、哪些地方可能导致ANR、哪些代码可能会导致ANR等等这些情况,这些是我们需要考虑的,要在它还没有发生的时候就考虑的所有的情况,不给它发生的机会和场景,这才是一个优秀的程序员应该做的。