王学岗ANR异常以、handler机制及其源码详解
2015-12-07 23:11
316 查看
ANR异常
1、什么是ANR异常?答:1)在android中,如果你的应用程序在一段时间内没有响应,那么这个时候系统会向用户显示一个对话框,这个对话框称作应用程序无响应对话框。用户可以在对话框上选择“等待”而让程序继续运行,也可以选择”强制关闭“。
2)在一个正常的app当中是不能出现ANR异常,让用户每一次都要等待处理这个对话框。因此在我们设计应用程序的时候性能设计非常关键,可以避免系统显示ANR对话框。
2、什么情况下会引发ANR异常? 答:应用程序的响应是由ActivityMananger和WindowManager系统服务监听。 1)在5秒之内没有响应输入事件(例如:返回键、屏幕触摸) 2)BroadcastReceiver在10秒内没有执行完毕 例如:在主线程中做了很多耗时操作,包括下载,io异常,图片加载、数据库操作、高耗时的图片尺寸处理、高复杂的视图加载等 3、如何解决? 答:1)运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。(可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等) 2)应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为 BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个 Service。 3)避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。
package com.example.hanglerjizhi; import android.os.Bundle; import android.app.Activity; import android.view.Menu; //ARE异常,由ActivityManager,WindowManager管理! public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); start(); } public void start() { while (true) { System.out.println("0000000000"); } } }
我们看下上面的代码,虽然可以无穷无尽的输出,但却是在主程序里面,但是也会造成ANR异常。
看下面单击事件造成的ANR异常
package com.example.hanglerjizhi; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; //ARE异常,由ActivityManager,WindowManager管理! public class MainActivity extends Activity { private Button bt_click; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt_click = (Button) findViewById(R.id.bt_click); bt_click.setOnClickListener(new OnClickListener() { //点击事件被阻塞住,也会报ANR异常,按钮颜色按下去颜色不会改变, @Override public void onClick(View v) { start(); } }); } public void start() { while (true) { System.out.println("0000000000"); } } }
解决ANR异常可以通过线程搞定。
代码如下
(布局文件就省略了,只有两个控件——一个按钮,和一个textView) package com.example.hanglerjizhi; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.TextureView; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; //ARE异常,由ActivityManager,WindowManager管理! public class MainActivity extends Activity { private Button bt_click; private TextView tv; // 使用handler更新UI。 private Handler handler = new Handler() { public void handleMessage(Message msg) { tv.setText(msg.arg1+""); }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv_text); bt_click = (Button) findViewById(R.id.bt_click); bt_click.setOnClickListener(new OnClickListener() { // 点击事件被阻塞住,也会报ANR异常,按钮颜色按下去颜色不会改变, @Override public void onClick(View v) { start(); } }); } public void start() { //启动子线程刷新UI Thread thread = new zhang_xin_Thread(); thread.start(); } public class zhang_xin_Thread extends Thread { int progress=1; @Override public void run() { super.run(); //死循环,耗时操作 while(true){ progress++; Message msg=handler.obtainMessage(); msg.arg1=progress; //发送到我们的目标对象! msg.sendToTarget(); } } } }
接下来我们就要看看handler源码,是如何进行通信的。
先看下该类的解释
A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}.
可以send and process 消息和Runnable对象,可以吧消息和Runnable对象加到消息队列(MessageQueue);
Each Handler instance is associated with a single thread and that thread's message queue
每个handler实例被创建的时候都必须关联一个线程,还要关联一个thread的消息队列
When you create a new Handler, it is bound to the thread message queue of the thread that is creating it
当你创建一个新的Handler时,它会被绑定到当前的线程,就是说handler在哪个线程创建的,就会被绑定到哪个线程!
from that point on,it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
从那时候开始,它将提供消息和runnable对象,当我们从消息队列中取出来的时候,意思就是说,从那时候开始,当我们的消息队列把我们的消息和runnable对象取出来的时候,就会执行!
以上其实要表达的意思是:就是一个handler,允许你发送message和runnable对象。当你的handler被创建的时候,会去关联一个线程和线程消息队列。当你的handler被创建出来的时候,他会绑定到主线程去。当你绑定到主线程的时候,由于你的消息队列里已经有消息、runnable对象,这时候我们取出runnable对象,或者取出消息。
总结下:
第一:handler允许发消息和runnable对象
第二:handler实例一旦被创建,就会关联一个线程和消息列队,同时会被绑定到当前线程,handler取来的消息和runnable对象就可以执行
看看构造方法
if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } }
注意看治理有一个Looper,Looper就是我们的消息泵(联想下农村的抽水机,类似与我们的消息泵),用来管理消息队列,在此处创建消息泵。
mQueue = mLooper.mQueue;
通过消息泵得到存放消息的消息队列。
大家注意往下看,消息泵是如何遍历消息队列的,
public static void loop() { }
消息泵里面有一个loop()方法,专门用来执行消息,把消息一个个的取出来,这样我们就可以在handler,message里面拿到消息。
Message msg = queue.next(); msg.target.dispatchMessage(msg);
消息泵里面有一个线程,如果没有消息就等待,有消息就分发到主线程
不知道大家看懂了没,接下来我把上一个例子改一下,不用 Thread thread = new zhang_xin_Thread();使用Runnable.
package com.example.hanglerjizhi; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.TextureView; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; //ARE异常,由ActivityManager,WindowManager管理! public class MainActivity extends Activity { private Button bt_click; private TextView tv; // 使用handler更新UI。 private Handler handler = new Handler() { public void handleMessage(Message msg) { }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv_text); bt_click = (Button) findViewById(R.id.bt_click); bt_click.setOnClickListener(new OnClickListener() { // 点击事件被阻塞住,也会报ANR异常,按钮颜色按下去颜色不会改变, @Override public void onClick(View v) { start(); } }); } public void start() { handler.post(new Runnable() { int progress = 0; //细读源码可以发现 run方法在主线程执行,所以可以执行UI更新 //如果不是runnable对象,而是普通的消息,则在public void //handleMessage(Message msg) {};里执行。 @Override public void run() { progress++; tv.setText(progress + ""); handler.postDelayed(this, 50); } }); } }
来张图解释下吧
主线程里有个handler,handler有个消息泵(loop),消息泵里有消息队列,消息泵会通过for循环去循环消息。如果发现消息,会发送到handler里面来!
子线程就是向消息泵里发送消息
下面的代码,我们把Runnable 对象改成全局的!
package com.example.hanglerjizhi; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.TextureView; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; //ARE异常,由ActivityManager,WindowManager管理! public class MainActivity extends Activity { private Button bt_run, bt_stop; private TextView tv; // 使用handler更新UI。 private Handler handler = new Handler() { }; //定义全局Runnable private Runnable runnable = new Runnable() { int progress = 0; // 注意run方法在主线程执行 @Override public void run() { progress++; tv.setText(progress + ""); //把当前的remove掉然后再执行,否则点击按钮数字变化会更快,这是因为点击一次按钮相当于 //post对象,点击十次就post了十次Runnable对象 // handler.removeCallbacks(this); handler.postDelayed(this, 100); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv_text); bt_run = (Button) findViewById(R.id.bt_run); bt_run.setOnClickListener(new OnClickListener() { // 点击事件被阻塞住,也会报ANR异常,按钮颜色按下去颜色不会改变, @Override public void onClick(View v) { start(); } }); bt_stop = (Button) findViewById(R.id.bt_stop); bt_stop.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stop(); } }); } public void stop() { handler.removeCallbacks(runnable); } public void start() { handler.post(runnable); } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories