android anr本质分析及解决办法

在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.email
Activity:com.android.email/.activity.MessageView
Subject:keyDispatchingTimedOut
CPU usage from 2550ms to -2814ms ago:
5%187/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20major
4.4% 1134/com.android.email: 0.7% user + 3.7% kernel /faults: 38 minor 19 major
4% 372/com.android.eventstream: 0.7%user + 3.3% kernel / faults: 6 minor
1.1% 272/com.android.phone:0.9% user + 0.1% kernel / faults: 33 minor
0.9%252/com.android.systemui: 0.9% user + 0% kernel
0%409/com.android.eventstream.telephonyplugin: 0% user + 0% kernel /faults: 2 minor
0.1% 632/com.android.devicemonitor: 0.1% user + 0%kernel
100%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;
		
	@Override
	public 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;
		
	@Override
	public void run() {
	     // TODO Auto-generated method stub
		System.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;
		
	@Override
	public void run() {
	     // TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getName() + " run");
		for(;;){
			synchronized(this){
			if(i>=1){
			   try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值