Android AsyncTask內部原理

yaya8_27@ 7年前發布 | 5K 次閱讀 AsyncTask Android開發 移動開發

Android AsyncTask內部原理

<p>@(Android)</p>

[toc]

小筆記

基本使用

/**

  • 在主線程中調用,可以做一些初始化的操作,但是不要在這里做耗時操作 */ @Override protected void onPreExecute() { super.onPreExecute(); }

/**

  • 在子線程中調用,耗時操作全部在這里完成。
  • 如果需要更新進度可以調用 publishProgress(Progress... values) */ @Override protected Object doInBackground(Object[] params) { return null; }

/**

  • 在主線程中調用,顯示子線程進度的回調函數
  • @param values */ @Override protected void onProgressUpdate(Object[] values) { super.onProgressUpdate(values); }

/**

  • 在主線程中調用,傳入的傳輸是在doInBackground中返回的值
  • @param o */ @Override protected void onPostExecute(Object o) { super.onPostExecute(o); }

/**

  • AsyncTask被取消的時候會回調
  • 參數是從哪里傳過來的呢。后面有解釋 */ @Override protected void onCancelled(Object o) { super.onCancelled(o); System.out.println(o instanceof Bitmap); if (o instanceof Bitmap) {
     image_view.setImageBitmap((Bitmap) o);
    
    } } /**
  • AsyncTask被取消的時候會回調 */ @Override protected void onCancelled() { super.onCancelled(); System.out.println("MyAsyncTask ========== onCancelled"); }</code></pre>

    了解了基本的使用方法之后,簡單的實現一個加載圖片的方法吧

    package com.example.wen.asynctask;

import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.ImageView;

import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL;

public class MainActivity extends AppCompatActivity {

private ImageView image_view;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    image_view = (ImageView) findViewById(R.id.image_view);
    new MyAsyncTask().execute();
}

class MyAsyncTask extends AsyncTask {

    /**
     * 在主線程中調用,可以做一些初始化的操作,但是不要在這里做耗時操作
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    /**
     * 在子線程中調用,耗時操作全部在這里完成。
     * 如果需要更新進度可以調用 publishProgress(Progress... values)
     */
    @Override
    protected Object doInBackground(Object[] params) {

        Bitmap bitmap = null;

        try {
            URL url = new URL("https://www.baidu.com/img/bd_logo1.png");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            bitmap = BitmapFactory.decodeStream(connection.getInputStream());

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bitmap;
    }

    /**
     * 在主線程中調用,傳入的傳輸是在doInBackground中返回的值
     * @param o
     */
    @Override
    protected void onPostExecute(Object o) {
        super.onPostExecute(o);

        if (o instanceof Bitmap) {
            image_view.setImageBitmap((Bitmap) o);
        }
    }

    /**
     * 在主線程中調用,顯示子線程進度的回調函數
     * @param values
     */
    @Override
    protected void onProgressUpdate(Object[] values) {
        super.onProgressUpdate(values);
    }

    /**
     * AsyncTask被取消的時候會回調
     */
    @Override
    protected void onCancelled(Object o) {
        super.onCancelled(o);
        System.out.println(o instanceof Bitmap);
        if (o instanceof Bitmap) {
            image_view.setImageBitmap((Bitmap) o);
        }
    }
    /**
     * AsyncTask被取消的時候會回調
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        System.out.println("MyAsyncTask ========== onCancelled");
    }
}

}</code></pre>

內部實現原理

  1. 在主線程中調用 execute(Params... params) 方法或者是指定的線程池的 executeOnExecutor(Executor exec,Params... params)
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread 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();

mWorker.mParams = params;
exec.execute(mFuture);

return this;

}</code></pre>

從上面的代碼中看到兩點:

  1. onPreExecute() 方法在主線程中調用的
  2. 另外,在執行 exec.execute(mFuture); 的時候,會先判斷 mStatus 的狀態。所以每一個AsyncTask對象都只能調用 execute() 方法一次。看一下mStatues的定義:
private volatile Status mStatus = Status.PENDING;

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,【

}</code></pre>

從定義中看到 mStatus 是用 volatile 關鍵字修飾的。 volatile 的作用是保證操作的 可見性 ,即修改之后其他能馬上讀取修改后的值。詳情看 Java 并發編程

那為什么需要用一個 volatile 關鍵字修飾呢。現在有這么一個場景,一個 AsyncTask 對象已經快執行完后臺任務了,準備修改狀態 Statue.FINISH ,但是這個時候,主線程保留了這個 AsyncTask 對象,并且調用了 execute() 方法,這個時候就會導致一個 AsyncTask 被調用了兩次。

而一個 AsyncTask 不允許執行兩次的原因是考慮到了線程安全的問題,如果一個對象被執行了兩次,那么就需要考慮自己定義的成員變量的線程安全的問題了。所以直接在new一個出來比執行兩次的方式更加方便。

當判斷是第一次調用的時候,后面就會調用到 exec.execute(mFuture); 方法。線程池中的 exec.execute() 需要一個 Runnable 的對象,所以讓我們看看mFuture的定義吧:

private final FutureTask<Result> mFuture;

我們發現他是一個 FutureTask 對象。 FutrueTask 對象需要實現一個方法:

/**

  • Protected method invoked when this task transitions to state
  • {@code isDone} (whether normally or via cancellation). The
  • default implementation does nothing. Subclasses may override
  • this method to invoke completion callbacks or perform
  • bookkeeping. Note that you can query status inside the
  • implementation of this method to determine whether this task
  • has been cancelled. */ protected void done() { }</code></pre>

    并且在 FutureTask 的構造方法中,需要傳一個 Callable 對象,那么 Callable 又是一個什么東西呢。簡單來說, Callable 是一個有返回值的 Runnable 。所以 FutureTask 在后臺運行的代碼就是 Callable 中的 call() 的方法。具體來看看在 AsyncTask 源碼中是怎么實現的:

    mWorker = new WorkerRunnable<Params, Result>() {
    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<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); } } };</code></pre>

上面看到的 mWorker 是一個實現了 Callable 的類,并且用一個變量保存了在執行 AsyncTask 時傳入的參數

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

上面的代碼的大概意思就是在一個子線程中調用了我們實現的 doInBackground() 方法。在 FutureTask 中的 done() 方法中有一個 get() 方法,作用就是獲取 doInBackground() 返回的數據。然后將返回的數據傳到 postResult 方法中:

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

在這里可以看到 AsyncTask 的內部是通過 Handler 來實現的。這里還有一個 AsyncTaskResult :

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

AsyncTaskResult(AsyncTask task, Data... data) {
    mTask = task;
    mData = data;
}

}

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

private static class InternalHandler extends Handler { public InternalHandler() { super(Looper.getMainLooper()); }

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

}</code></pre>

因為在 finish() 方法中需要判斷Task是否被取消,而Status是對象內部的成員變量,所以需要保留一個 AsyncTask 對象和在子線程中返回的數據。

當執行完 finish() 方法之后,基本 AsyncTask 的內部原理都講完了。耶!!

 

來自:http://www.jianshu.com/p/2f755d2af53d

 

 本文由用戶 yaya8_27@ 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!