天天看點

Android 從零開始打造異步處理架構

轉載請标明出處:http://www.cnblogs.com/zhaoyanjun/p/5995752.html

本文出自【趙彥軍的部落格】

概述

在Android中會使用異步任務來處理耗時操作,避免出現界面卡頓的問題,當然到目前為止可以使用的異步任務架構有很多,比如:

  • 直接 new Thread()
  • 用Android自帶的AsyncTask
  • 用RxJava
  • 等等

今天我們就來自己嘗試寫一個異步任務處理架構,代碼的設計思路參考AsyncTask

封裝嘗試

既然是異步的架構,那麼肯定是在子線程中,是以第一步我們用自定義的ThreadTask繼承Thread. 并且重寫裡面的run方法。

package com.zyj.app;

/**
 * Created by ${zyj} on 2016/10/17.
 */

public class ThreadTask extends Thread {

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

然後子線程需要把處理結果回調給主線程,我們需要定義3個方法:

  • onStart 任務開始之前調用,運作在主線程。可以做顯示進度條或者加載動畫。
  • onDoInBackground 異步任務執行,運作在子線程。可以做耗時操作。
  • onResult 異步任務處理的結果,運作在主線程。

onDoInBackground這個方法是要在子類中實作的,是以要寫成抽象的方法,那麼ThreadTask類自然也要寫成抽象類。同時這個方法會傳回異步處理結果,這個結果的類型需要寫成泛型,以便在子類中靈活運用。

package com.zyj.app;

import android.support.annotation.MainThread;
import android.support.annotation.WorkerThread;

/**
 * Created by ${zyj} on 2016/10/17.
 */

public abstract class ThreadTask<T> extends Thread  {

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

    /**
     * 任務開始之前調用,運作在主線程
     */
    @MainThread
    public void onStart(){ }

    /**
     * 子線程中調用,運作在子線程
     * @return
     */
    @WorkerThread
    public abstract T onDoInBackground() ;

    /**
     * 子線程傳回的結果,運作在主線程
     * @param t
     */
    @MainThread
    public void onResult( T t ){ }
}

           

另外子線程和主線程通信我們用的是Handler。Handler的初始化工作放在ThreadTask構造函數中完成。

private Handler handler ;

    public ThreadTask(){
        handler = new Handler( Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //在這裡接收子線程發過來的消息
            }
        } ;
    }
           

最後還需要一個execute() 方法啟動線程。在啟動的前一刻最好調用Onstart方法。

/**
     * 開始執行
     */
    public void execute(){
        onStart();
        start();
    }
           

最後一個完整的ThreadTask類是這樣的

package com.zyj.app;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.MainThread;
import android.support.annotation.WorkerThread;

/**
 * Created by ${zyj} on 2016/10/17.
 */

public abstract class ThreadTask<T> extends Thread  {

    private Handler handler ;

    public ThreadTask(){
        handler = new Handler( Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //在這裡接收子線程發過來的消息
                onResult((T) msg.obj);
            }
        } ;
    }

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

        Message message = Message.obtain() ;
        message.obj = onDoInBackground() ;
        handler.sendMessage( message ) ;
    }

    /**
     * 任務開始之前調用,運作在主線程
     */
    @MainThread
    public void onStart(){ }

    /**
     * 子線程中調用,運作在子線程
     * @return
     */
    @WorkerThread
    public abstract T onDoInBackground() ;

    /**
     * 子線程傳回的結果,運作在主線程
     * @param t
     */
    @MainThread
    public void onResult( T t ){ }


    /**
     * 開始執行
     */
    public void execute(){
        onStart();
        start();
    }
}

           

如何使用我們寫好的架構?

