Android線程

1、引言

在Android中,幾乎完全采用了Java的線程機制,由于Android的特性,主線程只處理和界面相關的事情,子線程處理耗時操作。Android中扮演線程角色的有Thread、AsyncTask、IntentService和HandlerThread。對于AsyncTask來說,底層用到了線程池,對于IntentService和HandlerThread,底層用到了線程。

2、AsyncTask

AsyncTask是一個抽象的泛型類,它提供了Params、Progress和Result這三個泛型參數。

public abstract class AsyncTask<Params, Progress, Result> 

其中Params表示參數類型,Progress表示后臺任務的執行進度的類型,Result表示后臺任務返回結果的類型。并且提供了五個核心的方法。

@MainThread
protected void onPreExecute() {
}

此方法有個@MainThread注解,表示在主線程執行,在異步任務執行之前,此方法會被調用,一般可以做一些準備工作。

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

此方法有個@WorkerThread注解,表示在工作(子)線程執行,用于執行異步任務,params參數表示異步任務的輸入參數,可調用publishProgress方法來更新任務進度,publishProgress會調用onProgressUpdate方法。并且此方法需要返回計算結果給onPostExecute方法。

@MainThread
protected void onPostExecute(Result result) {
}

在主線程中執行,在異步任務執行后,此方法會被調用,result是后臺任務的返回值。

@MainThread
protected void onProgressUpdate(Progress... values) {
}

在主線程中執行,后臺任務的執行進度發生變化時,此方法會被調用。

@MainThread
protected void onCancelled(Result result) {
    onCancelled();
}    

在主線程中執行,當異步任務被取消是,此方法會被調用,而onPostExecute則不會被調用。

class MyTask extends AsyncTask<String, Integer, String> {

    @Override
    protected void onPreExecute() {
        Log.i(TAG, "onPreExecute() called");
        textView.setText("loading...");
    }

    @Override
    protected String doInBackground(String... strings) {
        Log.i(TAG, "doInBackground(Params... params) called");
        try {
            OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
            Request request = new Request.Builder()
                    .url(strings[0])
                    .get()
                    .build();
            Call call = client.newCall(request);
            Response response = call.execute();
            if (response.code() == 200) {
                InputStream is = response.body().byteStream();
                long total = response.body().contentLength();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];
                int count = 0;
                int length = 0;
                while ((length = is.read(buf)) != -1) {
                    baos.write(buf, 0, length);
                    count += length;
                    //調用publishProgress公布進度,最后onProgressUpdate方法將被執行
                    publishProgress((int) (count * 1.0f / total * 100));
                    //為了演示進度,休眠500毫秒
                    Thread.sleep(500);
                }
                return new String(baos.toByteArray(), "utf8");
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
        return null;
    }

    //onProgressUpdate方法用于更新進度信息
    @Override
    protected void onProgressUpdate(Integer... progresses) {
        Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
        progressBar.setProgress(progresses[0]);
        textView.setText("loading..." + progresses[0] + "%");
    }

    //onPostExecute方法用于在執行完后臺任務后更新UI,顯示結果
    @Override
    protected void onPostExecute(String result) {
        Log.i(TAG, "onPostExecute(Result result) called");
        textView.setText(result);

        execute.setEnabled(true);
        cancel.setEnabled(false);
    }

    //onCancelled方法用于在取消執行中的任務時更改UI
    @Override
    protected void onCancelled() {
        Log.i(TAG, "onCancelled() called");
        textView.setText("cancelled");
        progressBar.setProgress(0);

        execute.setEnabled(true);
        cancel.setEnabled(false);

    }
}

上面代碼中,實現一個具體的AsyncTask類,主要模擬get請求,并且更新progress。
AsyncTask使用限制

  • 類必須在主線程中加載
  • 對象必須在主線程中創建
  • execute方法必須在UI線程調用
  • 不要在程序中直接調用onPreExecute、onPostExecute、doInBackground、onProgressUpdate方法
  • 一個AsyncTask對象只能執行一次,多次執行會報運行時異常。

3、HandlerThread

HandlerThread本質上是一個線程類,它繼承了Thread,并且可以創建一個帶有looper的線程,進行looper循環;looper對象可以用于創建Handler類來進行來進行調度,必須調用start()方法,Thread會先調用run方法來創建Looper對象。源碼如下:

public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

創建HandlerThread很簡單,它有兩個構造函數,第一個構造函數只需要傳遞線程名稱即可,第二構造函數除了線程名稱,還需要設置優先級的功能

HandlerThread thread = new HandlerThread("downImage");
thread.start();

HandlerThread thread = new HandlerThread("downImage", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();

需要注意的是,HandlerThread的run是一個無限循環,因此明確不需要它時,需要調用quit和quitSafely方法來終止線程的執行。

4、IntentService

IntentService繼承了Service,并且它是一個抽象類,因此必須創建它的子類才能使用IntentService。它用于執行后臺耗時的任務,執行完會自動停止。它擁有較高的優先級,不易被系統殺死(繼承自Service的緣故),因此比較適合執行一些高優先級的異步任務。
IntentService其實還是由HandlerThread和Handler實現的。

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

onCreate中創建了一個HandlerThread ,在通過HandlerThread創建了一個Handler。startService后,最終會調用onStart方法。

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

從上面可以看出,mServiceHandler 發送的消息最終都會在HandlerThread 執行,會將Intent的對象傳遞給onHandleIntent抽象方法(需要子類實現)處理。onHandleIntent處理完之后會調用stopSelf(int startId)方法嘗試停止服務,而不是stopSelf()來立即停止服務。下面創建一個示例看看它是怎樣實現的。

public class DownLoadService extends IntentService {

    public DownLoadService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@androidx.annotation.Nullable @Nullable Intent intent) {
        String url = intent.getStringExtra("url");
        Bitmap bitmap = dowload(url);
        // 保存邏輯處理
    }
}

啟動跟Service一樣

Intent intent = new Intent(this, DownLoadService.class);
startService(intent);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 不同形式的線程雖然都是線程,但是它們仍然具有不同的特性和實用場景: AsyncTask封裝了線程池和Handler...
    胡二囧閱讀 1,139評論 0 4
  • 線程分為主線程和子線程,主線程主要是做與界面相關的事,而子線程往往用于做耗時操作。Android中扮演線程的角色有...
    SeanMa閱讀 1,035評論 0 6
  • 子曰:“溫故而知新,可以為師矣。我們都是站在巨人的肩膀上一點點的前進,最近翻翻以前看的書,決定整理一下讀書筆記,希...
    renkuo閱讀 403評論 0 2
  • Android線程概述 線程分為主線程和子線程,主線程主要處理和界面相關的事情,子線程則往往用于處理耗時操作。線程...
    wangsye閱讀 655評論 0 7
  • 主線程與子線程 主線程:主線程主要處理UI交互,需要在任何時候都有較高的響應速度,否則界面會產生卡頓。主線程中不能...
    左大人閱讀 440評論 0 1