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