多線程(四)、Android多線程使用及AsyncTask源碼分析

本篇是多線程系列的第四篇,如果對前三篇感興趣的也可以去看看。

多線程(一)、基礎概念及notify()和wait()的使用

多線程(二)、內置鎖 synchronized

多線程(三)、線程池 ThreadPoolExecutor 知識點總結

除了前面的線程池的使用外,在Android中,我們除了通過Thread創建線程外,還可以通過 AsyncTaskIntentServiceHandleThread 來創建,線程池前面一篇已經詳細介紹了,下面對其他幾個方法簡單的介紹。

1.1、HandleThread

1.1.1、源碼
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;
        }

        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 ,其在內部自己實現了 Looper,可以單獨發送和接收消息,可以實現ui線程到子線程通信和子線程到子線程的通信。

1.1.2、使用

1、創建HandlerThread 並啓動

        // 創建 HandlerThread 實例對象,參數爲自定義線程名字,作爲標記
        handlerThread = new HandlerThread("HandlerThreadTest");
        // 啓動 HandlerThread
        handlerThread.start();

2、創建工作線程,並複寫 handleMessage 方法

        // 創建工作線程,複寫 handleMessage 方法
        workHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                boolean isMain = Looper.myLooper() == Looper.getMainLooper();
                System.out.println("收到消息:what=" + msg.what + ",message=" + (String) msg.obj);
                System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());
            }
        };

3、發送消息 (這裏模擬在子線程中發送消息)

    new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 使用工作線程向工作線程發送數據。
                        Message msg = Message.obtain();
                        msg.what = 1;
                        msg.obj = "測試數據";
                        workHandler.sendMessage(msg);
                        boolean isMain = Looper.myLooper() == Looper.getMainLooper();
                        System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());

                    }
                }).start();

4、釋放

 // 釋放
 mHandlerThread.quitSafely();

運行結果:

可以看到上面的示例實現了在兩個子線程之間數據傳送。

如果我們將發送消息放在ui線程,即實現了ui線程到子線程的數據通信。

1.2、IntentService

1.2.1、源碼

同樣的,intentService源碼也不多,這裏直接貼出來。

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread
    protected abstract void onHandleIntent(Intent intent);
}

通過源碼,可以看到 IntentService 是一個抽象類,還有一個抽象方法 onHandleIntent,繼承至 Service

1.2.2、使用

新建一個MyIntentService繼承至 IntentService

public class MyIntentService extends IntentService {


    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super("IntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {

        Log.i(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();

        Log.i(TAG, "onHandleIntent: isMainThread=" + isMainThread);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "onHandleIntent: 任務結束");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy");
    }
}

2、跟Service一樣,需要在 AndroidManifest.xml 中進行註冊

 <service android:name=".MyIntentService">

3、啓動服務

startService(new Intent(this, MyIntentService.class));

運行結果:

可以看到在MyIntentServiceonHandleIntent 默認給我們開啓了一個子線程來執行耗時操作,且當任務執行結束後自動停止服務,和Service的區別是 使用Service 可以同時執行多個請求,而使用IntentService 只能同時執行一個請求。

1.3、AsyncTask

AsyncTask 也是我們在Android中使用較爲頻繁的異步通信方法,我們可以通過 AsyncTask輕鬆的實現子線程到ui線程的通信,保證了線程安全,減少了我們通過 Thread + Handler 這種複雜的組合方法來實現,而且AsyncTask實現原理也是通過線程池,所以也具有上面線程池的好處。

1.3.1、使用
public abstract class AsyncTask<Params, Progress, Result>{
    // ......
}

首先可以看到 AsyncTask是一個抽象類,所以,我們在使用的時候需要創建自己的類來實現它,還要實現裏面唯一的一個抽象方法。

protected abstract Result doInBackground(Params... params);

可以看到抽象方法有三個泛型參數,分別是.

Params : 開始異步任務執行時傳入的參數類型,與 doInBackground()參數和執行時調用 excute() 參數類型一致。

Progress : 異步任務執行過程中,返回下載進度值的類型,與 onProgressUpdate() 參數類型一致。

Result : 異步任務執行完成後,返回的結果類型,與doInBackground()的返回值類型還有 onPostExecute() 參數類型一致

使用 AsyncTask 時,除了必須實現的抽象方法外,一般還有幾個方法比較重要,可以根據我們的需求進行重寫

  • onPreExecute:主線程,執行線程任務前自動調用,通過在這個方法裏面進行相應的初始化操作。
  • doInBackground:子線程,必須實現的抽象方法,方法裏面爲工作線程,可以進行耗時操作。
  • onProgressUpdate:主線程,任務執行時候,獲取進度,通過在 doInBackground 方法中調用 publishProgress 來觸發
  • onPostExecute :主線程,當任務執行結束後自動調用,可進行UI更新。

簡單示例:

