android anr本质分析及解决办法

来源:互联网 发布:沈阳seo新浪博客 编辑:程序博客网 时间:2024/06/06 07:33

在android开发中经常碰到的一类问题是anr,全称是application not respond,应用程序没有响应,关于这类问题,究其本质是主线程无法响应。而导致主线程无法响应的原因大致如下:

1、主线程请求网络资源,数据库访问或者io访问,这些操作都是耗时操作,主线程处于阻塞状态,如果超时等待,会发生anr

2、cpu处于饥饿状态,无法让主线程运行,导致anr

3、其他进程或者线程占用cpu资源,无法释放资源让该主线程运行,导致anr

4、死锁,即主线程等待的锁正在被其它线程占用,无法释放。

anr问题一般出现在app代码中,systemserver进程中的inputDispatcher线程会一直监听app的响应时间,如果键盘或者触摸事件超时等待5s没有响应,broadcastreceiver超时10s没有响应,或者service超时响应都会发生anr,ActivityManagerService会将anr的直接原因在aplog中打印出来,另外通知kernel往对应进程发送signal 3,将该进程的各个线程的函数堆栈信息打印出来,输出到data/anr/traces.txt中。所以分析anr问题一般主要看的就是aplog和traces.txt。下面主要分析aplog和traces.txt中的log。

Process:com.android.emailActivity:com.android.email/.activity.MessageViewSubject:keyDispatchingTimedOutCPU usage from 2550ms to -2814ms ago:5%187/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20major4.4% 1134/com.android.email: 0.7% user + 3.7% kernel /faults: 38 minor 19 major4% 372/com.android.eventstream: 0.7%user + 3.3% kernel / faults: 6 minor1.1% 272/com.android.phone:0.9% user + 0.1% kernel / faults: 33 minor0.9%252/com.android.systemui: 0.9% user + 0% kernel0%409/com.android.eventstream.telephonyplugin: 0% user + 0% kernel /faults: 2 minor0.1% 632/com.android.devicemonitor: 0.1% user + 0%kernel100%TOTAL: 6.9% user + 8.2% kernel +84%iowait

这是anr发生后aplog的主要内容,其中会显示cpu平均负载和使用率,实现在代码在activitymanagerservice中。

下面列出几个参考的网站:

 anr例子分析:http://blog.csdn.net/fengye810130/article/details/9223063

anr原理分析:http://rayleeya.iteye.com/blog/1955657

死锁分析:http://www.jb51.net/article/38561.htm

http://www.360doc.com/content/13/0222/10/3700464_267198940.shtml

当然,只有理解了多线程的意义才会对anr有较深的理解,下面讲解多线程编程。

先看如下代码,在主线程中起了两个子线程,子线程打印1到50,这里没有用到Runnable,所以这两个线程内部的变量是独立的,互不影响。这样写代码不需要用到同步。

public class helloThread extends Thread {private String str;public int i = 0;public helloThread(String str){this.str = str;}public void run(){             for(int j=0;j<50;j++)System.out.println(str + "     :" + (++i));}         public static void main(String[] args){new helloThread("线程A").start();new helloThread("线程B").start();}}

打印结果如下:

线程A     :1
线程B     :1
线程A     :2
线程B     :2
线程B     :3
线程A     :3
线程B     :4
线程A     :4
线程B     :5
线程A     :5

但如果代码是这样的:

class myRunnable implements Runnable{    public int i = 0;@Overridepublic void run() {     // TODO Auto-generated method stub              for(int j=0;j<5;j++)System.out.println(Thread.currentThread().getName() +"   :" + (++i));}}public class helloThread {public static void main(String[] args){myRunnable tx = new myRunnable();Thread A = new Thread(tx, "线程A");Thread B = new Thread(tx, "线程B");                  A.start();                  B.start();}}


这段代码的执行结果如下:

线程A   :1
线程A   :2
线程A   :3
线程A   :4
线程A   :5
线程B   :6
线程B   :7
线程B   :8
线程B   :9
线程B   :10

可见主线程创建的两个子线程共用同一个Runnbale实例,这里才会用到同步的概念。

还有一点必须注意,这里启动线程是用的start函数,只有它才会启动新的线程,并创建线程栈,而run只是简单的函数调用,它执行在主线程中。这点很关键。

当涉及到线程同步时,如果不是合理的使用锁还会出现死锁,也是android anr发生的原因之一。具体可参考文章如下http://www.cnblogs.com/riskyer/p/3263032.html

class myRunnable implements Runnable{    public int i = 10;@Overridepublic void run() {     // TODO Auto-generated method stubSystem.out.println(Thread.currentThread().getName() + " run");for(;;){if(i<=0) break;i = i - 1;try{System.out.println(Thread.currentThread().getName() + " begin sleep.." + i);Thread.sleep(1);System.out.println(Thread.currentThread().getName() + " end sleep.." + i);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "  :" + i);}}}public class helloThread {public static void main(String[] args){myRunnable tx = new myRunnable();Thread A = new Thread(tx, "线程A");Thread B = new Thread(tx, "线程B");        A.start();        B.start();}}

加锁后代码如下:
 

class myRunnable implements Runnable{    public int i = 10;@Overridepublic void run() {     // TODO Auto-generated method stubSystem.out.println(Thread.currentThread().getName() + " run");for(;;){synchronized(this){if(i>=1){   try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}   System.out.println(Thread.currentThread().getName() + " are selling ticket  :" + i);   i--;}}}}}public class helloThread {public static void main(String[] args){myRunnable tx = new myRunnable();Thread A = new Thread(tx, "线程A");Thread B = new Thread(tx, "线程B");        A.start();        B.start();}}

说说我的多线程同步的理解:还是以卖火车票为例,售票员A和B分别在两个售票窗口卖票,A和B就相当于代码中的两个线程,售票系统就相当于主线程,余票总额相当于代码中的变量i,当A和B吃饱饭准备工作,相当于线程调用了start()函数起来了。以临界值分析,当A在卖最后一张票的时候,进入循环,但是还没有i--的操作,此时A突然困了,心情不好了,不干了,此时B也在卖票,他不知道A也在卖最后一张票,售票系统的默认机制是处理B的售票请求,此时B卖出了最后一张票,i--,i=0,这个时候A突然想明白了,接着刚才的工作继续卖票,此时他就卖出了第0张票。要想避免这种情况发生,就是在A不干的时候,让售票系统将i锁起来,其他售票员无法操作i,这样其他人就不能卖票了,只有当A卖完了票,其他人才可以操作。这就是锁的概念。线程同步就是让线程对资源的使用有顺序性,A使用完资源后B才能继续使用。

另外关注几个函数:

Thread.yield():让线程从运行状态变为可运行状态。

Thread.join():比如Thread a = new Thread(); a.start(); a.join() 表示线程a执行完后才能执行其它线程。

Thread.sleep():线程睡眠,保持对象锁。

Thread.wait():线程睡眠,释放对象锁。



0 0