金沙注册网站-新金沙官网 新金沙官网 金沙注册网站Android线程与消息机制

金沙注册网站Android线程与消息机制

HandlerThread是Android
API提供的一个便捷的类,使用它我们可以快速的创建一个带有Looper的线程,有了Looper这个线程,我们又可以生成Handler,那么
HandlerThread是什么,可以做什么呢,有哪些奇技淫巧可以被我们利用呢?

OUTLINE

§UI线程

§Looper

§消息机制

§线程交互

§AsyncTask

§Activity/Service与主线程

实现原理

在介绍原理之前,我们先使用普通的Thread来创建一个Handler,创建的过程大致如下:

 Handler mHandler;
private void createManualThreadWithHandler() {
  new Thread() {
      @Override
        public void run() {
            super.run();
            Looper.prepare();
            mHandler = new Handler(Looper.myLooper());
            Looper.loop();
        }
    }.start();
}

实现很简单,在目标线程内如下配置

  • 调用Looper.prepare 创建与当前线程绑定的Looper实例
  • 使用上面创建的Looper生成Handler实例
  • 调用Looper.loop()实现消息循环

明白上面的实现步骤,HandlerThread的实现也就简单了,其实现为:

 @Override
public void run() {
  mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
      mLooper = Looper.myLooper();
      notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

确实很简单,无需赘述。

UI线程

先从一个经典错误开始:

android.view.ViewRootImpl$CalledFromWrongThreadException:

Only the original thread that created a view hierarchy can touch its
views

为什么会出现这个错误?

UI的呈现必须在同一个线程里面完成。

试想,如果多个线程可以绘制UI,那么肯定乱套,呈现结果不可预期。

因此界面程序必然有一个UI线程,android,java,windows等都是如此。

Android UI 丈量、排布、绘制最终都是在ViewRootImpl里面完成的。

ViewRootImpl在执行UI操作之前,会进行线程检查。如果当前线程不是UI线程,就会抛出上述异常。

每个应用都对应一个进程,进程创建是伴随一个主线程创建。这个主线程就是UI线程。

Android的主线线程的入口在ActivityThread。ActivityThread和普通的java入口类一样,有一个静态main函数,作为主线程的入口。

ActivityThread与Android应用生命周期密切相关,后续会讲到。

Handler原理

要理解Handler的原理,理解如下几个概念即可茅塞顿开。

  • Message
    意为消息,发送到Handler进行处理的对象,携带描述信息和任意数据。
  • MessageQueue 意为消息队列,Message的集合。
  • Looper
    有着一个很难听的中文名字,消息泵,用来从MessageQueue中抽取Message,发送给Handler进行处理。
  • Handler 处理Looper抽取出来的Message。

Looper

=

Thread是一个线性执行,界面程序需要持续存在,因此需要一个循环,Looper就是Android里面线程循环的封装。

这样说还是比较抽象,那么Looper到底是什么?

如何使用

HandlerThread使用起来很容易,首先需要进行初始化。

 private Handler mHandler;
private LightTaskManager() {
    HandlerThread workerThread = new HandlerThread("LightTaskThread");
    workerThread.start();
    mHandler = new Handler(workerThread.getLooper());
}

注意:上面的workerThread.start();必须要执行。

至于如何使用HandlerThread来执行任务,主要是调用Handler的API

  • 使用post方法提交任务,postAtFrontOfQueue将任务加入到队列前端,postAtTime指定时间提交任务,postDelayed延后提交任务。
  • 使用sendMessage方法可以发送消息,sendMessageAtFrontOfQueue将该消息放入消息队列前端,sendMessageAtTime
    指定时间发送消息,sendMessageDelayed延后提交消息。

通过包裹Handler API,我们可以实现如下代码(仅post相关方法):

 public void post(Runnable run) {
    mHandler.post(run);
}

public void postAtFrontOfQueue(Runnable runnable) {
    mHandler.postAtFrontOfQueue(runnable);
}

public void postDelayed(Runnable runnable, long delay) {
    mHandler.postDelayed(runnable, delay);
}

public void postAtTime(Runnable runnable, long time) {
    mHandler.postAtTime(runnable, time);
}

消息机制

=

任务的循环执行,需要一个队列,可进可出,Android使用消息队列MessageQueue来实现。

一个Looper绑定一个Thread,在这个线程中循环;同时绑定一个MessageQueue,在这个消息队列中存取消息;然后,通过Handler向外接口。通过Handler把消息加入。

MessageQueue,
Looper调用loop进行循环,循环地从消息队列中获取消息,处理消息。

控制优先级

了解到如何使用之外,关于HandlerThread的使用需要上升一个界别,那就是优化。这里的优化主要是合理调整HandlerThread的优先级。

HandlerThread的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为0。线程的优先级的取值范围为-20到19。优先级高的获得的CPU资源更多,反之则越少。-20代表优先级最高,19最低。0位于中间位置,但是作为工作线程的HandlerThread没有必要设置这么高的优先级,因而需要我们降低其优先级。

Message

§消息出队:MessageQueue::next

§消息入队<- 生成消息:绑定Handler

   Handler::obtainMessage

   Message::obtainMessage(Handler)

消息队列中的消息是供Looper来消耗的,Looper通过MessageQueue的next方法取出消息。这个过程是在Looper内部完成,我们不需要太过关心。

不过next方法也是比较讲究的,这个方法最终会调用native的方法,可能会等待睡眠,直到IO事件或者消息入队把它唤醒。

Android的Message必须绑定到一个Handler,由这个Handler来发送和处理消息。消息入队的时候会检查消息是否绑定了Handler,如果没有绑定,会直接抛出异常。

因此我们通常是Handler::obtainMessage,这个方法获得的Message直接绑定到了Handler;另外,也会Message::obtainMessage,当前必须传递Handler。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图