Android深入理解:Handler + Looper + Message

声明:本文是一篇对Handler相关内容的整理(经过相当一段时间,几次内容增减),有相当部分内容来源网络,其中融入部分作者本身的理解,并加以整理。如有涉及到哪位老师的原作,在此深表感谢!

目录

 

Handler + Looper + Message:生产者 + 消费者 + 仓库(任务队列)

1.Handler(生产者add)

1.1.Handler创建对象

1.2.Handler发送消息

1.3.Handler处理消息

1.4.Handler移除消息

2.HandlerThread

2.1.定义:

2.2.适用场景:

2.3.源码:

2.4.DEMO:

3.IntentService

3.1.定义:

3.2.源码:

3.3.DEMO:

3.3.1.多文件下载:

3.3.2.广播更新UI:

3.4.总结:

4.Looper(消费者get and deal)

4.1.定义:

4.2.机制:

4.3.应用:

4.4.quit:

5.Message(任务,产品)(Intent, Runnable, Message)

5.1.定义:

5.2.关键属性:

5.3.构造方式:

6.MessageQueue(任务队列,产品池)

6.1.定义:

7.应用案例

7.1.子线程发消息给子线程

7.2.主线程发消息给子线程

7.3.子线程发消息给主线程

7.4.AsyncTask


Handler + Looper + Message:生产者 + 消费者 + 仓库(任务队列)

作者浅见。

Handler和Looper是多对一的关系,Looper和MessageQueue是一对一的关系。一个线程中,只有一个Looper 和 MessageQueue,但可以有多个Handler。Handler发送消息时,本身作为关联的target存入消息,Looper获取消息时,最终调用消息关联的Handler进行处理。

1.Handler(生产者add)

Handler:线程间通讯,是由Looper和MessageQueue来构建消息机制的。Handler发送消息给MessageQueue,并处理Looper分发过来的消息(谁发送的,最终由谁来处理)。

Handler实现线程转换:

Handler在子线程把Message或者Runnable发送到MessageQueue,然后由Looper循环调度,再召唤发送消息的Handler进行处理,这个时候,处理过程将在Handler所属线程执行,即完成线程切换。

可以这样理解:

(1)跨线程

场景:Handler的加工厂(消息处理场所:handleMessage())在A;Handler想把B的原料运回A加工:

过程:Handler出差到B把原料(Message or Runnable)委托在A的快递 + 仓储公司(Looper + MessageQueue)通过中转邮寄给自己,再由自己在加工厂(A城)加工处理。

(2)子线程

场景:Handler想在B本地加工B的原料:

过程:Handler把加工厂(消息处理场所:handleMessage())建在B城address1,B城address2的原料委托在B的快递公司(Looper + MessageQueue)通过中转邮寄给自己,再由自己在加工厂(B城address1)加工处理。

规则:快递公司邮寄的目的地只能是快递公司所在地(注册地点)。

所以,选择不同城市(Thead)的快递公司(Looper + MessageQueue),可以实现在不同的地点加工原料(Message)。

区别:A城已经存在成熟的快递公司,并一直在运营;而B城没有现成的快递公司,需要临时创建,并启动运营。

1.1.Handler创建对象

每个Handler对象都会绑定一个Looper对象,每个Looper对象对应一个消息队列(MessageQueue)。如果在创建Handler时不指定与其绑定的Looper对象,系统默认会将当前线程的Looper绑定到该Handler上。

在主线程中(系统会自动为主线程创建Looper对象,开启消息循环)可以直接使用new Handler()创建Handler对象,其将自动与主线程的Looper对象绑定。

new Handler()等价于new Handler(Looper.myLooper()),Looper.myLooper():获取当前线程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。

在非主线程中,需先在该线程中手动开启Looper(Looper.prepare()-->Looper.loop()),然后将其绑定到Handler对象上;或者通过Looper.getMainLooper(),获得主线程的Looper,将其绑定到此Handler对象上。

创建Handler对象,还可以通过传入Callback接口类型方式创建。

public Handler(Callback callback) {
    this(callback, false);
}
public interface Callback {
    public boolean handleMessage(Message msg);
}

1.2.Handler发送消息

Handler发送的消息都会加入到Looper的MessageQueue中。Handler只有一个消息队列,即MessageQueue。

关于:

handler.post(new Runnable() {
      @Override
      public void run() {
                        
      }
});