new ThreadTask<String>(){

        @Override
        public void onStart() {
            super.onStart();
            Log.d( "ThreadTask " , "onStart線程:" + Thread.currentThread().getName() ) ;
        }

        @Override
        public String onDoInBackground() {
            Log.d( "ThreadTask " , "onDoInBackground線程: " + Thread.currentThread().getName() ) ;

            //模拟耗時操作
            try {
                Thread.sleep( 3000 );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "結果傳回了";
        }

        @Override
        public void onResult(String s) {
            super.onResult(s);
            Log.d( "ThreadTask " , "onResult線程: " + Thread.currentThread().getName()  + " 結果:" + s ) ;
        }
    }.execute();


           

運作的結果:

ThreadTask: onStart線程:main

ThreadTask: onDoInBackground線程: Thread-229

ThreadTask: onResult線程: main 結果:結果傳回了

Handler優化

到目前為止我們的架構初步就封裝好了,但是有沒有缺點呢,肯定是有的。首先每次建立一個ThreadTask的時候都會建立一個Handler,這顯然不是我們想看到的。

  • 要保證Handler的執行個體的唯一性,可以用單例模式來擷取Handler
/**
     * 單例模式,保證handler隻有一個執行個體
     * @return
     */
    private static Handler getHandler(){
        if ( handler == null ){
            synchronized ( MHandler.class ){
                if ( handler == null ){
                    handler= new MHandler( Looper.getMainLooper()) ;
                }
            }
        }
        return handler ;
    }
           
  • MHandler是我們自定義的一個Handler類
private static class MHandler extends Handler {

        public MHandler( Looper looper ){
            super( looper );
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //在這裡接收子線程發過來的消息
            ResultData resultData = (ResultData) msg.obj;
            resultData.threadTask.onResult( resultData.data );
        }
    }
           
  • ResultData是一個消息實體
/**
     * handler發送資料的實體
     * @param <Data>
     */
    private static class ResultData<Data>{
        ThreadTask threadTask ;
        Data data ;
        public ResultData( ThreadTask threadTask  ,Data data  ){
            this.threadTask = threadTask ;
            this.data = data ;
        }
    }
           
  • 一個完整的代碼執行個體
package com.zyj.app;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.MainThread;
import android.support.annotation.WorkerThread;

/**
 * Created by ${zyj} on 2016/10/17.
 */

public abstract class ThreadTask<T> extends Thread  {

    private static Handler handler ;

    public ThreadTask(){
    }

    @Override
    public void run() {
        super.run();
        Message message = Message.obtain() ;
        message.obj = new ResultData<T>( this , onDoInBackground() ) ;
        getHandler().sendMessage( message ) ;
    }

    /**
     * 任務開始之前調用,運作在主線程
     */
    @MainThread
    public void onStart(){ }

    /**
     * 子線程中調用,運作在子線程
     * @return
     */
    @WorkerThread
    public abstract T onDoInBackground() ;

    /**
     * 子線程傳回的結果,運作在主線程
     * @param t
     */
    @MainThread
    public void onResult( T t ){ }


    /**
     * 開始執行
     */
    public void execute(){
        onStart();
        start();
    }

    /**
     * 單例模式,保證handler隻有一個執行個體
     * @return
     */
    private static Handler getHandler(){
        if ( handler == null ){
            synchronized ( MHandler.class ){
                if ( handler == null ){
                    handler= new MHandler( Looper.getMainLooper()) ;
                }
            }
        }
        return handler ;
    }

    private static class MHandler extends Handler {

        public MHandler( Looper looper ){
            super( looper );
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //在這裡接收子線程發過來的消息
            ResultData resultData = (ResultData) msg.obj;
            resultData.threadTask.onResult( resultData.data );
        }
    }

    /**
     * handler發送資料的實體
     * @param <Data>
     */
    private static class ResultData<Data>{
        ThreadTask threadTask ;
        Data data ;
        public ResultData( ThreadTask threadTask  ,Data data  ){
            this.threadTask = threadTask ;
            this.data = data ;
        }
    }
}

           

到現在已經解決了Handler多次建立的問題,那麼這個ThreadTask本質上還是建立線程來運作異步任務,為了避免不斷的建立線程,是以還需要一個線程池。

線程優化

  • 首標明義一個線程池,預設最大10個線程。
/**
     * 線程池,建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
     */
    private static ExecutorService executorService = Executors.newFixedThreadPool( 15 ) ;
           
  • 修改run()方法。
private void run() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain() ;
                message.obj = new ResultData<T>( ThreadTask.this , onDoInBackground() ) ;
                getHandler().sendMessage( message ) ;
            }
        });
    }
           
  • execute() 方法
/**
     * 開始執行
     */
    public void execute(){
        onStart();
        run();
    }
           
  • 完整的代碼執行個體
package com.zyj.app;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.MainThread;
import android.support.annotation.WorkerThread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by ${zyj} on 2016/10/17.
 */

public abstract class ThreadTask<T>  {

    private static Handler handler ;

    /**
     * 線程池,建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
     */
    private static ExecutorService executorService = Executors.newFixedThreadPool( 15 ) ;

    public ThreadTask(){

    }

    private void run() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain() ;
                message.obj = new ResultData<T>( ThreadTask.this , onDoInBackground() ) ;
                getHandler().sendMessage( message ) ;
            }
        });
    }

    /**
     * 任務開始之前調用,運作在主線程
     */
    @MainThread
    public void onStart(){ }

    /**
     * 子線程中調用,運作在子線程
     * @return
     */
    @WorkerThread
    public abstract T onDoInBackground() ;

    /**
     * 子線程傳回的結果,運作在主線程
     * @param t
     */
    @MainThread
    public void onResult( T t ){ }


    /**
     * 開始執行
     */
    public void execute(){
        onStart();
        run();
    }

    /**
     * 單例模式,保證handler隻有一個執行個體
     * @return
     */
    private static Handler getHandler(){
        if ( handler == null ){
            synchronized ( MHandler.class ){
                if ( handler == null ){
                    handler= new MHandler( Looper.getMainLooper()) ;
                }
            }
        }
        return handler ;
    }

    private static class MHandler extends Handler {

        public MHandler( Looper looper ){
            super( looper );
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //在這裡接收子線程發過來的消息
            ResultData resultData = (ResultData) msg.obj;
            resultData.threadTask.onResult( resultData.data );
        }
    }

    /**
     * handler發送資料的實體
     * @param <Data>
     */
    private static class ResultData<Data>{
        ThreadTask threadTask ;
        Data data ;
        public ResultData( ThreadTask threadTask  ,Data data  ){
            this.threadTask = threadTask ;
            this.data = data ;
        }
    }
}

           

架構使用

  • 方式1
new ThreadTask<String>(){

            @Override
            public String onDoInBackground() {
                return "我是線程";
            }
        }.execute();
           
  • 方式2
new MyTask().execute();
    
    class MyTask extends ThreadTask<String> {

        @Override
        public void onStart() {
            super.onStart();
        }

        @Override
        public String onDoInBackground() {
            try {
                //模拟耗時操作
                Thread.sleep( 2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "ThreadTask" ;
        }

        @Override
        public void onResult(String s) {
            super.onResult(s);
        }
    }
           

參考資料

【1】Android AsyncTask 深度了解、簡單封裝、任務隊列分析、自定義線程池

【2】Android 自定義線程池的實戰

【3】Java 單例模式

【4】Android Handler、Loop 的簡單使用

【5】Android 更新UI的幾種方式