// 新建 MyAsyncTask 實現 抽象類 AsyncTask 
// Params:Integer
// Progress:String
// Result:Double
public  class MyAsyncTask extends android.os.AsyncTask<Integer, String, Double> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            System.out.println(TAG + "--onPreExecute");
        }
        
        // 這裏的參數類型爲我們自己設置的Params 參數類型
        @Override
        protected Double doInBackground(Integer... integers) {
            // 模擬耗時操作
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(TAG + "--doInBackground  length=" + integers.length);

            for (Integer i : integers) {
                System.out.println(TAG + "--doInBackground  data=" + i);
                // 在 doInBackground 調用 publishProgress 觸發 onProgressUpdate
                publishProgress(i + "—更新後的值");
            }
            // 這裏模擬返回結果,參數類型爲我們設置的 Double
            return 2.2;
        }

        // 這裏的參數類型爲我們自己設置的String類型 (Progress)
        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            for (String data : values) {
                System.out.println(TAG + "--onProgressUpdate  date=" + data);
            }
        }

        // 任務執行結束後自動觸發,參數類型爲我們自己設置的Result 參數類型
        @Override
        protected void onPostExecute(Double aDouble) {
            super.onPostExecute(aDouble);
            System.out.println(TAG + "--onPostExecute  aDouble=" + aDouble);
        }
    }

調用 MyAsyncTask :

通過 execute 方法調用,需要在主線程裏面執行,傳入參數需要和 doInBackground 參數一致,及 Params 設置的類型。

只能調用一次,如果調用兩次 execute 方法,會拋異常

Caused by: java.lang.IllegalStateException: Cannot execute task: the task is already running.

 MyAsyncTask myAsyncTask = new MyAsyncTask();
 myAsyncTask.execute(100,101,102);

結果:

1.3.2、源碼分析

我們深入 AsyncTask 大致看看它是怎麼實現的,首先從 構造方法入手

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        // 通過Callable創建一個任務
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    // 從 doInBackground 中獲取
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 任務執行結束,將結果 發送到
                    postResult(result);
                }
                // 返回結果
                return result;
            }
        }; 
        // 將任務傳給 FutureTask ,複寫其 done 方法
        mFuture = new FutureTask<Result>(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);
                }
            }
        };
    }


// WorkerRunnable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
}

我們看到構造方法中,先創建了一個Callable的任務,在 多線程(一)、基礎概念及notify()和wait()的使用 我們介紹了通過 Callable 來創建線程,可以在線程結束後獲取結果,我們也看到在 任務裏面通過 result = doInBackground(mParams) 獲取到結果。任務創建後,將其放入到 FutureTask 中,這是 Callable 線程創建的標準用法。

看完構造方法,我們再來看調用 execute 方法執行的時候,它又做了啥。

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

這裏沒啥信息,直接調用了executeOnExecutor 方法,傳入了 sDefaultExecutor 和我們設置的參數,再看 executeOnExecutor 方法:

public final AsyncTask<Params, Progress, Result> 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 方法
        onPreExecute();

        mWorker.mParams = params;
        // 執行了我們在構造方法裏面創建的mFuture
        exec.execute(mFuture);

        return this;
    }

這裏面我們還是看到很有用的信息,首先調用了 onPreExecute 方法,這也是我們在執行 AsyncTask 方法的時候,調用的第一個方法,用來進行一些初始化操作,我們的任務通過 exec.execute(mFuture) 進行執行,看看我們傳入的 sDefaultExecutor 是個啥。

最終創建了 SerialExecutor

    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();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

內部聲明瞭一個雙端隊列ArrayDeque類型的mTasks(雙端隊列中offer方法表示從隊列尾插入,poll()表示從隊列頭獲取元素)。

這裏通過 mTasks.offer 將我們的任務不斷的放入阻塞隊列中,再通過 mTasks.poll() 拿出任務,最終通過 THREAD_POOL_EXECUTOR 來執行。

再看 THREAD_POOL_EXECUTOR 又是啥?

static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), sThreadFactory);
        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
 }

我們前面說了,AsyncTask 內部是通過線程池來執行的,看這裏應該就清楚了,任務的執行,實質是通過創建的 threadPoolExecutor線程池,來執行任務。按照前面的老規矩,看看具體參數。

參數說明:

    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 20;
    private static final int KEEP_ALIVE_SECONDS = 3;

int corePoolSize: 1

int maximumPoolSize: 20

long keepAliveTime:3

TimeUnit unit:TimeUnit.SECONDS

BlockingQueue<Runnable> workQueue:new SynchronousQueue<Runnable>()

1個核心線程,非核心線程數目爲20,使用 SynchronousQueue 阻塞隊列。

這裏源碼是查看的API 29 ,不同的版本有區別。

看到了任務的執行,那麼AsyncTask是怎麼實現子線程到主線程的數據通信的呢?

postResult:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

前面構造中,我們看到通過 doInBackground 返回的結果,通過 postResult 方法傳遞出去了,而在 postResult 裏面就是通過Handler,來將數據從子線程傳到了主線程。

    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;
            }
        }
    }

我們再看看publishProgress

protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

其本質,也是通過 Handler 來實現子線程到主線程的數據通信。

總結:所以,AsyncTask 本質是通過線程池來執行任務,並且封裝了 Handler 來實現子線程到ui線程的數據傳輸。

優點:AsyncTask 是一個輕量級的異步任務處理類,輕量級體現在,使用方便、代碼簡潔上,而且整個異步任務的過程可以通過cancel()進行控制。

缺點:不適用於處理長時間的異步任務,一般這個異步任務的過程最好控制在幾秒以內,如果是長時間的異步任務就需要考慮多線程的控制問題;當處理多個異步任務時,UI更新變得困難。

二、總結

多線程部分一共四篇,這篇收了個尾,後面也會繼續對Android其他知識點進行總結,Android進階系列也會一直寫下去,如果文中有錯誤的地方,歡迎大佬們批評指點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章