通过post()传进去的Runnable对象将会被封装成消息对象后传入MessageQueue;使用Handler.sendMessage()将消息对象直接加入到消息队列中。

使用post()将Runnable对象放到消息队列中后,当Looper轮循到该Runnable执行时,实际上并不会单独开启一个新线程,而仍然在当前Looper绑定的线程中执行,Handler只是调用了该线程对象的run()而已。如,在子线程中定义了更新UI的指令,若直接开启将该线程执行,则会报错;而通过post()将其加入到主线程的Looper中并执行,就可以实现UI的更新。

使用sendMessage()将消息对象加入到消息队列后,当Looper轮询到该消息时,就会调用Handler的handleMessage()来对其进行处理。再以更新UI为例,使用这种方法的话,就先将主线程的Looper绑定在Handler对象上,重载handleMessage()来处理UI更新,然后向其发送消息就可以了。

如果Handler对象与其调用者在同一线程中,如果在Handler的消息处理方法中设置了延时操作,则调用线程也会堵塞,因为Looper轮循是线性的,所以Handler处理Message的过程也是线性的。

1.3.Handler处理消息

Handler 在处理消息时,会有三种情况:

if :msg.callback 不为空

这在使用 Handler.postXXX(Runnable) 发送消息的时候会发生,直接调用 Runnable 的 run() 方法。

else if :mCallback 不为空

如果构造Handler时候以 Handler.Callback 为参数构造 Handler 时会发生,调用构造函数里传入的 handleMessage() 方法,如果返回 true,那就不往下走了。

else :最后就调用 Handler.handleMessage() 方法

需要我们在 Handler 子类里重写

1.4.Handler移除消息

public final void removeCallbacks(Runnable r){

    mQueue.removeMessages(this, r, null);

}

public final void removeMessages(int what) {

    mQueue.removeMessages(this, what, null);

}

2.HandlerThread

2.1定义

HandlerThread 是一个包含 Looper 的 Thread,我们可以直接使用这个 Looper 创建 Handler。避免在子线程中对Looper手动繁琐的操作(Looper.prepare() ——> Looper.loop()),让我们可以直接在线程中使用 Handler 来处理异步任务。

2.2适用场景

为某个任务 / 回调单独开启线程,并提供由Handler + Looper提供任务(Message)调度机制。

HandlerThread 本身是一个Thread,需要start()启动。

HandlerThread 在run() 方法中为本线程创建了Looper(Looper.prepare()),调用 onLooperPrepared 后开启了循环(Looper.loop())

HandlerThread 需要在子类中重写 onLooperPrepared,做Looper启动前的初始化工作

HandlerThread 可以指定优先级,注意这里的参数是 Process.XXX 而不是 Thread.XXX

HandlerThread = Thread + Looper,适合在子线程中执行耗时的、可能有多个任务的操作的场景,比如说多个网络请求操作,或者多文件 I/O 等等。

