Android異步之旅:探索AsyncTask

前言:?

在Android應用程序開發中,異步操作是非常常見的需求。比如,我們可能需要在后臺線程中執行網絡請求、數據庫操作或者其他耗時的任務,而不阻塞UI線程。為了實現這些異步操作,Android提供了多種方式,其中之一就是使用AsyncTask類。

1.什么是AsyncTask?


? ? ?AsyncTask是一種輕量級的異步任務類,它可以在線程池中執行后臺任務,然后把執行的進度和最終的結果傳遞給主線程并在主線程中更新UI。(兩個線程池+Handler

2.介紹AsyncTask類的泛型參數和核心方法


AsyncTask是一個抽象的泛型類,它提供了ParamsProgressResult這三個泛型參數。

public abstract class AsyncTask<Params, Progress, Result>

其中,

  • 🟨Params表示參數的類型
  • 🟨Progress表示后臺任務的執行進度和類型
  • 🟨Result表示后臺任務的返回結果

AsyncTask類提供了四個核心方法(按照執行順序介紹):

1??onPreExecute():主線程中執行,用于準備工作

2??doInBackground(Params...params):在線程池中執行,用于執行異步任務。在此方法中可以通過publishProgress方法來更新任務的進度,publishProgress方法調用onProgressUpdate方法。

3??onProgressUpdate(Progress...values):在主線程中執行,當后臺任務的執行進程發送變化時此方法會被調用。

4??onPostExecute(Result result):在主線程中執行,在異步任務執行之后,此方法會被調用。

AsyncTask還提供了onCancelled()方法,它同樣在主線程中執行,當異步任務被取消時,onCancelled會被調用,這個時候,onPostExecute則不會被調用。

3.如何使用AsyncTask?


使用AsyncTask的步驟:

🔶步驟一:繼承AsyncTask類,并實現它的幾個回調方法,比如doInBackground()方法用來執行后臺任務,onPostExecute()方法用來更新UI。

🔶步驟二:在UI線程中創建AsyncTask的實例,并調用execute()方法來啟動異步操作。

示例:

在Activity中,創建一個子類來繼承AsyncTask,完成以下任務:

1.展示ProgressDialog

2.發送網絡請求

3.關閉ProgressDialog

代碼如下:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new AsyncTask_test().execute("");}
?public static ProgressDialog dialog;public class AsyncTask_test extends AsyncTask<String,Integer,Long> {@Overrideprotected void onPreExecute() {dialog=ProgressDialog.show(MainActivity.this,"", "努力加載中");}
?@Overrideprotected Long doInBackground(String... strings) {OkHttpClient okHttpClient = new OkHttpClient();Request request=new Request.Builder().url("https://www.httpbin.org/get?a=1&b=2").build();Response response= null;try {response = okHttpClient.newCall(request).execute();if(!response.isSuccessful()){Log.e("xxx","網絡請求失敗"+response);}String responsedata=response.body().string();if(responsedata!=null){Log.e("xxx","輸出:"+responsedata);}} catch (IOException e) {Log.e("xxx","報錯"+e);throw new RuntimeException(e);}
?return null;}
?@Overrideprotected void onPostExecute(Long aLong) {if(dialog!=null){dialog.dismiss();}super.onPostExecute(aLong);}}
}

AsyncTask在使用時的注意事項:

🟠AsyncTask的對象必須在主線程中創建。(原因:AsyncTask的handler對象是靜態的,Handler對象要切換到主線程,由于靜態成員在類加載時就被初始化,因此AsyncTask必須在主線程中加載)

🟠execute方法必須在UI線程調用

🟠不要在線程中直接調用onPreExecute()、onPostExecute、doInBackground和onProgressUpdate方法。

🟠一個AsyncTask對象只能執行一次,即只能調用一次execute方法,否則會報運行時異常。

4.AsyncTask的工作原理


工作原理:

??當asyncTask執行execute()方法的時候,會先調用onPreExecute()方法。

??然后調用SERIAL_EXECUTOR的execute(mFuture),把任務加入到隊列的尾部等待執行。

??執行的時候調用THREAD_POOL_EXECUTOR的execute(mFuture)方法。

??mFuture調用mWorker的call()方法,在call()方法中調用了dolnBackground()方法,并在最后調用了postResult()方法。

??postResult()方法也就是通過Handler發送消息給主線程,在主線程中調用AsyncTask的finish()方法,來決定是調用onCancelled()還是onPostExecute()方法。

源碼解析:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);
}@MainThreadpublic 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;}

其中,sDefaultExecutor是一個串行的線程池,一個進程中所有的AsyncTask全部在這個串行的線程池中排隊執行。在executeOnExecutor方法中,AsyncTask的onPreExecute方法最先執行,然后線程池開始執行,下面是線程池的執行過程:

 ?  public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;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);}}}

當一個AsyncTask任務執行完后,AsyncTask會調用scheduleNext()方法繼續執行下一個任務直到所有任務被執行為止,總的來說,默認情況下,AsyncTask是串行執行的。

AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handle:

SerialExecutor:用于任務的排隊

THREAD_POOL_EXECUTOR:用于真正地執行任務

