Android 中的消息机制

来源:互联网 发布:合肥兆度网络骗 编辑:程序博客网 时间:2024/05/17 23:08

前言

涉及知识点:

  • 消息机制:Handler、Looper 和 MessageQueue
  • AsyncTask 基本使用
  • 实现一个简单的 SimpleAsyncTask

消息机制

Android 中的消息机制由三大部分组成:Handler、Looper 和 MessageQueue.

Looper 就是创建一个 MessageQueue,然后进入一个死循环里面不断地读取 MessageQueue 里面的消息,Handler 就是消息的创建者,处理者。

消息机制

由图我们可以看出,消息队列被封装在了 MessageQueue 中,通过 Looper 和线程 Thread 关联起来。而 Handler 又通过 Looper 关联,因而 Handler 最终和线程、线程的消息队列关联上来了。这也就是为什么我们常说更新 UI 的 Handler 必须要在主线程中创建,因为只有在主线程中创建,Handler 才能和主线程的消息队列关联上,这样 handleMessage 才会执行在 UI 线程,这时候更新 UI 才是线程安全的。

题外话:为什么常说只能在 UI 线程更新 UI ?

子线程可以有好多个,但如果每个子线程都直接对UI元素进行操作,界面会混乱不堪,线程会面临安全问题,虽然可以通过加锁机制来解决线程的安全问题,但是加锁会降低运行效率, 所以主线程(UI线程)并没进行加锁限制多线程访问, 可能这就是“出于性能优化考虑”。

既然没有对多线程访问进行限制,而且子线程依然有进行UI操作的需求,那么该如何解决呢?

所以Android规定只能在主线程中进行UI元素的更改,你们一帮菜鸡子线程如果还执意要来修改我管辖的用户界面 就必须先通知我(主线程),我来帮你们完成 :)

——知乎用户:大大大大头啊

AsyncTask 基本使用

我们往往使用 Thread 创建子线程进行耗时操作,但是由于不能在子线程更新 UI,一般就会使用 Handler 发送消息给 UI 线程然后再更新。这个操作起来有点麻烦,在多个任务同时执行的时候,不易于对线程进行精细控制。于是 AsyncTask 应运而生。

public abstract class AsyncTask< Param, Progress, Result > ( ) //三个泛型类型: < 参数类型,后台执行任务的进度类型,返回的结果类型 > 如果不需要某个类型可以设置为 void

一个异步任务一般包含以下步骤:

AsyncTask

实现一个简单的 AsyncTask

下面我们来实现一个简单的 AsyncTask,类名为 SimpleAsyncTask。与 AsyncTask 类似,提供了三个函数:onPreExecute( )、 doInBackground( )、onPostExecute( )。泛型参数为了方便只有一个 doInBackgroud( ) 函数返回值类型的泛型参数。SimpleAsyncTask 执行起来和 AsyncTask 基本一样。首先是 onPreExecute 函数在任务运行之前执行,而且运行在 UI 线程之中。doInBackgroud 运行在后台执行耗时操作,并且将结果返回。onPostExecute 含有一个参数,这个参数就是 doInBaskgroud 的返回结果,onPostExecute 执行在 UI 线程。

public abstract  class SimpleAsyncTask<Result> {    private static final HandlerThread HT = new HandlerThread("SimpleAsyncTask",            Process.THREAD_PRIORITY_BACKGROUND);    static {        HT.start();    }    final Handler mUIHandler = new Handler(Looper.getMainLooper());    final Handler mAsyncHandler = new Handler(HT.getLooper());    /**     * @功能描述:onPreExecute 任务执行之前的初始化操作等     */    protected void onPreExecute(){}    /**     * 后台执行任务     * @return 返回执行结果     */    protected abstract Result doInBackground();    /**     * 返回结果传递给执行在 UI 线程的 onPostExecute     * @param result 执行结果     */    protected void onPostExecuted(Result result){ }    public final SimpleAsyncTask<Result> excute () {        onPreExecute();        mAsyncHandler.post(new Runnable() {            @Override            public void run() {                postResult(doInBackground());            }        });        return this;    }    private void postResult(final Result result){        mUIHandler.post(new Runnable() {            @Override            public void run() {                onPostExecuted(result);            }        });    }}

在 SimpleAsyncTask 里面首先创建了一个 HandlerThread(自带消息队列的 Thread),当线程启动之后就会构建它的消息队列,所以构建完成后,直接在静态代码块里面启动了该线程。然后创建了两个 Handler,分别关联 UI 线程和 HandlerThread 的子线程 mAsyncHandler。剩下三个函数已经解释过了,有需要的时候我们可以重写这三个方法。

execute 是执行的函数,里面先调用 onPreExecute,然后 doInBackground 函数被一个 Runnbale 包装通过 mAsyncTask 提交给了 HandlerThread 线程执行,当得到结果的时候又通过 mUIHandler 将结果提交到一个 Runnable 里面,这个 Runnbale 中执行了 onPostExecute。

下面是调用的示例代码:

new SimpleAsyncTask<String>() {    private void makeToast(String msg){        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();    }    @Override    protected void onPreExecute() {        makeToast("onPreExecute");    }    @Override    protected String doInBackground() {        try{            Thread.sleep(6000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return "doInBackground finish!";    }    @Override    protected void onPostExecuted(String s) {        makeToast("onPostExecuted"+s);    }}.excute();

执行结果就是先 Toast:onPreExecute,延时 6 秒之后 Toast: onPostExecuted doInBackground finish!

执行结果


喜欢就点个赞,有问题就留个言,你不说话我怎么知道你来过

原创粉丝点击