2.3.源码:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    /**
     * 子类需要重写的方法,在这里做一些执行前的初始化工作
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }
    //调用 start() 后就会执行的 run()
    @Override
    public void run() {
        mTid = Process.myTid();
	//创建了 Looepr
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();//Looper 已经创建,唤醒阻塞在获取 Looper 的线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();//开始循环
        mTid = -1;
    }
    /**
     * 获取当前线程的 Looper
     * 如果线程不是正常运行的就返回 null
     * 如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    /**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
    /**
     * Quits the handler thread's looper.
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    /**
     * Quits the handler thread's looper safely.
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

2.4.DEMO:

使用 HandlerThread 实现子线程完成多个下载任务。

DownloadThread,它有两个 Handler 类型的成员变量,一个是用于在子线程传递、执行任务,另一个用于外部传入,在主线程显示下载状态:

public class DownloadThread extends HandlerThread implements Handler.Callback {
    private final String KEY_URL = "url";
    public static final int TYPE_START = 1;
    public static final int TYPE_FINISHED = 2;
    /**
     * 外部传入,通知主线程显示下载状态
     */
    private Handler mUIHandler;
    /**
     * 内部创建,子线程传递、执行任务
     */
    private Handler mWorkerHandler;
    /**
     * download list
     */
    private List<String> mDownloadUrlList;
    public DownloadThread(final String name) {
        super(name);
    }
    /**
     * 执行初始化任务
     */
    @Override
    protected void onLooperPrepared() {
        super.onLooperPrepared();
        mWorkerHandler = new Handler(getLooper(), this);
        if (mUIHandler == null) {
            throw new IllegalArgumentException("No UIHandler!");
        }
        // 将接收到的任务消息挨个添加到消息队列中
        for (String url : mDownloadUrlList) {
            Message message = mWorkerHandler.obtainMessage();
            Bundle bundle = new Bundle();
            bundle.putString(KEY_URL, url);
            message.setData(bundle);
            mWorkerHandler.sendMessage(message);
        }
    }
    public void setDownloadUrls(String... urls) {
        mDownloadUrlList = Arrays.asList(urls);
    }
    /**
     * 获取主线程 Handler
     */
    public Handler getUIHandler() {
        return mUIHandler;
    }
    /**
     * 注入主线程 Handler
     */
    public DownloadThread setUIHandler(final Handler UIHandler) {
        mUIHandler = UIHandler;
        return this;
    }
    /**
     * 子线程中执行任务,完成后发送消息到主线程
     */
    @Override
    public boolean handleMessage(final Message msg) {
        if (msg == null || msg.getData() == null) {
            return false;
        }
        String url = (String) msg.getData().get(KEY_URL);
        //下载开始,通知主线程
        Message startMsg = mUIHandler.obtainMessage(TYPE_START, "\n 开始下载 @" + url);
        mUIHandler.sendMessage(startMsg);
        SystemClock.sleep(2000);    //模拟下载
        //下载完成,通知主线程
        Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, "\n 下载完成 @" + url);
        mUIHandler.sendMessage(finishMsg);
        return true;
    }
    @Override
    public boolean quitSafely() {
        mUIHandler = null;
        return super.quitSafely();
    }
}

创建一个子线程 mWorkerHandler,在 onLooperPrepared()中初始化 Handler,使用的是 HandlerThread 创建的 Looper 。同时将外部传入的下载 url 以 Message 的方式发送到子线程中的 MessageQueue 中。

当调用 DownloadThread.start() 时,子线程中的 Looper 开始工作,会按顺序取出消息队列中的队列处理,然后调用子线程的 Handler 处理(handleMessage()),在这个方法中进行耗时任务,然后通过 mUIHandler 将下载状态信息传递到主线程

在外部Activity(Fragment)实现Hander.Callback接口,调用:

mUIHandler = new Handler(this);
mDownloadThread = new DownloadThread("下载线程");
mDownloadThread.setUIHandler(mUIHandler);
mDownloadThread.setDownloadUrls("http://pan.baidu.com/s/1qYc3EDQ",
        "http://bbs.005.tv/thread-589833-1-1.html", 
        "http://list.youku.com/show/id_zc51e1d547a5b11e2a19e.html?");
mDownloadThread.start();

同时在Activiy的onDestroy()方法中退出下载线程:

mDownloadThread.quitSafely();

3.IntentService

3.1.定义:

IntentService 是一个抽象类,本身是一个Service,同时又在内部创建了一个HandlerThread 。IntentService 使用工作线程逐一处理所有启动请求。如果你不需要在 Service 中执行并发任务,IntentService 是最好的选择。

3.2.源码:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    //内部创建的 Handler
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            //调用这个方法处理数据
            onHandleIntent((Intent) msg.obj);
            //处理完Service就自尽了
            stopSelf(msg.arg1);
        }
    }
    //子类需要重写的构造函数,参数是服务的名称
    public IntentService(String name) {
        super();
        mName = name;
    }
    //设置当前服务被意外关闭后是否重新启动
    //如果设置为 true,onStartCommand() 方法将返回 Service.START_REDELIVER_INTENT,这样当
    //当前进程在 onHandleIntent() 方法返回前销毁时,会重启进程,重新使用之前的 Intent 启动这个服务
    //(如果有多个 Intent,只会使用最后的一个)
    //如果设置为 false,onStartCommand() 方法返回 Service.START_NOT_STICKY,当进程销毁后也不重启服务
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        //创建时启动一个 HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        //拿到 HandlerThread 中的 Looper,然后创建一个子线程中的 Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //将 intent 和 startId 以消息的形式发送到 Handler
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     *
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    @Override
    public void onDestroy() {
        mServiceLooper.quit();    //值得学习的,在销毁时退出 Looper
    }
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

源代码做了以下几件事情:

(1)创建了一个 HandlerThread 默认的工作线程

(2)使用 HandlerThread 的 Looper 创建了一个 Handler,这个 Handler 执行在子线程

