android消息机制分析

消息机制主要用于多线程的通信,在android开发中最常见的场景就是,在子线程中做耗时操作,比如下载图片等等,操作完成后需要在主线程中更新ui,这时就需要用到消息机制来完成子线程与主线程的通信。Android规定访问UI只能在主线程中进行,如果在子线程中访问UI就会发生异常,这是因为ViewRootImpl对UI的操作做了验证,这个验证是在ViewRootImpl的checkThread()方法中完成的。

void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}

系统提供Handler主要原因是解决在子线程中无法更新UI的矛盾。那么刚才提到为什么不允许子线程中访问UI呢?那是因为Android的UI控件并不是线程安全的。如果存在多线程的并发访问可能会导致UI控件处于不可预期的状态。那么系统为什么不对UI控件加锁呢。一方面加锁使逻辑更复杂,二来要进行锁判断,影响效率,阻塞了其它线程,所以简单高效的模型就是单线程。
Handler消息机制常见场景如下简单示例:

public class MainActivity extends Activity {
    private TextView tvText;
    //定义Handler
    private Handler mHandler = new Handler() {
         @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //获取子线程中发送过来的消息,然后在主线程中更新ui
            tvText.setText(msg.obj.toString());
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvText = (TextView) findViewById(R.id.tv_text);
        //创建子线程,启动子线程任务
        new Thread() {
          @Override
            public void run() {
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message msg = Message.obtain();
                msg.obj = Thread.currentThread().getName();
                //子线程任务执行完毕后,将信息封装成消息Message,通过Handler发送回主线程
                mHandler.sendMessage(msg);
            }
        }.start();
    }
}

Android的消息机制其实主要是指Handler的运行机制,而Handler的运行又需要底层MessageQueue和Looper的支撑。MessageQueue中文翻译是指消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper中文翻译为循环,由于MessageQueue只是提供了一个消息的存储,它并不能去处理消息,Looper就填补了这个功能,Looper会以无限循环的方式去查找是否有新消息,如果有的话就处理否则就会一直等待着。Looper中还要一个特殊的概念ThreadLocal,用它可以在不同的线程中存储数据,互不干扰,解决多线程程序并发问题。那么在Handler如何获取到当前线程的Looper呢。就是通过这个ThreadLocal,ThreadLocal可以在不同线程中互不干扰的存储和读取数据,通过ThreadLocal就可以轻松获取到每个线程的Looper。线程默认是没有Looper的,我们在子线程使用Handler必须先创建Looper否则会发生异常。至于主线程为什么可以直接使用Handler呢?那是因为主线程在入口的main方法中就已经帮我们创建了一个Looper对象,并开启了一个Looper循环,帮我构建好了这样一个消息消息循环的环境。
那么Handler为什么能切换线程?
一句话总结就是:Handler是线程间共享的一个对象,而Looper则是每个线程私有的。Handler是共享的意思是,在需要切换的2个线程中都能访问我们创建的Handler对象,比如能调用Handler的sendMessage方法。
下面我们重点介绍一点android Handler消息机制的几个核心类及源码分析:Looper,Handler,Message
Looper的工作原理:
Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。其中涉及到MessageQueue,存放消息的地方,MessageQueue主要包含两个操作:插入和读取。读取本身伴随着删除操作,对应着两个方法分别是enqueueMessage()和next()。enqueueMessage是插入一条消息到消息队列中,next是取出一条信息并将其从队列移除。MQ被封装到Looper里面了,我们不会直接与MQ打交道。在子线程中需要我们手动创建Looper对象

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
        // 开始循环处理消息队列
        Looper.loop();
    }
}

通过 Looper.prepare(); Looper.loop(); 这两个方法就将此线程变成了Looper线程了,看看这两个方法的源码

//每个线程中的Looper对象其实都是一个ThreadLocal,存放在sThreadLocal中
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//looper消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
          //一个线程有且仅有一个looper
            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();
    }
public static void loop() {
        //获取当前线程looper
        final Looper me = myLooper();
        if (me == null) {
         //如果当前线程没有将looper对象定义为ThreadLocal,即没执行looper.prepare 将抛出异常
            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);
            }
          将真正的处理工作交给message的target,即Handler
            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();
        }
    }

Handler的工作原理:
handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。一个线程可以有多个Handler,但是只能有一个Looper!

 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());
            }
        }
      //默认将关联当前线程的looper
        mLooper = Looper.myLooper();
        // looper不能为空,即该默认的构造方法只能在looper线程中使用
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler发送消息
有了handler之后,我们就可以使用 post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long)这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了。

// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
    public final boolean post(Runnable r)
    {
       // 注意getPostMessage(r)将runnable封装成message
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();  //得到空的message
        m.callback = r;  //将runnable设为message的callback,
        return m;
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;  // message的target必须设为该handler!
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

Handler处理消息
说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,

处理消息,该方法由looper调用 ,msg.target.dispatchMessage(msg);
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
         // 如果message设置了callback,即runnable消息,处理callback!
            handleCallback(msg);
        } else {
            if (mCallback != null) {
            // 如果handler本身设置了callback,则执行callback
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 如果message没有callback,则调用handler的钩子方法handleMessage
            handleMessage(msg);
        }
    }
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

Message 解析
Message封装了任务携带的信息和处理该任务的handler。尽管Message有public的默认构造方法,但是应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。如果message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存,擅用message.what来标识信息,以便用不同方式处理message,下面看一下obtain的源码

 /*package*/ int flags;

    /*package*/ long when;
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;
    
    /*package*/ Runnable callback;
    
    // sometimes we store linked lists of these things
    /*package*/ Message next;
    创建消息的时候会带着target,即此message是哪个Handler处理
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

总结
现在重新回顾一下前面举例的子线程处理耗时,然后更新主线程更新ui的过程:完整的走一次消息处理机制的流程
定义一个handler 实现handleMessage方法 处理子线程返回的消息 在主线程中开启一个子线程,由于是在主线程中所以无需创建Looper对象,如果是在其他线程中需手动创建。在子线程执行完毕后,定义一个message
Message msg = Message.obtain(); 并在消息中带了target 即处理此message的handler,然后将message 通过 mHandler.sendMessage(msg); 发送到MessageQueue中,looper处理MessageQueue,然后通过 msg.target.dispatchMessage(msg); 将消息交由handler的dispatchMessage方法处理,再回调到handleMessage,handleMessage 将message在主线程中更新ui,这个消息处理过程就完成了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值