Handler消息机制全解(一)消息机制的创建

    进程是应用程序的一个运行活动过程,是操作系统资源管理的实体,也就是操作系统分配资源的最小单元,比如系统内存、代码、数据、CPU时间片等资源,为正在运行的应用程序提供运行环境。 每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,并且系统给每个进程分配的内存会有限制。
    线程是进程内部执行代码的实体,它是CPU调度资源的最小单元,一个进程至少包括一个主线程(进程被操作系统创建,时一个主线程也立刻运行,它无需由用户去主动创建)。线程没有自己独立的内存资源,它只有自己的执行堆栈和局部变量,所以线程不能独立地执行,它必须依附在一个进程上。也就是说进程只是提供一个环境和资源,或者说进程像容器一样提供了一个空间,里面包含了很多资源,线程等,真正执行代码的地方是在线程里。多个线程共享一块内存空间和一组系统资源,所以系统在各个线程之间切换时,资源占用要比进程小得多。
    一个进程运行起来也就是主线程运行起来,一旦主线程运行终止了,进程也就随之终止,因此几乎任何操作系统和编程语言中,一旦程序运行起来就会进入一个死循环中,并在循环中处理各种事件,直到进程结束。那么在Android中也不例外,Android应用程序就是通过消息来驱动的,在主线程的死循环中,一旦有消息来就会取出消息执行对应的事件,如果没有消息,此时主线将会进入睡眠状态,因此消息循环并不会对CPU性能有过多的消耗。这种消息循环机制主要涉及到三个类,Handler,Looper,MessageQueue,下面将全面分析这几个类之间的协作关系。


消息机制的创建

    四大组件的管理服务ActivityManagerService在启动一个应用程序组件时,如果发现这个组件所需要的应用程序进程还没有启动起来,那么它就会请求Zygote进程将这个应用程序进程启动起来,Zygote进程是通过复制自身的方式来创建一个新的应用程序进程的。
    一个新的应用程序进程在创建后会初始化一些资源,比如获取一个虚拟机实例,一个Binder线程池等,接着会调用应用程序进程的入口函数,它就是ActivityThread类的静态成员函数main,在这个main方法中会为主线程创建一个消息循环。这样,运行在它里面的应用程序组件就可以方便地使用Android系统的消息处理机制,以及Binder进程间通信机制来实现自己的业务逻辑。
public final class ActivityThread {
    ......
    public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
       
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ......
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    ......
}
  第5行代码首先调用Looper类的静态方法prepareMainLooper来创建一个消息队列,然后在第7行创建了该类的实例,并调用该类的实例方法attach来初始化一些资源,以及在 attach方法保存该类实例为一个静态变量,以便后面程序中使用,最后在14行调用Looper类的静态方法loop来开启消息循环。 至此,新进程的消息循环就创建完成,那么进程的启动过程也就完成了。
     接下来我们来分析一下,Android线程消息队列的创建过程,也就是Looper类的prepareMainLooper()和prepare()方法的实现,下面列出的是Looper类的主要实现。
public final class Looper {
    ......
    // 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;
    ......
   
    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));
    }

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    public static void loop() {
        ......//下面分析
    }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

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

    public void quit() {
        mQueue.quit(false);
    }

    public void quitSafely() {
        mQueue.quit(true);
    }

    ......
}
  第8行代码prepareMainLooper()方法首先会调用prepare()方法来创建一个 Looper对象 ,并将Looper对象保存在sThreadLocal变量中, sThreadLocal代表当前线程 。在 Looper的构造方法中,会创建一个消息队列MessageQueue,以及将当前所在的线程对象保存在mThread变量中。 在调用 prepare()方法时传入 的false最终会传给MessageQueue,false代表主线程的消息循环不能够调用quit方法来退出。
public final class MessageQueue {
   
    // True if the message queue can be quit.
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; // used by native code

    private boolean mQuitting;
    
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
    
    ......
   
}
    在MessageQueue的构造方法中,将Looper传递过来的参数false保存在mQuitAllowed变量中,从第15行的quit()方法中可以看出 主线程的消息循环不能够调用 quit方法来退出的。在构造方法中还调用了Native层中的nativeInit()本地方法,该方法在Native层创建了一个与MessageQueue对应的NativeMessageQueue对象,并返回该对象的指针给mPtr变量,这样
Java层的 MessageQueue对象和Native层的NativeMessageQueue对象建立了关联。其实,在Andrid 2.3以前,只能在Java层中向MessageQueue中添加消息以驱动Java世界的正常运转,但从Android 2.3开始,MessageQueue的核心部分下移至Native层,可以在Native层利用消息循环来处理他们所在世界的事情,所以在Native层也有对应的Looper,NativeMessageQueue及Handler类。
        当消息队列MessageQueue创建完成后,消息机制还不能正常工作,最后还需要调用Looper类的loop()方法来开启消息循环,接下来我们来分析一下,Android线程消息循环的启动过程。
public final class 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;
        ......
       
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ......
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            
            ......

            msg.recycleUnchecked();
        }
    }
 
    ......
}
    在loop()方法的第10行代码中,会获取Looper中创建的MessageQueue,随后开启一个for死循环,不断调用next()方法从 MessageQueue中获取要处理的消息,如果没有消息或者消息的处理时间没到,线程将会在第14行代码处睡眠,直到被唤醒。第15行代码判断当前返回的消息是否为空(null),为空一般会睡眠,而此处竟然返回消息,说明某处调用了MessageQueue的quit方法,此时就要退出线程消息循环。当获取到非空消息后会在第22行分发该消息来处理,处理完消息又回到第14行代码来获取下一个消息,如此循环,Android应用程序线程循环就是这样被消息驱动的执行业务的。





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值