(3)在 onStartCommand() 中调用 onStart(),然后在 onStart() 中将 intent 和 startId 以消息的形式发送到 Handler

(4)在 Handler 中将消息队列中的 Intent 按顺序传递给 onHandleIntent() 

(5)在处理完所有启动请求后自动停止服务,不需要我们调用 stopSelf()

可以看到在 handleMessage 方法中只调用了一次onHandleIntent() 之后就调用 stopSelf() 了,那么说,该Handler只能执行一个任务么?

非也!stopSelf() 方法传递了一个 id,这个 id 是启动服务时 IActivityManager 分配的 id,当我们调用 stopSelf(id) 方法结束服务时,IActivityManager 会对比当前 id 是否为最新启动该服务的 id,如果是就关闭服务。子线程中onHandleIntent方法经历了耗时操作,当本次stopSelf(id)调用之前,很可能onStartCommand会多次调用,每次调用都会生成新的启动id,所以本次stopSelf传入的id,很可能已经不是最新启动服务的id了。

因此只有当最后一次启动 IntentService 的任务执行完毕才会关闭这个服务。

注意:只有onHandleIntent方法是执行在子线程的,因为处理消息的looper是子线程HandlerThread提供的looper。其余方法均在主线程运行。

由于最终每个任务的处理都会调用 onHandleIntent(),因此使用 IntentService 也很简单,只需实现 onHandleIntent() 方法,在这里执行对应的后台工作即可。

3.3.DEMO:

3.3.1.多文件下载:

/**
 * Description:使用 IntentService 实现下载
 */
