美文网首页
关于AsyncTask、HandlerThread的理解

关于AsyncTask、HandlerThread的理解

作者: wayDevelop | 来源:发表于2019-05-08 21:57 被阅读0次

    在这之前 我们来先理解下消息循环机制


    image.png
    • 作用

    实现多线程
    在工作线程中执行任务,如 耗时任务
    异步通信、消息传递
    实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作

    1、AsyncTask

    1.21 基本使用
      private class MyTask extends AsyncTask<String, Integer, String> {
            // 方法1:onPreExecute()
            // 作用:执行 线程任务前的操作
            @Override
            protected void onPreExecute() {
                Log.w("TAG", "---開始加載---onPreExecute----");
                // 执行前显示提示
            }
    
            // 方法3:onProgressUpdate()
            // 作用:在主线程 显示线程任务执行的进度
            @Override
            protected void onProgressUpdate(Integer... progresses) {
                Log.w("TAG", "---执行中---onProgressUpdate----"+ + progresses[0] + "%");
    
            }
    
            // 方法4:onPostExecute()
            // 作用:接收线程任务执行结果、将执行结果显示到UI组件
            @Override
            protected void onPostExecute(String result) {
                // 执行完毕后,则更新UI
                Log.w("TAG", "---执行完成---onPostExecute----"+result);
    
            }
    
            // 方法5:onCancelled()
            // 作用:将异步任务设置为:取消状态
            @Override
            protected void onCancelled() {
                Log.w("TAG", "------onCancelled----");
            }
    
            @Override
            protected String doInBackground(String... strings) {
                try {
                    int count = 0;
                    int length = 1;
                    while (count<99) {
                        count += length;
                        // 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
                        publishProgress(count);
                        // 模拟耗时任务
                        Thread.sleep(50);
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                return null;
            }
        }
    

    在需要的地方調用

     /**
                     * 步骤:手动调用execute(Params... params) 从而执行异步线程任务
                     * 注:
                     *    a. 必须在UI线程中调用
                     *    b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
                     *    c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
                     *    d. 不能手动调用上述方法
                     */
                task=new MyTask();
                    task.execute();
    
    //执行结果
     W/TAG: ---開始加載---onPreExecute----
     W/TAG: ---执行中---onProgressUpdate----1%
     W/TAG: ---执行中---onProgressUpdate----2%
     W/TAG: ---执行中---onProgressUpdate----3%
     W/TAG: ---执行中---onProgressUpdate----4%
     W/TAG: ---执行中---onProgressUpdate----5%
    ....................
    .....................
    ....................
     W/TAG: ---执行完成---onPostExecute----null
    
    
    1.2 具体原理介绍
    • AsyncTask的实现原理 = 线程池 + Handler

    其中:线程池用于线程调度、复用 & 执行任务;Handler 用于异步通信

    • 其内部封装了2个线程池 + 1个Handler,具体介绍如下:
    image.png

    执行任务前,通过 任务队列 线程池类(SerialExecutor)将任务按顺序放入到队列中;
    通过同步锁 修饰execute()从而保证AsyncTask中的任务是串行执行的
    SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后看mActive是不是空的,是空的就调用scheduleNext()在头部拿一个给它,也就是说每次当一个任务执行完毕后,下一个任务才会得到执行

    private static class SerialExecutor implements Executor {
            final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
            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();
                }
            }
    
    //通过同步锁 修饰execute()从而保证AsyncTask中的任务是串行执行的
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
        }
    

    任務自行完成后通过

        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    
      private static class InternalHandler extends Handler {
            public InternalHandler(Looper looper) {
                super(looper);
            }
    
            @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
            @Override
            public void handleMessage(Message msg) {
                AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
                switch (msg.what) {
                    case MESSAGE_POST_RESULT:
                        // There is only one result
                        result.mTask.finish(result.mData[0]);
                        break;
                    case MESSAGE_POST_PROGRESS:
                        result.mTask.onProgressUpdate(result.mData);
                        break;
                }
            }
        }
    

    handleMessage中对消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。那么finish()方法的源码如下所示:

        private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    

    总结

    • 静态代码块 创建了THREAD_POOL_EXECUTOR 线程池,用来执行任务

    • 任务队列 线程池 SerialExecutor,内部维护一个双向队列,任务调度,按需执行多个线程,按顺序排列
      通过同步锁execute保证任务是串行执行,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部。然后看mActive是不是空的,是空的就调用scheduleNext()在头部拿一个给它,也就是说每次当一个任务执行完毕后,下一个任务才会得到执行。

    • 任務自行完成后通过调用 postResult(Result result) ,通过handler发送消息,在handleMessage
      对消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。

    2、HandlerThread

    线程间通信的时候,比如Android中常见的更新UI,涉及到的是子线程和主线程之间的通信,实现方式就是Handler+Looper,但是要自己手动操作Looper,不推荐,所以谷歌封装了HandlerThread类(类似于AsyncTask类)。

    2、1 基本使用
          //这个是主线程Handler 实例 还有一个知识点就是如果new Handler时候没有给她传递looper的话,他会默认的去当前运行线程的looper进行联系
           MainHandler mainHandler = new MainHandler(this);
    
    
           HandlerThread handlerThread = new HandlerThread("first");
           handlerThread.start();
            //这是子线程联系的handler 传入了handlerThread的looper
            final Handler childHandler = new Handler(handlerThread.getLooper()) {
                int count = 0;
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    System.out.println("子线程 的handler 接受到数据  现在 将数据发送给 主线程 执行 "+Thread.currentThread().getName());
                    //发送消息给 主线程的handler 让她更新ui
                    Message.obtain(mainHandler, 1, ++count).sendToTarget();
                }
            };
    
    
    
        // 开启一个子线程 调用childHandler 发送信息   
        button.setOnClickListener(new View.OnClickListener() {
             @Override
            public void onClick(View v) {
    
               new Thread(new Runnable() {
                  @Override
                   public void run() {
                    System.out.println("子线程发送数据 " +  Thread.currentThread().getName());
                    //发送给handlerThread的handler 
                     childHandler.sendEmptyMessage(1);
                  }
             }).start();
         }
      });
    
    
        //这个是主线程的Handler 定义成这种形式,可以防止内存泄露
       static class MainHandler extends Handler{
            private WeakReference<MainActivity> mainActivityWeakReference;
            
            MainHandler(MainActivity mainActivity) {
                this.mainActivityWeakReference =new WeakReference<>(mainActivity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                mainActivityWeakReference.get().textView.setText(msg.obj+"");
            }
        }
    
    
    

    HandlerThread是不能更新UI的

    正确使用是 我们仍然需要主线程的handler来更新ui,HandlerThread来接受其他子线程发送过来的消息,这些消息的处理不需要更新ui
    在上面我们可以看见,我们开启了一个子线程,在子线程里面调用了 HandlerThread的childHandler发送信息,childHandler接受到消息,再调用主线程的mainHandler刷新ui。

    通过上面的例子来看 ,大家肯定感觉 这不是多此一举么。一开始 我也是这么想的,后来 我仔细思考,这个HandlerThread 其实可以应用于这些情况下面:

    比如,你同时有好几个子线程,你需要得到他们运算时候发出的信息,以前使用接口回调,还得实现的 ,如果 好几个子线程 岂不是要好几个接口。
    比如 还有 一个子线程运算完 你想知道结果,也可以通过这个来实现。以前你使用callable,接口的话 也还是 比较复杂一点的。

    2、1 工作原理

    内部原理 = Thread类 + Handler类机制,即:

    通过继承Thread类,快速地创建1个带有Looper对象的新工作线程
    通过封装Handler类,快速创建Handler & 与其他线程进行通信

    
    HandlerThread退出销毁
    
     // 方式1:quit() 
      // 特点:效率高,但线程不安全
      public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit(); 
                return true;
            }
            return false;
        }
    
      // 方式2:quitSafely()
      // 特点:效率低,但线程安全
      public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
    
    public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
    
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
        
       
        public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
        
       
        protected void onLooperPrepared() {
        }
    
        @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
        
       
        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;
        }
    
        
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
    
       
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
      
        public int getThreadId() {
            return mTid;
        }
    }
    
    

    首先我们可以看到HandlerThread继承自Thread,因此在run()中的逻辑都是在子线程中运行的。

    接下来就是两个关键的方法,run()和getLooper():
    run()中可以看到是很简单的创建Looper以及让Looper工作的逻辑。
    run()里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这有什么用呢?因为的mLooper在一个线程中执行创建,而我们的handler是在UI线程中调用getLooper()初始化的。
    也就是说,我们必须等到mLooper创建完成,才能正确的返回。getLooper();wait(),notify()就是为了解决这两个线程的同步问题。

    相关文章

      网友评论

          本文标题:关于AsyncTask、HandlerThread的理解

          本文链接:https://www.haomeiwen.com/subject/kejvoqtx.html