Android Handler 异步消息处理机制

引言

首先说明我是怎么接触到这个Handler对象的,当我尝试在Activity中处理网络传输的时候,报了异常。上网查了下资料,知道了再Android 4.0 版本以上是不能在主线程(UI线程 | Activity)中进行网络连接, 网络连接通常执行耗时操作,这样会造成主线程阻塞,主线程阻塞在手机上看就是卡死机了,这样会带来很不好的用户体验。好,既然不能在主线程执行,那我就创建一个线程呗,一运行问题又来了。经过查询得知,Android 系统也不允许在子线程执行UI操作,既然不能再子线程操作,那就回Activity操作吧。经过查询,获知Handler这个对象。 然后经过上网大量查询资料,下面列出我的一些理解。

概念

Handler 翻译过来就是一个处理器,负责消息处理,Android 系统中UI线程是不安全的,并且不能通过子线程更新UI,所以引入了Handler这个东西。那么Handler是怎么东西?Handler负责子线程和主线程之间发送消息。

Handler机制

  • 首先我要引入4个对象:

    • MessageQueue
    • Looper
    • Handler
    • Message

    MessageQueue 是一个消息队列,在Activity创建的时候会自动创建一个MessageQueue 对象和一个Looper对象。然后Looper又是什么鬼?Looper 我们暂且称为轮询器,Handler 是处理消息的处理器,Message就是传说中的消息。
    下面我来理一理他们之间的关系:

    • 首先Activtity创建,自动创建MessageQueue 和 Looper
    • 第二步是Looper 会一直循环检测MessageQueue 里面是否有消息,如果有消息,就通知给Handler处理
    • 第三步,Handler 收到通知,然候执行handleMessage(Message msg)方法(下面会讲到这个方法)
      这是系统内部自动执行的操作,我这里暂且不研究源代码怎么做的,大家有兴趣的话,网上有很多分析的,我这里就只是宏观上展示一下Handler处理的过程。
      下面通过一个图来看看他们的关系:

图可能画得有点丑,不过也能表达我的意思了,就是Looper一直在检查MessageQueue是否有消息,如果有,就把消息给Handler处理,而在我们开发中,实际要使用的仅仅是一个Handler 对象,Handler对象不是系统创建的,需要我们在Activity中创建,然后在子线程中调用, 在创建的时候需要复写一个HandleMessage(Message msg)方法,这个方法是收到消息的时候调用。

    static Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            //处理代码
        }
    };

子线程调用方法是sendMessage(Message msg) 其实有很多重载的方法,这里只列举一个。

    Message msg = Message.obtain();
    handler.sendMessage(msg);

通常我们需要携带数据,这时候就需要说到Message对象了,Message对象有一个obj 属性,是一个Object类型了,你可以赋值任何的数据类型。然后如果有多个消息发送个Handler,怎么区别呢? Message还有一个成员叫做 what ,你可以指定一个int的值给它,在handlerMessage中就可以通过switch(msg.what) 方法来分别处理不同的消息了。

  • 使用Handler需要注意的地方
    • 除了在Activity中会自动创建MessageQueue 和 Looper 之外,子线程是没有这两个对象的,所以在使用的时候要小心。
    • 调用了Looper的Looper.prepare()Looper.loop() 两个才会创建MessageQueue对象并且开始检测消息队列的。具体源码我就不分析了。

最后我给大家一个完整的Handler使用实例,该类是用于下载网络图片的。

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends Activity {
    static ImageView im;
    static Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch(msg.what){
            case 0:
                Bitmap bitmap = (Bitmap) msg.obj;
                im.setImageBitmap(bitmap);
                break;
            case 1:

            }

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        im = (ImageView) findViewById(R.id.imageView);

    }

    /** * Android 4.0 以上不允许在UI线程执行网络等耗时操作 并且只有主线程才能操作UI,所以要用到Handler * */
    public void click(View v) {
    final String path = "http://192.168.32.113/crm/ui/images/ban_1.gif";
        //判断缓存中是否存在图像
    final File file = new File(getCacheDir(),getFileName(path));
        if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
            Message msg = Message.obtain();
            msg = handler.obtainMessage();
            // 以上两种方式获取的message对象是从message池里面获取,可以节省内存
            msg.obj = bitmap;
            msg.what = 0;
            handler.sendMessage(msg);
            System.out.println("从缓存获取");
        }else{
            System.out.println("从网络中获取");
            Thread t = new Thread() {
                public void run() { 
                    try {
                    URL url = new URL(path);
                        // 强转为httpconnection
            HttpURLConnection conn = (HttpURLConnection) url
                                .openConnection();
                        // 设置请求方式
                        conn.setRequestMethod("GET");
                        // 设置连接超时
                        conn.setConnectTimeout(5000);
                        // 设置读取超时
                        conn.setReadTimeout(5000);
                        // 发送请求,与服务器建立连接
                        conn.connect();
                    System.out.println(conn.getResponseCode());
                        if (conn.getResponseCode() == 200) {
                            //获取输入流
                        InputStream is = conn.getInputStream();
                            //缓存图片
        File file = new File(getCacheDir(),getFileName(path));
            FileOutputStream fos = new FileOutputStream(file);
                            byte[] b = new byte[1024];
                            int len = 0 ;
                            while((len=is.read(b))>0){
                                fos.write(b, 0, len);
                            }
                            fos.close();                    
                            Message msg = Message.obtain();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                            // msg = handler.obtainMessage();
                            // 以上两种方式获取的message对象是从message池里面获取,可以节省内存
                            msg.obj = bitmap;
                            msg.what = 0;
                        }else{
                            Message msg = Message.obtain();
                            msg.what = 1;
                            handler.sendMessage(msg);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                };
            };
            t.start();
        }
    }
    public String getFileName(String path){
        return path.substring(path.lastIndexOf("/"));

    }
}

以上代码为了能在一行显示,有些缩进太难看,不要介意哈。
如果大家有兴趣剖析一下源代码的话,网上有很多资料。各种解析,各种牛逼哄哄的分析,我写这个适合初学者宏观上理解一下Handler是运行机制,并没有深入解析。

最后我在加一点,通常在项目中,Handler对象都是定义为静态的,这样有很大好处,方便Android其他组件和Activity线程通信,而且Google也是支持我们这样做的。

你可能感兴趣的:(android)