2.3 Thread Communication -- Android Message Passing

来源:互联网 发布:医疗网络咨询培训 编辑:程序博客网 时间:2024/05/22 03:27

Android Message Passing

1. Message Passing机制介绍

之前介绍的线程间通信方式 - Pipe,Shared Memory,Blocking Queue,都会导致线程阻塞,因此影响Android UI线程的刷新,Android通过Message Passing的机制实现了一种无阻塞的生产者-消费者模式。
相关的类:
这里写图片描述

Message被生产者线程插入到MessageQueue中,消费者线程从MessageQueue中取出Message并处理。流程:
这里写图片描述
1. Insert:生产者线程通过使用连接消费者的Handler把message插入到队列中。
2. Retrieve(检索/取回/恢复):消费者线程中的Looper运行并从顺序的队形中取出message。
3. Dispatch(派遣/分派):那些Handler负责处理消费者线程中的message消息。一个线程中可以有多个Handler实例来处理message消息;这个Looper确保了message消息被分发到正确的Handler中。


这里写图片描述


Demo

/** * 本例使用Android的Message机制实现生产者-消费者问题。 * 本例中UI Thread为生产者,它通过子线程中的Handler向子线程的Message Queue中发送消息; * 子线程为消费者,它通过Looper取回并分发到子线程中的Handler中处理message。 */public class LooperActivity extends Activity {    private static final String TAG = LooperActivity.class.getSimpleName();    private LooperThread mLooperThread = null;    private Button mMessagePassingBtn = null;    private static class LooperThread extends Thread {        public Handler mHandler = null;        @Override        public void run() {            // 1. 初始化子线程中的Looper。注:Android的UI Thread中默认有Looper,因此不需要显式的初始化。            Looper.prepare();            // 2. 实例化Handler,用来处理发送过来的message            mHandler = new Handler() {                @Override                public void handleMessage(Message msg) {                    if (msg.what == 0) {                        // TODO 在子线程中做耗时的操作                        Log.i(TAG, "LooperThread # handleMessage # msg.what is 0");                    }                }            };            // 3. 把Message Queue中的message分发给消费者线程,即本例中的子线程            Looper.loop();        }    }    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_message);        mMessagePassingBtn = (Button) findViewById(R.id.message_passing_btn);        mLooperThread = new LooperThread();        mLooperThread.start();        mMessagePassingBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                if (mLooperThread.mHandler != null) {                    // UI Thread调用子线程中的Handler把message发送的MessageQueue中                    Message msg = mLooperThread.mHandler.obtainMessage(0);                    mLooperThread.mHandler.sendMessage(msg);                }            }        });    }    @Override    protected void onDestroy() {        super.onDestroy();        // 关闭子线程的Looper,停止分发MessageQueue中的Message,因此使得Thread的run()方法结束,子线程停止        mLooperThread.mHandler.getLooper().quit();    }}

2. Message Passing使用到的类

MessageQueue

这个message queue对应的类是android.os.MessageQueue,它是一个没有约束的,单方向的链接List。message queue中的message是根据时间戳来排序的,时间越小越靠前,当一个message的时间戳比当前时间小的时候才会被分发出去。
这里写图片描述
生产者线程可以随时插入一个新的message到message queue中,而新插入的message位置由message的时间戳确定的。
当message queue中的message为空了,消费者线程中的Looper无法取出message时,那么消费者线程就会一直阻塞,当又有新的message时,消费者线程就会再次执行。

MessageQueue.IdelHandler

如果没有message处理的话,消费者线程会有一些idle time(空闲时间)。默认情况下消费者线程在idle time期间会等待新的message,但是我们可以利用这段时间去执行其他的任务。
这里写图片描述
使用Idle Time
我们事先android.os.MessageQueue.IdleHandler接口,在queueIdle()方法中处理其他任务。
这里写图片描述

queueIdle()方法返回的是boolean类型,
true:保留这个idle handler,即不会移除这个IdleHandler。当在有Idle Time时还会继续调用这个IdleHandler。
false:移除这个idle handler,即只需要执行一遍,和MessageQueue.removedIdleHandler()方法的效果一样。

IdleHandler其他介绍的文章:http://bbs.51cto.com/thread-1094228-1.html

3. Message

Message分为两种:Data Message 和 Task Message。

Data Message

Data message 可以设置多个参数,然后发送到消费者线程中。
参数介绍:
这里写图片描述

Task Message

此Task是一个java.lang.Runnable对象,在消费者线程中执行。Task message 只能包含这个Task。
介绍:
这里写图片描述

处理Message

如果是Data message,消费者线程直接处理这个Data。
如果是Task message,会让Runnable在消费者线程中执行,但是消费者线程不会接收要在Handler.handleMessage(Message)方法中处理的message,因为这是Data message。

Message声明周期

这里写图片描述
这个传输的状态部分被应用控制,部分被平台控制。这些状态不是区分显著的。

Initialized

这里写图片描述

Pending

message已经被生产者线程插入到message queue中,在等待被分发到消费者线程。

Dispatched

在这个状态,Looper会把message从message queue中取出并分发到消费者线程中处理。
这个分发操作是由Looper控制的,不受application的影响。

Recycled

到这个状态时,message state会被清除,然后message实例被放入message pool中。
当message在消费者线程中执行完毕了,Looper会处理message的回收操作。
Recycling of messages is handled by the runtime and should not be done explicitly by the application.

4. Looper

当Looper被添加到Thread上面时,message queue也被添加到这个Thread上面了。Looper管理着message queue和分发message到消费者线程。
UI Thread默认有Looper,而在应用中其他的子线程需要显式的创建Looper。
一个线程只能有一个相关联的Looper,一个Message Queue。多个生成者线程把message都发送到这个Message Queue中,然后消费者线程按顺序的执行这些message。
需要耗时操作的message最好不应使用,因为它会导致message queue中其他重要的任务延迟执行。

Looper termination

终止Looper的相关方法:quit()和quitSafely()。
quit():终止Looper中所有的message,包括message queue中的pending message和已经passed the dispatch barrier(界线/屏障)。
quitSafely():只是终止那些没有passed the dispatch barrier的message。Pending message不受影响,可以正常分发并处理。
Looper和Thread的关系
终止Looper不会终止Thread,它仅仅是终止了Looper.loop()操作。但是Thread不可以再开启旧的Looper,也不可以再创建一个新的Looper,因此这个Thread不能再enqueue(入队)或者处理message了。如果你调用了Looper.prepare(),那么它将会抛出RuntimeException,这是因为这个Thread已经有了一个相关联的Looper了。如果你调用Looper.loop(),它将会阻塞,但是没有消息从这个message queue被分发出去。
UI Thread Looper
UI Thread 默认已经关联上一个Looper。
UI Thread Looper和其他Thread Looper的不同点(UI Thread Looper的特点):
这里写图片描述

5. Handler

如果没有Looper的话,Handler就不能正常工作,Handler不能和message queue结合插入message,也不能接收到message然后处理。Handler实例在创建的时候已经和Looper绑定了。
* Handler构造方法中没有绑定当前Thread中的Looper(可以调用Looper.prepare()方法绑定):

new Handler();new Handler(Handler.Callback);
  • Handler构造方法中显式的绑定Looper:
new Handler(Looper);new Handler(Looper, Handler.Callback);

一个Thread中可以有多个Handler。多个Handler中不能并行执行,message一直在同一个队列中并且是顺序处理的。
这里写图片描述

Message creation

这里写图片描述

Message insertion

这里写图片描述
Message插入到Message Queue时可能会出的错误:
这里写图片描述
Demo:使用两种Message(Task Message和Data Message)

public class HandlerActivity extends Activity {    private final static int HIDE_PROGRESS_BAR = 0;    private final static int SHOW_PROGRESS_BAR = 1;    private static UIHandler uiHandler;    private Button handlerTestBtn = null;    private TextView showText = null;    private ProgressBar progressBar = null;    private BackgroundThread myBackgroundThread = null;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_handler);        // 实例化UI Thread Handler        uiHandler = new UIHandler(HandlerActivity.this);        // 实例化子线程并开启        myBackgroundThread = new BackgroundThread();        myBackgroundThread.start();        handlerTestBtn = (Button) findViewById(R.id.handler_test_btn);        showText = (TextView) findViewById(R.id.show_text);        progressBar = (ProgressBar) findViewById(R.id.progress_bar);        handlerTestBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                myBackgroundThread.doWork();            }        });    }    @Override    protected void onDestroy() {        super.onDestroy();        myBackgroundThread.exit();    }    // 使用Activity的弱引用,防止Handler不释放Activity中的Component,导致内存泄漏    private static class UIHandler extends Handler {        private final WeakReference<HandlerActivity> activity;        public UIHandler(HandlerActivity handlerActivity) {            activity = new WeakReference<HandlerActivity>(handlerActivity);        }        @Override        public void handleMessage(Message msg) {            HandlerActivity myActivity = activity.get();            if (myActivity != null) {                switch (msg.what) {                    case HIDE_PROGRESS_BAR:                        myActivity.showText.setText("耗时(ms):" + msg.arg1);                        myActivity.progressBar.setVisibility(View.GONE);                        break;                    case SHOW_PROGRESS_BAR:                        myActivity.progressBar.setVisibility(View.VISIBLE);                        break;                }            }        }    }    // 子线程模拟处理耗时操作,使用子线程中的Handler.post()把Task Message发送到UI Thread中处理    private static class BackgroundThread extends Thread {        // 子线程的Handler        private Handler backgroundHandler = null;        @Override        public void run() {            Looper.prepare();            backgroundHandler = new Handler();            Looper.loop();        }        public void doWork() {            // 子线程的Handler发送一个Task Message            backgroundHandler.post(new Runnable() {                @Override                public void run() {                    // 使用UIHandler把Data Message发送到UI Thread,然后显示ProgressBar                    Message uiMsg = uiHandler.obtainMessage(SHOW_PROGRESS_BAR, 0, 0, null);                    uiHandler.sendMessage(uiMsg);                    // 模拟耗时操作                    Random random = new Random();                    int randomInt = random.nextInt(3000);                    SystemClock.sleep(randomInt);                    // 使用UIHandler把Data Message发送到UI Thread,然后显示耗时时间,隐藏ProgressBar                    uiMsg = uiHandler.obtainMessage(HIDE_PROGRESS_BAR, randomInt, 0, null);                    uiHandler.sendMessage(uiMsg);                }            });        }        public void exit() {            backgroundHandler.getLooper().quit();        }    }}

Message Processing(处理)

Looper分发的message会被消费者线程中的Handler处理。

两种message(Task Message和Data Message)处理方式:

  • Task message
    Task message中只包含一个Runnable,而没有数据。因此,需要处理执行的部分在Runnable的run()方法中,它会在消费者线程中自动执行,而不会调用Handler.handleMessage()方法。
  • Data message
    当message中带有数据时,这个Handler会接收这个数据并负责处理。消费者线程通过重写(override) Handler.handleMessage(Message msg)方法来处理数据。

两种方式处理Data message数据

第一种

创建Handler对象时,重写handleMessage()方法。
handleMessage()方法应该在message queue可访问之后(在Looper.prepare()方法调用之后),并且在获取message之前(在Looper.loop()方法调用之前)被定义。
Eg:

class ConsumerThread extends Thread {    Handler mHandler = null;    @Override    public void run() {        Looper.prepare();        mHandler = new Handler() {            public void handleMessage(Message msg) {                // TODO 处理Data message            }        };        Looper.loop();    }}

第二种

实现Handler.Callback接口,此接口中定义了一个返回boolean类型的handleMessage()方法。

public interface Callback {    public boolean handleMessage(Message msg);}

使用这种方式,当我们实例化Handler对象是,需要把Callback传进Handler的构造方法中。
Eg:

public class HandlerCallbackActivity extends Activity implements Handler.Callback {    private Handler uiHandler = null;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // 在Handler构造方法中传进Handler.Callback        uiHandler = new Handler(this);    }    // 重写Handler.Callback的方法    @Override    public boolean handleMessage(Message msg) {        // TODO 处理Data message        return true;    }}

Callback.handleMessage()方法返回boolean类型值的意思:
true:表示这个message被处理;
false:这个message被传递Handler.handleMessage()方法中处理。
注意:
Handler.Callback不会重写Handler.handleMessage()方法。Handler.Callback的方法是先于Handler.handleMessage()方法被调用的。Handler.Callback可以在Handler.handleMessage()获得message之前截获到并且可以修改message。
Demo

public class HandleMessageActivity extends Activity implements Handler.Callback {    private final String TAG = HandleMessageActivity.class.getSimpleName();    private Button handleMessageBtn = null;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_handlemessage);        handleMessageBtn = (Button) findViewById(R.id.handle_message_test_btn);        handleMessageBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                onHandlerCallback();            }        });    }    // 实现Handler.Callback中的方法    @Override    public boolean handleMessage(Message message) {        switch (message.what) {            case 1: // 截获并修改message内容,返回true,因此在这个方法中message被处理了,传不到Handler.handleMessage()方法                message.what = 11;                Log.i(TAG, "Callback截获并修改message.what(1 -> 11),Callback -> handleMessage: " + message.what);                return true;            case 2: // 截获并修改message内容,返回false,因此会传到Handler.handleMessage()方法中处理这个被修改的message                message.what = 22;                Log.i(TAG, "Callback截获并修改message.what(2 -> 22),Callback -> handleMessage: " + message.what);                return false;            default:                return false;        }    }    public void onHandlerCallback() {        // 实例化Handler对象时传进Callback,创建关联        Handler handler = new Handler(this) {            @Override            public void handleMessage(Message msg) {                // Handler.Callback中的方法handleMessage()会在Handler.handleMessage之前截获到message,并且可以修改这个message内容,                // 当Handler.Callback中的方法handleMessage()返回false的话,会传到这里处理;                // 当Handler.Callback中的方法handleMessage()返回true的话,那么这个message会被处理,而传不到这个方法。                Log.i(TAG, "Handler -> handleMessage: " + msg.what);            }        };        handler.sendEmptyMessage(1);        handler.sendEmptyMessage(2);    }}

6. Removing Messages from the Queue

Message identifiers(标识符)

这里写图片描述

从message queue中移除message的方法

这里写图片描述
Object标识符可以被用到Data message和Task message,因此可以通过这个方式把message分到一个Tag,这样能方便我们移除同一个Object Tag的message。
注意:一旦这个message已经被分发出去了,那么把这个message放入队列的生产者线程就不能停止这个message的处理,即已分发出去的message会被正常的处理掉

Object tag = new Object();Handler handler = new Handler() {    @Override    public void handleMessage(Message msg) {        // TODO 处理message    }};// Tag为Object标识符的Data messageMessage msg = handler.obtainMessage(0, tag);handler.sendMessage(msg);// Tag为Object标识符的Task messagehandler.postAtTime(new Runnable() {    @Override    public void run() {    }}, tag, SystemClock.uptimeMillis());// 移除同一个Object Tag的所有messagehandler.removeCallbacksAndMessages(tag);
原创粉丝点击