安卓开发之消息机制和AsyncTask实现的基本原理

一、基本概述

  1. 在Android中,只可以在UiThread(UI主线程)才可以直接更新界面,不然会抛出异常。
    WHY:
    防止多个线程来修改界面,导致混乱
    通过同步锁来防止界面混乱会导致性能降低 。

  2. 在Android中,长时间的工作(比如联网)都需要在workerThread(分线程/工作线程)中执行。

  3. 在分线程中获取服务器数据后,可通过消息的传递进行线程间的通信,到主线程中更新界面显示。

二、消息机制中的消息Message

  1. Message可以理解为线程间通讯的消息单元,可通过message携带需要传递的数据。

  2. 封装数据:

    public int what  // 标识消息的id
    
    public int arg1   // 可用来携带int类型的数据
    
    public int arg2   // 可用来携带int类型的数据  
    
    public Object obj //可用来携带一个对象数据
    
  3. 与一个Handler对象绑定

    Handler target //绑定的一个handler对象,由该handler对象发送和处理该消息。
    
  4. 创建对象:

    Message.obtain() 从**消息池**中获取消息 /new Message() 重新创建一个消息。
    

三、消息机制中的处理器Handler

Handler是消息Message的处理器,同时负责消息的发送、处理和未处理消息的移除工作。

具体方法

    发送即时消息:sendMessage(Message msg)

    发送延时消息:sendMessage(Message msg, long time) 【延时处理】

    发送带标识的空消息:sendEmptyMessage(int what)

    立即发送Message到队列的最前面:sendMessageAtFrontOfQueue()   

    处理消息:handleMessage(Message msg) 【这是一个回调方法,需要重写回调方法来处理消息】

    移除还未处理的消息:removeMessage(int what)

四、消息机制中的消息队列MessageQueue

  1. MessageQueue用来存放通过Handler发送过来的消息

  2. 消息队列MessageQueue是一个按Message的when排序的优先级队列,先进先出。

    即时消息:when = 发送时的当前时间

    延时消息:when = 发送时的当前时间 + 延迟时间

  3. MessageQueue内部使用链表来实现,容易对队列进行删/增结点

五、消息机制中的循环器Looper(钩子)

消息泵,不断地从MessageQueue中抽取Message执行。一个MessageQueue对应一个Looper。

  1. 负责循环取出消息队列MessageQueue里面的当前需要处理的消息Message

  2. 负责将取出的消息Message交给对应的目标Handler来处理。

  3. 处理完之后,将Mesaage缓存到消息池,以备复用。

六、不使用直接Handler实现异步工作

1.`Activity的runOnUiThread方法在主线程中调用。(内部还是使用下面的Handler的post方法)

//在分线程获取数据,获取到数据在主线程中更新UI
new Thread(){

        @Override
        public void run() {
            super.run();

            final String jsonResult = request(httpUrl, httpArg);
            //分线程获取网络数据,获取数据完后再调用下面的方法

            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(jsonResult!=null)
                    TextView.setText(jsonResult);
                    //主线程UiThread中更新界面
                }
            });

        }
    }.start();

2. View.post(Runnable) : Runnable与View在同一个线程。

   mScrollView.post(new Runnable() {
        @Override
        public void run() {
            mScrollView.scrollTo(0,0);
        }
    });

七、使用Handler实现异步工作

1. 直接调用Handler对象的post方法

handler的post方法:Runnable与handler在同一个线程。

//在分线程获取数据,获取到数据在主线程中更新UI
new Thread(){

        @Override
        public void run() {
            super.run();

            final String jsonResult = request(httpUrl, httpArg);
            //分线程获取网络数据

            // (mHandler在主线程)
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    if(jsonResult!=null)
                    TextView.setText(jsonResult);
                    //主线程UiThread中更新界面
                }
            });

        }
    }.start();

2. 使用Handler对象发送消息

  1. 创建Handler成员变量对象,并重写其handleMessage(Message msg)方法。

  2. 在分线程/主程序创建Message对象,使用Message消息携带异步获得的消息

  3. 使用Handler对象发送Message

  4. handleMessage(Message msg) 回调方法中获取数据。(也可以在该方法中发送延时消息,从而形成一种定时循环,停止循环时使用removeMessage(int what)方法即可)

    // 写在MainActiviy中
    private Handler handler = new Handler(){
    
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    
        if(msg.what==0)
        TextView.setText((String) msg.obj);
    
            }
    };
    

    // 写在MainActivity的onCreate中
      new Thread(){
    
        @Override
        public void run() {
            super.run();
    
            //异步线程获取数据
            final String jsonResult = request(httpUrl, httpArg);
    
            //在分线程/主程序创建Message对象,使用Message消息携带异步消息。
            Message msg = Message.obtain();
            msg.what = 0;
            msg.obj = jsonResult;
            handler.sendMessage(msg);//发送即时消息
    
    
        }
     }.start();
    

    “`

八、Handler对消息的三种处理方式

主要通过Handler中的dispatchMessage方法对Message对象处理

下图为dispatchMessage(Message msg)的源码