Handler:用于將執行環境從線程池切換到主線程

在AsyncTask的構造方法中有如下一段代碼,由于FutureTask的run方法會調用mWorker的call方法,因此mWorker的call方法最終會在線程池中執行。

  public AsyncTask(@Nullable Looper callbackLooper) {mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler(): new Handler(callbackLooper);
?mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Result result = null;try {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedresult = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}};
?mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected 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);}}};}
?

在mWorker的call方法中,首先將mTaskInvoked設為true,表示當前任務已經被調用過了,然后執行AsyncTask的doInBackground方法,接著將返回值傳遞給postResult方法,它的實現如下:

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

在上面代碼中,postResult方法會通過Handler對象發送一個MESSAGE_POST_RESULT的消息,這個Handler對象的定義如下:

getHandler()獲取Handler對象

private Handler getHandler() {return mHandler;
}

賦值mHandler

private final Handler mHandler;
public AsyncTask(@Nullable Looper callbackLooper) {mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler(): new Handler(callbackLooper);...}

(1)如果callbackLooper == null,就getMainHandler()

private static Handler getMainHandler() {synchronized (AsyncTask.class) {if (sHandler == null) {sHandler = new InternalHandler(Looper.getMainLooper());}return sHandler;}
}

(2)如果callbackLooper == Looper.getMainLooper(),就new Handler(callbackLooper)

public Handler(@NonNull Looper looper) {this(looper, null, false);
}

Handler對象收到MESSAGE_POST_RESULT這個消息后會調用AsyncTask的finish方法。

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

如果AsyncTask被取消執行了,那么就調用onCancelled方法,否則就會調用onPostExecute方法,就可以看到doInBackground的返回結果會傳遞給onPostExecute方法,到這里AsyncTask的整個工作過程就分析完畢了。


總結

通過本篇博客,我們了解了AsyncTask的工作原理和如何在Android應用程序中使用它來進行異步操作。AsyncTask提供了一種簡單而強大的方式來管理異步任務,并在UI線程中更新UI,是Android開發中不可或缺的工具之一。希望本篇博客能幫助你更好地理解和使用AsyncTask。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160941.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160941.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160941.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于Qt的UDP通信、TCP文件傳輸程序的設計與實現——QQ聊天群聊

&#x1f64c;秋名山碼民的主頁 &#x1f602;oi退役選手&#xff0c;Java、大數據、單片機、IoT均有所涉獵&#xff0c;熱愛技術&#xff0c;技術無罪 &#x1f389;歡迎關注&#x1f50e;點贊&#x1f44d;收藏??留言&#x1f4dd; 獲取源碼&#xff0c;添加WX 目錄 前言一…

PostgreSQL序列,怎么才能第二天重新從1開始計數

