Android中Java层消息机制的简单分析

一、引言:
Android中的消息机制主要是用于线程间通信,常见的应用场景有apk中,UI只能在主线程中更新,在子线程中是不能的,这个时候,就需要使用消息机制,让子线程通知主线程更新UI。Android中的消息机制不仅在java层大量使用,native更是频繁,比如媒体的stagefright框架,Android中消息机制的运转由四个部分联合实现,分别是Message、Handler、Looper和MessageQueue。

二、写个demo感受一下:
理清4者关系之前,我们先写一个简单的demo来看看,消息机制是怎么玩的:我们在Main Activity中创建两个控件,一个button,一个TextView,点击button,实现TextView中内容的改变。

  1. xml配置:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/change_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Change Text" />
    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello world"
        android:textSize="20sp" />

</RelativeLayout>
  1. Main Activity代码:
package com.example.androidthreadtest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    public static final int UPDATE_TEXT = 1;
    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = (TextView) findViewById(R.id.text);
        Button changeText = (Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }
    /* 使用匿名内部类简化代码 */
    private Handler handler = new Handler() {
        /* 覆写handleMessage方法 */
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    // 在这里可以进行UI操作
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message); // 将Message对象发送出去
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

在主活动中,我们监听按键事件,如果按键响应,我们创建一个子线程,在子线程中发送一个message,然后,主线程中收到消息之后,更新TextView中的内容;
3. 测试结果:
未按键前,TextView显示hello world:
在这里插入图片描述
按键改变显示内容:
在这里插入图片描述
三、代码分析:

  1. 主线程:
    /* 使用匿名内部类简化代码 */
    private Handler handler = new Handler() {
        /* 覆写handleMessage方法 */
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    // 在这里可以进行UI操作
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

这里,我们直接使用匿名内部类来创建消息处理者handler,并且覆写了其中的handleMessage来处理具体的消息,如果接收到的消息内容为UPDATE_TEXT的话,那么我们就改变TextView中的内容。那么什么是handler?handler即消息处理者,其作用是发送消息和处理消息,通常使用sendMessage()方法,也可以使用延迟发送消息的方法,发送出去的消息经过looper之后,会分发到handle的handleMessage()方法进行处理;其实,也就是自己处理自己发送的消息,只不过,在子线程中发送消息,主线程中处理消息;
原生代码中的sendMessage()方法:

frameworks\base\core\java\android\os\Handler.java

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

什么也没做,也就是要我们覆写实现自己的处理逻辑。

  1. 子线程:
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message); // 将Message对象发送出去
                    }
                }).start();
                break;
            default:
                break;
        }
    }

按键响应中,创建一个子线程,子线程干的事是首先实例化一个message,然后将要发送的消息内容填充,最后通过调用主线程中handler的sendMessage方法将消息发送出去。
这里简单说下message:Message是在线程之间传递的消息, 它可以在内部携带少量的信息, 用于在不同线程之间交换数据。 demo中我们使用到了Message的what 字段, 除此之外还可以使用arg1 和arg2 字段来携带一些整型数据, 使用obj 字段携带一个Object 对象。

  1. looper该出场了:
    目前我们简单说了message和handle,接下来我们需要引出messagequeue的概念了,这是消息队列,用于存放所有使用handler发送的消息,相当于在代码中扮演了“邮箱”的角色既然存放消息的“邮箱”有了,那么自然还需要一个角色来扮演“邮局”,没错,那就是looper,looper用于分发信息;但是,为什么我们的代码中全程都没有看到looper的存在呢?那是因为我们的主活动是运行在apk的主线程中,主线程已经帮我们创建好了looper,并开启了loop循环,所以,在代码中,looper是蒙着面纱的,简单看下“蒙面者”的部分代码:

frameworks\base\core\java\android\os\Looper.java

    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));
    }
    
    ...
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

这是初始化looper,从构造函数可以看出来,这是一个单例设计模式,所以,每个线程只能有一个looper和MessageQueue。
再来看下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;
		...
        for (;;) {
			/* 如果没有获取消息,则退出loop方法 */
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }			
			...
			
			/* 调用message类中的dispatchMessage方法去处理消息 */
            msg.target.dispatchMessage(msg);

			...
        }
    }

首先是获取当前线程的looper对象,然后进入for循环开始不停地处理消息队列中的消息,如果没有获取到消息,那么就将退出消息队列,如果获取到了消息,那么交由message中的dispatchMessage方法处理,看一下dispatchMessage方法:

    public void dispatchMessage(Message msg) {
    	/* 1.如果message中有callback 函数,则使用回调 */
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        /* 2.如果当前handler有回调,那么使用回调去处理消息 */
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            /* 由handler来处理 */
            handleMessage(msg);
        }
    }

这个方法可以看到,消息处理优先看是否有回调,没有的话才会调用handler的handleMessage来处理。

四、贴图总结:
在这里插入图片描述
demo参考:

郭霖老师:《第一行代码:Android(第2版)》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值