王学岗ANR异常以、handler机制及其源码详解

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

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值