安卓开发之消息机制和AsyncTask实现的基本原理_第1张图片

一、通过Message对象的callback处理

主要体现在 Handler.post(Runnable r) / View.post(Runnable r) 方法中。该方法将传入的参数Runnable对象封装成一个Message对象,该Message对象的callback就是传入Runnable对象r.

在dispatchMessage源码中,当Message对象的回调callback不为空时,是通过handleCallback来处理该消息的。下面为handleCallback的源码:

private final void handleCallback(Message message) {  
    message.callback.run();  
}  

二、使用Handler的handleMessage(Message msg)处理。

private Handler handler = new Handler(){

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);



            }
    };

三、通过Handler的callback来处理

public Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {

        //TODO
        return false;

        //返回false时不对该消息拦截,因此下面的消息会被再handleMessage处理
        //返回true时对该消息拦截,因此下面的消息不会被再处理

        //可以不复写下面的方法
    }
}){

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);

        //TODO

    }
};

Handler的使用:

如果通过按钮的点击事件来使用handler传递消息启动异步工作,那么一个异步工作启动后要限制住按钮的可操作性,防止启动多个异步工作。(比如设置逻辑:如果再次点击按钮,则重新启动异步工作,而不是再次启动另一个异步工作;或者设置按钮不可以点击。)


异步任务AsyncTask

  1. 在没有AsyncTask之前,我们可以使用Handler+Thread来实现异步任务的功能需求。

  2. AsyncTask是对Handler和Thread的封装,使用它编码更简洁,效率更高。

  3. AsyncTask封装了ThreadPool线程池,实现了线程的复用,比直接使用Thread效率更高。

  4. API

    1.AsyncTask //不能使用基本数据类型
                Params : 启动任务执行的输入可变参数泛型 
                Progress:后台任务执行的百分比 //一般为Integer类型
                Result:后台执行任务后返回的结果泛型
    
    2.execute(Params...params) 
    //启动任务
    
    3.void onPreExecute()  
    //在分线程工作开始之前在UIThread中执行
    
    4.Result doInBackground(Params..params) 
    //在分线程中执行,完成任务的主要哦工作
    
    5.void onPostExecute(Result  reslut ) 
    // 在doInBackground执行完后在UiThread中执行,一般用来更新界面
    
    
    6.publishProgress(Progress..values) 
    //在分线程中,发布当前进度
    
    7.void onProgressUpdate(Progress..values) 
    //在主线程中,更新当前进度
    

    安卓开发之消息机制和AsyncTask实现的基本原理_第2张图片

  5. 在分线程里不要进行UI操作,比如在doInBackground执行Toast提示操作。

  6. 实例:

        new DemoAsyncTask(.......).execute(...); 
        //自行设计泛型/参数,进度和返回结果的类型,补充各个以上方法即可
    

AysncTask实现的基本原理

AysncTask是一个抽象类:下列代码片段摘自AsyncTask源码(Android-23)

  1. AsyncTask的三种状态:等待、运行、完成

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    public enum Status {
    
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    
    }
    
  2. AsyncTask的构造方法

    A. Params和Result为我们实现AsyncTask时传入的参数和返回结果类型

    B. 在构造函数中,我们写的后台操作 doInBackground 先被封装为一个WorkerRunnable对象mWorker,再将mWorker封装为一个FutureTask对象mFuture。mFuture的任务执行结束后调用done方法通过handler来传递返回的结果:

    C. 在构造方法中,mFuture任务的执行线程并没有马上启动。

    public AsyncTask() {
        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
    
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };
    
        mFuture = new FutureTask(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
    ------------------------
    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
    
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }
    
  3. AsyncTask中的execute方法

    public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    

    然后再看一下executeOnExecutor方法:

    A. 在该方法中,先判断AsyncTask对象的状态,状态不为等待时则抛出异常

    B. 然后在方法中调用我们的预处理方法 onPreExecute(在主线程中调用),最后设置一下异步任务对象的状态和参数,再将上述的FutureTask对象mFuture执行。

    public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }
    
        mStatus = Status.RUNNING;
    
        onPreExecute();
    
        mWorker.mParams = params;
        exec.execute(mFuture);
    
        return this;
    }
    

    C. exec是一个SerialExecutor对象(线程池),这里将mFuture在线程池中执行。

    private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();
        Runnable mActive;
    
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
    
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    
  4. AsyncTask执行任务时并行/串行?

    在Android 1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了修改,可以支持并行和串行,当想要串行执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor)

Handler和AysncTask的考量

AysncTask会使用到线程池,当异步任务特别庞大时,线程池的优势就会显示出来。当异步任务比较多时,可使用AsyncTask来完成异步操作。单个任务使用AsyncTask会销毁较多的资源。

当然AsyncTask的线程池个数也是有限的

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

private static final BlockingQueue sPoolWorkQueue =
        new LinkedBlockingQueue(128);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

以8核手机为例,其核心线程数是9个,最大线程是17,所能最大加入的任务数是128+17=145

参考:

Android异步消息处理机制完全解析,带你从源码的角度彻底理解

你可能感兴趣的:(Android学习)