Android消息机制

Android消息机制,主要指Handler的运行机制的运行需要底层的MessageQueue和Looper支撑。

Handler

Handler作为一个Android开发人员在熟悉不过了,我们通常用Handler对象来发送和处理消息,使用Handler可以将一个任务切换到某个指定的线程中去执行。

MessageQueue

中文翻译消息队列,它的内部存储了一组消息。MessageQueue以队列的形式对外提供插入和删除的工作,虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。

Looper

Looper中文翻译为循环,在这里可以理解为消息循环,由于MessageQueue只是一个消息的存储单元,它不能去处理消息,Looper就填补了这个功能,会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。

通过以上三者的定义我们可以理清大概的关系。

Handler负责发送消息Message到MessageQueue中,Looper不断循环从MessageQueue获取消息,最后再交由Handler来处理。这中间可以完成跨线程消息的传递。

Android为什么需要跨线程传递消息这个消息机制。

Android是事件驱动型的,也可以说成是消息驱动型的。

进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。

线程:线程对应用来说非常常见,比如每次newThread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片。

对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。

1.作为一个App需要一个循环保证App的长时间运行。

2.Android采用单线程模型,需要额外的线程来处处理其他事物,并和主线程来进行交互。

而Looper和MessageQueue就是这个循环中重要的类,Handler可以将一个任务切换到某个指定的线程中去执行。

Looper

首先我们来看下Looper类是如何构建这个循环的。
Looper的源码其实并不多 去掉注释也就100来行。

public final class Looper {
    //静态常量,全局唯一,负责保存Looper对象,很重要的一个对象。
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //主线程Looper对象
    private static Looper sMainLooper;  // guarded by Looper.class

    //消息队列
    final MessageQueue mQueue;
    //所在线程
    final Thread mThread;

    public static void prepare() {
        prepare(true);
    }

    //创建Looper对象的具体方法
    private static void prepare(boolean quitAllowed) {
        //Looper对象线程唯一,一个线程最多有一个Looper对象。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //创建主线程Looper对象的具体方法
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //获取主线程Looper对象
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    //获取当前线程Looper对象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    //获取当前线程对应的MessgerQueue
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    //Looper的构造方法
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    //获取当前线程方法
    public @NonNull Thread getThread() {
        return mThread;
    }

    //获取当前线程对应的MessgerQueue
    public @NonNull MessageQueue getQueue() {
        return mQueue;
    }
}

我们先分析Looper构造,创建MessageQueue,获取当前线程Thread。

//Looper的构造方法
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

再来看看具体的创建方法。也非常简单,直接使用构造创建Looper对象,保存在sThreadLocal变量中。

public static void prepare() {
        prepare(true);
    }

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));
}

ThreadLocal

sThreadLocal是一个ThreadLocal对象,ThreadLocal是一个非常重要的类,一般来说,当某些数据是以线程为作用域,并且不同线程具有不同的数据副本的时候就可以考虑采ThreadLocal。

很显然Looper就是以线程为作用域的。那么ThreadLocal是如何工作的呢?
我这里简单的分析一下。
ThreadLocal是多线程中很重要的一个类,内部有比较重要的两个方法和静态内部类。

static class Values {

}

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

set方法,首先会通过values方法来获取当前线程中的ThreadLocal数据,在Thread类的内部有一个成员专门用于存储线程的ThreadLocal的数据:ThreadLocal.Values localValues,因此获取当前线程的数据就变得异常简单了。如果localValues的值为null,那么就对其进行初始化,初始化后再将ThreadLocal的值进行存储。localValue内部有一个数组table。reference字段所标识的对象的下一个位置。

get方法,同样是取出当前线程的localValues对象。如果这个对象为null,那么就返回初始值,如果不为null,那么就取出它的table数组,并查找出ThreadLocal的reference对象在数组中的位置,然后数组中的下一个位置,所存储的数据就是ThreadLocal的值。不同的线程访问同一个ThreadLocal的get方法,ThreadLocal会从各自的线程中取出一个数组,然后再从数组中根据当前的索引去查找对应的value值,然而不同的线程对象中数组是不同的。

通过前面Looper构造和创建方法的分析我们知道Looper对象持有MessageQueue和Thread的应用,Looper对象存储在static final的ThreadLocal对象 sThreadLocal中,在那个Thread中使用sThreadLocal的set方法保存了Looper对象,那么我们就可以在那个Thread中通过sThreadLocal的get方法获取Looper对象。找到了Looper对象也就找到MessageQueue也就能从中获取消息。

Looper.loop(),Looper类执行循环的具体方法。

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();
    }
}

内部使用了一个无限的for循环。不断的queue.next()获取消息,然后使用 msg.target.dispatchMessage(msg);来处理消息。queue就是MessagerQueue,而msg.target其实就是Handler。

MessagerQueue

我们先暂时搁置Looper来看一看MessageQueue。我们知道MessageQueue对象时在Looper中创建的,而MessageQueue的内部会创建一个C++的NativeMessageQueue,NativeMessageQueue又回创建一个C++的Looper,C++的Looper又会创建一个管道,其实真正的处理都是在C++中完成的。(MMP)

消息可以跨线程的原因———管道

首先当一个线程没有新的消息需要处理时就会睡眠在这个管道的读端文件描述符上,直到有新的消息需要处理为止,其次,当其他线程向这个线程的消息队列发送一个消息之后,其他线程就会通过这个管道的写端文件描述符往这个管道写入一个数据从而将这个线程唤醒,以便他可以对刚才发送到他的消息队列中的消息进行处理。

当其他线程向当前线程的消息队列发送了一个消息之后,他们就会向与当前线程所关联的一个管道写入一个新的数据,目的是将当前线程唤醒,以便它可以及时的去处理刚刚发送到他的消息队列的消息。

Handler

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

    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();
        //如果当前线程没有创建Looper对象就会抛出异常
        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;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在Handler的构造方法中我们不难发现Looper的身影,Handler和Looper是息息相关的,这也是为什么我们在子线程创建Handler对象需要同时构建Looper对象的原因之一。

Hnadler发送消息
     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);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
        //msg.target 就是Handler
        msg.target = this; 
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler发送消息其实就是向MessageQueue中插入一条消息,然后调用enqueueMessage方法来,而enqueueMessage是最终会唤醒C++层的Looper所在的线程。最终进入到java层Looper.loop()循环中msg.target.dispatchMessage(msg);让Handler来处理消息。
子线程如果没有创建Looper对象,在子线程中使用Handler处理消息就会报异常。
throw new RuntimeException(“Can’t create handler inside thread that has not called Looper.prepare()”);

线程如何构建消息循环

public void create(){
   new Thread(new Runnable() {
       @Override
       public void run() {
           //创建Looper对象,并保存到ThreadLocal中
           Looper.prepare();
           //从ThreadLocal中取出当前线程的Looper对象
           Looper looper = Looper.myLooper();
           //使用Looper对象创建Handler,使Hander关联当前线程的Looper
           Handler handler = new Handler(looper);
           //开启Looper循环
           Looper.loop();
       }
   }).start();
}

这样我们就能在子线程中构建了消息循环,并且可以在子线程中使用handler 处理消息。

为什么我们再主线程中使用Handler,没用这么麻烦,那是因为主线程在一开始就创建了Looper对象。

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        AndroidKeyStoreProvider.install();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
        //创建主线程Looper对象
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //开启消息循环
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值