--確定日期最大值和每天序列號最大值 with cte as(select (((1::bigint)<<32)-1) as max_date_second,(((1::bigint)<<31)-1) as max_sn )select max_date_second,to_timestamp(max_date_second),max_sn,((max_date_second<<31)|max_sn) as max_val,(((max_d…

Selenium 元素不能定位總結

目錄 元素不能定位總結: 1、定位語法錯誤&#xff1a; 定位語法錯誤&#xff0c;如無效的xpath&#xff0c;css selector,dom路徑錯誤&#xff0c;動態dom 定位語法錯誤&#xff0c;動態路徑&#xff08;動態變化&#xff09; 定位策略錯誤&#xff0c;如dom沒有id用id定位…

研發探索:導購APP、查券返利機器人與淘客系統,全面對比與選擇

研發探究&#xff1a;導購APP、查券返利機器人與淘客系統&#xff0c;全面對比與選擇 在互聯網購物的時代&#xff0c;導購APP、淘客機器人和微賺淘客系統成為了消費者們的三大好幫手。它們各具優勢&#xff0c;但也存在一些不足。本文將為您詳細對比這三種工具&#xff0c;幫…

vue history路徑編碼

記錄今天遇到的一個問題&#xff1a; 問題現狀 有一個需要前端偽造302進行重定向的需求&#xff0c;我們需要將這樣的一個路徑&#xff1a;http://xxx.com/system-name/#/index&#xff0c;拼接在跳轉地址的后面&#xff0c;進行重定向。拼接的方式是這樣的&#xff1a; htt…

攻防世界-web-Confusion1

1. 題目描述 打開鏈接&#xff0c;如圖 點擊Login和Rigister&#xff0c;都報錯 但是有提示 指出了flag所在的位置&#xff0c;題目中直接能獲取到的信息暫時就這么些了 2. 思路分析 既然告訴了我們flag文件的位置&#xff0c;那么要讀取到這個文件&#xff0c;要么是任意文…

AI輔助帶貨直播場景源碼系統 附帶網站的搭建教程

互聯網技術的發展和普及&#xff0c;直播帶貨行業迅速崛起。然而&#xff0c;直播帶貨在帶來商機的同時&#xff0c;也面臨著諸多挑戰。如直播內容缺乏新意、轉化率低等問題。針對這些問題&#xff0c;AI輔助帶貨直播場景源碼系統應運而生&#xff0c;旨在利用人工智能技術&…

【高級滲透篇】網絡安全面試

【高級滲透篇】網絡安全面試 1.權限維持2.代碼安全Python語法相關 1.權限維持 Linux權限維持方法論 Windows權限維持方法論 2.代碼安全 Python 語法相關 1、Python的值類型和引用類型是哪些 Python 中的值類型包括&#xff1a; 數字類型&#xff08;如整數、浮點數、復數…

對接蘋果支付退款退單接口

前言 一般而言&#xff0c;我們其實很少對接退款接口&#xff0c;因為退款基本都是商家自己決定后進行操作的&#xff0c;但是蘋果比較特殊&#xff0c;用戶可以直接向蘋果發起退款請求&#xff0c;蘋果覺得合理會退給用戶&#xff0c;但是目前公司業務還是需要對接這個接口&am…

試試MyBatis-Plus可視化代碼生成器,太香了,你一定會感謝我

前言 在基于Mybatis的開發模式中&#xff0c;很多開發者還會選擇Mybatis-Plus來輔助功能開發&#xff0c;以此提高開發的效率。雖然Mybatis也有代碼生成的工具&#xff0c;但Mybatis-Plus由于在Mybatis基礎上做了一些調整&#xff0c;因此&#xff0c;常規的生成工具生成的代碼…

PC端使子組件的彈框關閉

子組件 <template><el-dialog title"新增部門" :visible"showDialog" close"close"> </el-dialog> </template> <script> export default {props: {showDialog: {type: Boolean,default: false,},},data() {retu…

【JavaSE】-5-嵌套循環

回顧 一、java語言特點 二、配置java環境 path 三、記事本 javac -d . java 包名.類名 四、eclipse 五、變量 定義變量 數據類型 變量名值; 六、相關的數據類型 ? 基本&#xff08;四類 、8種&#xff09;、引用 ? 類型轉換&#xff08;自動、強制&#xff09; ? 運…

Java面向對象(高級)-- 類中屬性賦值的位置及過程

文章目錄 一、賦值順序&#xff08;1&#xff09;賦值的位置及順序&#xff08;2&#xff09;舉例&#xff08;3&#xff09;字節碼文件&#xff08;4&#xff09;進一步探索&#xff08;5&#xff09;最終賦值順序&#xff08;6&#xff09;實際開發如何選 二、(超綱)關于字節…

1992-2021年省市縣經過矯正的夜間燈光數據(GNLD、VIIRS)

1992-2021年省市縣經過矯正的夜間燈光數據&#xff08;GNLD、VIIRS&#xff09; 1、時間&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年為年度數據&#xff0c;2013-2021年3月為月度數據 2、來源&#xff1a;DMSP、VIIRS 3、范圍&#xff1a;分區域匯總&#xff1a…

SpringBoot : ch05 整合Mybatis

前言 隨著Java Web應用程序的快速發展&#xff0c;開發人員需要越來越多地關注如何高效地構建可靠的應用程序。Spring Boot作為一種快速開發框架&#xff0c;旨在簡化基于Spring的應用程序的初始搭建和開發過程。而MyBatis作為一種優秀的持久層框架&#xff0c;提供了對數據庫…

【Linux】-進程間通信-共享內存(SystemV),詳解接口函數以及原理(使用管道處理同步互斥機制)

&#x1f496;作者&#xff1a;小樹苗渴望變成參天大樹&#x1f388; &#x1f389;作者宣言&#xff1a;認真寫好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee? &#x1f49e;作者專欄&#xff1a;C語言,數據結構初階,Linux,C 動態規劃算法&#x1f384; 如 果 你 …

中低壓MOSFET 2N7002T 60V 300mA 雙N通道 采用SOT-523封裝形式

2N7002KW小電流雙N通道MOSFET&#xff0c;電壓60V電流300mA&#xff0c;采用SOT-523封裝形式。低Ros (on)的高密度單元設計&#xff0c;堅固可靠&#xff0c;具有高飽和電流能力&#xff0c;ESD防護門HBM2KV。可應用于直流/直流轉換器&#xff0c;電池開關等產品應用上。

Redis JDBC

1、導入依賴&#xff1a; <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.3</version></dependency> 2、連接JDBC public class JedisDemo05 {public static void main(String[]…

成為AI產品經理——AI產品經理工作全流程

一、業務背景 背景&#xff1a;日常排球訓練&#xff0c;中考排球項目和排球體測項目耗費大量人力成本和時間成本。 目標&#xff1a;開發一套用于實時檢測排球運動并進行排球墊球計數和姿勢分析的軟件。 二、產品工作流程 我們這里對于產品工作流程的關鍵部分進行講解&…

「Docker」如何在蘋果電腦上構建簡單的Go云原生程序「MacOS」

介紹 使用Docker開發Golang云原生應用程序&#xff0c;使用Golang服務和Redis服務 注&#xff1a;寫得很詳細 為方便我的朋友可以看懂 環境部署 確保已經安裝Go、docker等基礎配置 官網下載鏈接直達&#xff1a;Docker官網下載 Go官網下載 操作步驟 第一步 創建一個…