public class DownloadService extends IntentService {
    private static final String TAG = "DownloadService";
    public static final String DOWNLOAD_URL = "down_load_url";
    public static final int WHAT_DOWNLOAD_FINISHED = 1;
    public static final int WHAT_DOWNLOAD_STARTED = 2;
    public DownloadService() {
        super(TAG);
    }
    private static Handler mUIHandler;
    public static void setUIHandler(final Handler UIHandler) {
        mUIHandler = UIHandler;
    }
    /**
     * 这个方法运行在子线程
     *
     * @param intent
     */
    @Override
    protected void onHandleIntent(final Intent intent) {
        String url = intent.getStringExtra(DOWNLOAD_URL);
        if (!TextUtils.isEmpty(url)) {
            sendMessageToMainThread(WHAT_DOWNLOAD_STARTED, "\n " + DateUtils.getCurrentTime() + " 开始下载任务:\n" + url);
            try {
                Bitmap bitmap = downloadUrlToBitmap(url);
                SystemClock.sleep(1000);    //延迟一秒发送消息
                sendMessageToMainThread(WHAT_DOWNLOAD_FINISHED, bitmap);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 发送消息到主线程
     *
     * @param id
     * @param o
     */
    private void sendMessageToMainThread(final int id, final Object o) {
        if (mUIHandler != null) {
            mUIHandler.sendMessage(mUIHandler.obtainMessage(id, o));
        }
    }
    /**
     * 下载图片
     *
     * @param url
     * @return
     * @throws Exception
     */
    private Bitmap downloadUrlToBitmap(String url) throws Exception {
        HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
        BufferedInputStream in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
        Bitmap bitmap = BitmapFactory.decodeStream(in);
        urlConnection.disconnect();
        in.close();
        return bitmap;
    }
}

主线程调用:

public void downloadImage() {
    DownloadService.setUIHandler(new Handler(this));
    Intent intent = new Intent(this, DownloadService.class);
    for (String url : urlList) {
        intent.putExtra(DownloadService.DOWNLOAD_URL, url);
        startService(intent);
    }
    mBtnDownload.setEnabled(false);
}

(1)设置 UI 线程的 Handler 给 IntentService

(2)使用 startService(intent) 启动 IntentService 执行图片下载任务

(3)在 Handler 的 handleMessage 中根据消息类型进行相应处理

在第一次启动 IntentService 后,IntentService 仍然可以接受新的请求,接受到的新的请求被放入了工作队列中,等待被串行执行。

3.3.2.广播更新UI:

public class MyIntentService extends IntentService {
    /**
     * 是否正在运行
     */
    private boolean isRunning;
    /**
     *进度
     */
    private int count;
    /**
     * 广播
     */
    private LocalBroadcastManager mLocalBroadcastManager;
    public MyIntentService() {
        super("MyIntentService");
        Logout.e("MyIntentService");
    }
    @Override
    public void onCreate() {
        super.onCreate();
        Logout.e("onCreate");
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        Logout.e("onHandleIntent");
        try {
            Thread.sleep(1000);
            isRunning = true;
            count = 0;
            while (isRunning) {
                count++;
                if (count >= 100) {
                    isRunning = false;
                }
                Thread.sleep(50);
                sendThreadStatus("线程运行中...", count);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 发送进度消息
     */
    private void sendThreadStatus(String status, int progress) {
        Intent intent = new Intent(IntentServiceActivity.ACTION_TYPE_THREAD);
        intent.putExtra("status", status);
        intent.putExtra("progress", progress);
        mLocalBroadcastManager.sendBroadcast(intent);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Logout.e("线程结束运行..." + count);
    }
}

3.4.总结:

IntentService是一个串行执行异步任务、会自尽的 Service,优先级比较高,在后台不会轻易被系统杀死;它可以接收多个 Intent 请求,然后在子线程中按顺序执行。

适合于执行由UI触发的处理多任务的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI。

IntentService,在onHandlerIntent()的回调里面来处理塞到IntentService的任务。所以IntentService就不仅仅具备了异步线程的特性,还同时保留了Service不受主页面生命周期影响的特点。我们可以在IntentService里面通过设置闹钟间隔性的触发异步任务,例如刷新数据,更新缓存的图片等。

首先,因为IntentService内置的是HandlerThread作为异步线程,所以每一个交给IntentService的任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

其次,通常使用到IntentService的时候,我们会结合使用BroadcastReceiver把工作线程的任务执行结果返回给主UI线程。使用广播容易引起性能问题,我们可以使用LocalBroadcastManager来发送只在程序内部传递的广播,从而提升广播的性能。我们也可以使用runOnUiThread()快速回调到主UI线程。

最后,包含正在运行的IntentService的程序相比起纯粹的后台程序更不容易被系统杀死,该程序的优先级是介于前台程序与纯后台程序之间的。

4.Looper(消费者get and deal)

4.1.定义:

消息循环管理器。每个线程只有一个Looper,负责管理MessageQueue, 以无限循环的方式去查找MessageQueue中是否有新消息。如果有,MessageQueue中取出消息,并将消息分给对应的Handler处理;如果没有就standby(等待)。一个线程创建Handler时首先需要创建Looper的,不然报错:RuntimeException: No Looper; Looper.prepare() wasn't called on this thread,而且每个线程下只能创建一个Looper,不然会报错:RuntimeException: Only one Looper may be created per thread。

4.2.机制:

线程中默认没有 Looper,我们需要调用 Looper.prepare() 方法为当前线程创建一个 Looper,调用Looper.loop()来使消息循环起作用,使用Looper.prepare()和Looper.loop()就可以让消息处理在当前线程(主线程默认开启,不需要手动调用)中完成。

Looper.loop()之后,进入无限循环:

for (;;) {    //无限循环模式
    Message msg = queue.next(); //从消息队列中读取消息,可能会阻塞
    if (msg == null) {    //当消息队列中没有消息时就会返回,不过这只发生在 queue 退出的时候
        return;
    }
    //...
    try {
        msg.target.dispatchMessage(msg);    //调用消息关联的 Handler 处理消息
    } finally {
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
    //...
    msg.recycleUnchecked();    //标记这个消息被回收
}

可以看到,loop()其实是调用 MessageQueue.next() 方法取消息,如果没有消息的话会阻塞,直到有新的消息进入或者消息队列退出。

拿到消息后Looper 并没有执行消息,而是调用消息关联的 Handler (target)处理消息,真正执行消息的还是添加消息到队列中的那个 Handler。

所以应该说Looper属于哪个线程的,消息处理就在哪个线程执行!!!

4.3.应用:

首先,Android 本身是由事件驱动的,在主线程中…… looper.loop() 不断地接收事件、处理事件。四大组件的调度、输入事件、绘制请求、每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。

只能是UI线程中某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不能是 Looper.loop() 阻塞UI线程。也可以理解为,UI线程中执行的代码其实是执行在looper.loop()的循环周期中。

主线程同一时间只能处理一个 Message,所以,如果UI线程中某个消息处理时间过长(点击事件业务处理操作),那么下一次的消息比如用户的点击事件不能处理了,整个循环就会产生卡顿,时间一长就成了ANR

4.4.quit:

Looper开启loop()之后,必须保证结束quit(),quitSafely()!!!

Looper 两种结束方式的区别:

(1)quit():

立即回收链表中所有消息,并且禁止后续消息入队列。在停止后如果 Handler 还发送消息,会返回 false,表示入队失败,所以这个方法是不安全的。

(2)quitSafely():

将还未执行的消息回收掉,后续进入队列的消息将不会被处理,同时标记消息队列为退出状态。Handler.sendMessage 也会返回 false。当消息队列被标记位退出状态时,它的 next() 方法会返回 null,于是 Looper.loop() 循环就结束了。

5.Message(任务,产品)(Intent, Runnable, Message)

5.1.定义:

Message可以承载任意类型的对象和描述信息,可以被发送给 Handler。

5.2.关键属性:

(1)public int what;                 // 用来标识一个消息,接收消息方可以根据/它知道这个消息是做什么的

(2)public int arg1;

         public int arg2;                  // 数据载体:int 型

(3)public Object obj;              // 数据载体: 对象型

(4)/*package*/Bundle data;    // 数据载体:复杂对象

(5)/*package*/Handler target; // 发送和处理消息关联的 Handler

(6)private static final Object sPoolSync = new Object();

(7)private static Message sPool; // 回收消息链表

(8)private static int sPoolSize = 0;

5.3.构造方式:

new Message();

Message.obtain();  推荐

Handler.obtainMessage();   推荐

推荐使用后两个,会从一个消息回收池里获取消息,而不是新建一个,这样可以节省内存。原理:如果 sPool回收消息链表)存在就从复用消息链表头部取一个消息,然后重置它的标志位;如果不存在复用消息链表就新建一个消息。

一个消息在被 Looper 处理时或者移出队列时会被标识为 FLAG_IN_USE,然后会被加入回收的消息链表,这样我们调用 Message.obtain() 方法时就可以从回收的消息池中获取一个旧的消息,从而节约成本。

6.MessageQueue(任务队列,产品池)

6.1定义:

MessageQueue(先进先出):消息队列,管理着一个 Message 的列表,Handlers 为它添加消息,Looper 从中取消息。。虽然名为队列,但事实上它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表的,其中主要有插入enqueue()和从中拿走并删除next()两个方法。

7.应用案例

写在Looper.loop()之后的代码不会被立即执行,这个函数内部是一个循环,当调用后mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。

警惕线程未终止造成的内存泄露;譬如在Activity中关联了一个生命周期超过Activity的Thread,在退出Activity时切记结束线程。一个典型的例子就是HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了Activity生命周期,我们必须手动在Activity的销毁方法中中调运thread.getLooper().quit();才不会泄露。

另外,注意不要在任何子线程持有 UI 组件或者 Activity 的引用。

7.1.子线程发消息给子线程

class TestThread extends Thread{
    @Override
    public void run() {
        super.run();
        // prepare MessageQueue and looper
        Looper.prepare();
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
// deal message……
            }
        };
handler.sendMessage(new Message())
        // start looper
        Looper.loop();
    }
}

7.2.主线程发消息给子线程

(1)子线程中创建持有子线程Looper的Handler

(2)主线程使用子线程中Handler发送消息,最终在子线程的Handler中执行。

7.3.子线程发消息给主线程

方案一:子线程中创建持有主线程Looper的Handler,由该Handler在子线程发送消息到主线程的MessageQueue,并由该Handler在主线程执行。

          Handler handler = new Handler(getMainLooper());

方案二:子线程持有主线程创建的Handler对象,并由该Handler在子线程发送消息到主线程的MessageQueue,并由该Handler在主线程执行。

7.4.AsyncTask

默认情况下,所有的AsyncTask任务都是被线性调度执行的,他们处在同一个任务队列当中,按顺序逐个执行。假设你按照顺序启动20个AsyncTask,一旦其中的某个AsyncTask执行时间过长,队列中的其他剩余AsyncTask都处于阻塞状态,必须等到该任务执行完毕之后才能够有机会执行下一个任务。

为了解决线性队列等待的问题,我们可以使用AsyncTask.executeOnExecutor()强制指定AsyncTask使用线程池并发调度任务。

使用AsyncTask很容易导致内存泄漏,一旦把AsyncTask写成Activity的内部类的形式就很容易因为AsyncTask生命周期的不确定而导致Activity发生泄漏。

猜你喜欢

转载自blog.csdn.net/qingjuyashi/article/details/83148302