[Android源码]l理解handler机制

    xiaoxiao2021-12-14  21

    Android中的异步消息处理机制,就是对核心类handler、looper类和Message类的应用。如果我们想要把一些耗时的操作(比如网络请求),放在worker线程里面去做,就需要对这个handler机制有一个深入的了解。本文将从源码的角度来分析handler内部是如何把消息发送出去,并且在完成时通知UI线程去进行相应的操作的。

    源码分析

    Thread,Looper,MessageQueen,Message和handler的逻辑关系:

    说明:

    任何一个Thread最多只能拥有一个Looper普通的Thread(除去UI线程,因为UI线程在程序启动时候自己已经调用了相应的.prepare())要想要变成LooperThread必须要经过Looper.prepare()和Loop.loop();Handler负责向所属的线程里面的Looper里面添加Message(消息入队)或者处理消息(消息出队)Handler的归属问题,一个Handler属于创建他的那个线程。一个线程可以创建多个handler

    从Looper说起

    私有的构造函数

    除了这是一个final的类以外,私有的构造函数,也禁止了从外部来 new 一个Looper(),是这样的:

    private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

    但是查看了Looper的整个源码,我们会发现,只有这里:

    private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }

    是new了一个Looper,所以就解释了为什么一定要先调用Looper.prepare()才能变成有个LooperThread

    成员变量

    // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; ThreadLocal:通过它我们可以得到与当前Thread相绑定的Looper是哪个,并且还可以通过它来绑定一个Looper到当前的Thread sThreadLocal.set(new Looper(quitAllowed)); return sThreadLocal.get();sMainLooper:与UI线程绑定的LoopermQueue:如逻辑关系图中所示,指Looper里面的那个MessageQueuemThread:当前线程,通过getThread()可以返回他的值

    核心方法loop()

    public static void loop() { final Looper me = myLooper(); //返回当前线程里面的Looper final MessageQueue queue = me.mQueue; //拿到Looper里面的MessageQueue Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); //没看懂但是不影响理解消息处理机制 for (;;) { //判断条件为空,所有是一个无限循环 Message msg = queue.next(); // might block //Message里面的next()方法,将下一个消息出队, //并进行处理,当队列没有消息时候就阻塞在这里 msg.target.dispatchMessage(msg); //对消息进行处理,非常重要!! //msg.target返回的是Handler对象,message.target的 //初始化是在Handler中enqueueMessage()入队操作时候 msg.recycle(); //释放处理掉的消息所占用的资源 } }

    Loop中的一个for循环将MessageQueue驱动着动起来,如果队列里面有消息就拿出来进行处理,如果没有就一直阻塞并检查着。

    核心类Handler

    构造函数

    构造函数一共有7个,调用关系如下: 通过构造函数的源码我们可以知道,在构造handler时,我们可以传入一个callback参数,也可以选择不传入这个参数

    如果传入callback的时候,因为存在 mCallback = callback; 而mCallback是一个本地的接口,所以我们就必须去实现这个接口里面的handleMessage,同时我们也要去重写空的成员函数handleMessage来接受messages public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } 如果不传入callback这个参数,那么也需要我们去重写空的成员函数handleMessage: /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }

    通过暴露这两个handlerMessage来实现对消息的处理操作。

    通过handler向绑定的线程里面的Looper里面的MessageQueue发送消息

    发送消息有两种途径post系列和sendMessage系列,来张图说明下调用关系: 我们可以看到最终都是调用了enqueueMessage方法:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //重要代码,为Message设置target,方便在looper里面 //拿到这个Message时候,找到对应的target(handler对象) //从而调用里面的处理方法(dispatchMessage方法) if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); //把这个消息加入到当前handler所属的线程里面的looper中的 //messageQueue里面去 }

    说明:两种方式Post和sendMesssage的不同点是,采用post时,传入的是一个runnable对象,再转化成message对象;采用sendMessage时,传入的直接就是Message对象。

    对Message的处理(消费)dispatchMessage函数

    当我们从Looper中loop方法的for(;;)里面拿到一个消息时候会回调Message.target.dispatchMessage方法,看下源码:

    /** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }

    需要认真分析一下

    首先检查Message自身的callback(实质是一个Runnable对象)是否为空,我们会看到只有调用post—->getPostMessage—->m.callback = runnable时候进行了初始化,所以可以通过post传入Runnable来实现对消息的消费目的。 private static void handleCallback(Message message) { message.callback.run(); }

    通过执行这个callback(Runnable)来达到消费Message的目的

    如果Message自身的callback为空,继续检查handler的成员变量mCallback(本身是一个接口类型): final Callback mCallback; public interface Callback { public boolean handleMessage(Message msg); }

    并且只有当handleMessage返回false的时候,才会继续向下执行handler的成员函数handleMessage

    handleMessage(msg)是handler的一个空的成员函数,需要重写来实现对消息的消费。 小总结:我们可以在三个地方对消息进行消费,如果我分别在Message里面写了callback,实现了handler的接口Callback,同时又重写了成员函数handlerMessage,那么他们的优先级是 Message.callback>Callback>handlerMessage

    Message类

    一个Message就是一个要处理的事件,常用到的有以下几点: * 这是一个final类,所以可以通过Message.obtain来获得一个空的消息 * 可以携带数据 有两个 int arg1 arg2 可以携带少量的数据,并且效率也比较高,还有一个成员变量object可以携带任何数据 * Message.what 用来标志Message的标识符,可以自己赋值,来区别Message * Handler target 可以得到或者设置Message所绑定的handler * Runnable callback 可以接收一个Runnable来对消息进行消费


    到此,对handler分析就结束了,再提示一下handler的常用场景,就像例子里面一样,在UI线程创建handler,完成绑定,并通过最低优先级的方法—重写了空的成员函数handleMessage来消费这个事件,然后再新开一个Thread,在run方法里面通过已经创建的handler.post 或者handler.sendmessage 加入消息队列就可以了。 (其实我们也可以做到在thread1里面做网络请求,传到thread2,再传到thread3…..最后传到UI线程,只不过android都是依托UI线程来做的,这样实际效果更好一些。)

    我们放一个例子:—->直观的使用handler

    (如果不想看例子的直接可以跳过了跳过) * 想要达到的效果: * 主要代码: * MainActivity.java

    public class MainActivity extends Activity { Button bt; static ImageView iv; static TextView tv; final String address= "http://www.yydm.com/yfile/allimg/140708/1PI3L60-0.jpg"; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt = (Button) findViewById(R.id.btbt); tv= (TextView) findViewById(R.id.tv); iv= (ImageView) findViewById(R.id.iv); final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ //7.把位图对象显示至imageview case 1: tv.setText("一只大龙猫!"); iv.setImageBitmap((Bitmap) msg.obj); break; case 2: Toast.makeText(MainActivity.this,"请求失败!",Toast.LENGTH_SHORT).show(); break; } }}; bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Thread(){ @Override public void run() { try { //1.下载图片确定网址,将网址封装成url对象 URL url = new URL(address); //2.获取客户端与服务器连接对象,此时还没有建立连接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //3.对连接对象进行初始化 conn.setRequestMethod("GET");//连接方式 conn.setConnectTimeout(3000);//连接超时 conn.setReadTimeout(3000);//读取超时 //4.发送请求,与服务器建立连接 conn.connect(); //响应码=200,说明请求成功 if (conn.getResponseCode()==200){ //5.获取服务器响应头里的流,流中的数据就是请求端的数据 InputStream is = conn.getInputStream(); Log.d("InputStream", String.valueOf((is.toString()==null))); //6.读取数据流里的数据,构造成位图对象 Bitmap bitmap = BitmapFactory.decodeStream(is); Log.d("bitmap", String.valueOf(bitmap==null)); Message msg=handler.obtainMessage(); //消息对象可以携带数据 Log.d("Message", String.valueOf(msg==null)); msg.obj=bitmap; Log.d("msg.obj", String.valueOf(msg.obj==null)); msg.what=1; //把消息发送至主线程的消息队列 handler.sendMessage(msg); }else { Message msg = handler.obtainMessage(); msg.what=2; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } }}.start(); } }); } } xml文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/btbt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="click" android:layout_gravity="center" android:gravity="center"></Button> <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Hello World, MyActivity" /> <ImageView android:id="@+id/iv" android:layout_width="fill_parent" android:layout_height="fill_parent"></ImageView>> </LinearLayout>

    当然了,网络请求,得想着在manifest里面获取到权限:

    <uses-permission android:name="android.permission.INTERNET"></uses-permission> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <uses-permission android:name="android.permission.INSTALL_PACKAGES"></uses-permission>

    这样就可以让demo跑起来了

    注:demo参考

    参考: [0]http://blog.csdn.net/iispring/article/details/47180325#t0 [1]http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html [2]http://www.jianshu.com/p/36a978b6cacc [3]http://www.jianshu.com/p/86ea1c817fff [4]http://www.cnblogs.com/JczmDeveloper/p/4403129.html [5]http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html

    转载请注明原文地址: https://ju.6miu.com/read-968581.html

    最新回复(0)