Android消息处理机制Message,Looper,Handler

本文详细介绍了Android的消息处理机制,包括Handler、Looper和Message的使用。通过示例展示了如何在子线程中完成任务并利用Handler在主线程更新UI。同时,文章还对消息处理的源码流程进行了分析,帮助读者深入理解这一机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android上面通过Handler,Looper和Message实现了消息处理机制,极大的方便我们实现线程通信,在后台线程完成耗时动作并准备好数据通知主线程更新UI。下面我们通过简单实例和附带源码去探索Android消息机制的技术细节。

一,示例

下面简单实现在子线程中完成工作(+1s)并在工作完成之后通知主线程更新进度条。

1)要继承Handler实现自己的消息处理类MyHandler;

2)将Handler绑定到MainLooper,可以理解做该Handler的消息将会发送到主线程的消息队列并由主线程的取出消息数据并调用MyHandler的handleMessage方法处理消息,因为是主线程在调用所以当然handleMessage就运行在主线程中啦;

3)将MyHandler的引用交给子线程。这样子线程就可以将准备好的数据包在Message中通过该handler发送自己的数据了。这里发送的是当前进度条的进度progress;

protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);
        progressBar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                progressBar.setProgress(progressBar.getProgress() + 10);runnable.run();
            }
        });
        progressBar.setProgress(0);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0;i<100;i++){
                    Message message = handler.obtainMessage();
                    message.arg1 = i;
                    handler.sendMessage(message);
                    try {
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    
    class MyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {

            int process = msg.arg1;
            Log.d("MyHandler", "current process is" + process);
            progressBar.setProgress(process);
            super.handleMessage(msg);
        }
        public MyHandler(Looper looper){
            super(looper);
        }

    }


二,源码流程分析


一,Looper处理消息活动图




如上图展示Looper处理一条消息的流程。
1)在消息队列为空时Looper会block在取消息阶段。在有新消息到达的时候Looper会被唤醒从消息队列中取出并处理消息。
2)对于Message.callback不为空的消息直接运行callback,否则尝试用Handler自带的mCallback去处理消息。如果处理成功(返回trrue)则直接返回不调用我们自己实现的消息处理方法处理消息。如果handler.mCallback未成功处理消息则调用我们自己实现的handler的handleMessage方法处理消息。
自此一个消息便被成功处理。

二,消息的载体Message


//Message主要成员列表
public int what;//消息码
public int arg1;
public int arg2;//两个简单数据域用于存放整形数字,节约内存
public Object obj;//一个对象数据域
Bundle data;//Bundle数据集
Runnable callback;//消息处理时的回调方法
Handler target;//指定消息处理器
如上代码所示,Message主要是数据成员,包括简单的int型数据和复杂的数据对象,另外Message有一个target成员将Message与Handler连接起来

三,消息处理器Handler

1)数据成员
final MessageQueue mQueue;//消息队列
final Looper mLooper;//消息循环器
final Callback mCallback;//消息处理器,分发的消息会先由callback处理,如果callback可以处理则不再分发直接返回否则有hanleMessage处理
final boolean mAsynchronous;//同步标志
IMessenger mMessenger;
如上图所示Handler通过mLooper和mQuene将Handler与Looper连接起来。通过Looper的mQuene成员可以删除已该Handler为target的消息队列中的Message.因为Message有实现消息池,所以通过Handler.obtainMessage方法获取新的消息对象可以复用消息实例,减少系统GC提高性能。

2)Handler构造方法
构造方法主要分两种,一种是有参数Looper另一种无参数Looper
无参数Looper的构造方法通过尝试获取当前线程Looper来初始化Handler的Looper。若当前线程没有Loopter则会抛出异常
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
如上代码所示,对于没有输入Looper参数的构造方法,将会尝试通过Looper.myLooper获取当前线程的looper以此来确定Handler的消息循环器。


3)发送消息sendMessage(Message)
消息发送接口通过一路调用,直到调到enqueueMessage方法将该Message入队,同时也由此确定该Message 会由哪个Looper循环到。在本例中mQueue是Looper的成员,Looper通过loop方法遍历mQueue取出消息并调用消息target的handleMessage方法处理消息。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
然后在enqueue方法中设定Message的target到自身,这会决定最终这个Message将会被哪个handler处理。
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
         msg.target = this;
         if (mAsynchronous) {
             msg.setAsynchronous(true);
         }
         return queue.enqueueMessage(msg, uptimeMillis);
     }

4)dispatchMessage分发消息
public void dispatchMessage(Message msg) {
       if (msg.callback != null) {
            handleCallback(msg);
         } else {
             if (mCallback != null) {
                 if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
如上代码所示,Handler通过dispatchMessage分发消息,如果Message的callback不为null直接处理Message callback否则通过Handler的mCallback和handleMessage处理消息。handleMessage则是需要自己实现的消息处理方法。

四,消息循环器Looper

Looper负责从消息队列中取出消息并调用对应的Handler进行处理。除了主线程会默认开通Looper以外其它线程不会启动Looper,如果需要的话需要调用Looper.prepare方法然后调用Looper.loop开始消息循环。

1)开始循环Looper.loop
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
如上代码所示
msg.target.dispatchMessage(msg);
Looper从消息队列中取出消息体并调用Message的target(发送消息的Handler)的dispatchMessage方法处理消息



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值