您的位置:首页 > 移动开发 > Android开发

王学岗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);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 异常 源码