您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么構建AsyncTask”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么構建AsyncTask”吧!
Android 單線程模型,多線程的操作系統
耗時操作放在非主線程中運行
子線程中更新UI
封裝簡化異步操作
AsyncTask
doInBackground(): 必須重寫,異步執行后臺線程將要完成的任務
onPreExecute(): 執行后臺線程前被調用,通常用來做一些初始化操作
onPostExecute(): 當doInBackground() 方法完成后系統會自動調 用,并將doInBackground() 方法的返回值作為參數春遞給onPostExecute()方法
onProgressUpdate(): 在doBackground() 方法中調用publishProgress()方法更新任務的執行進度后,就會調用該方法
接下來我們寫個程序測試一下這些方法的執行順序
首先創建一個AsyncTask的子類 MyAsyncTask
public class MyAsyncTask extends AsyncTask<Void, Void, Void>{ String LOGCAT = "LOGCAT"; @Override protected Void doInBackground(Void... params) { Log.d(LOGCAT, "doInBackground------------"); System.out.println("doInBackground------------"); return null; } @Override protected void onPreExecute() { super.onPreExecute(); Log.d(LOGCAT, "onPreExecute"); } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); Log.d(LOGCAT, "onPostExecute"); } @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); Log.d(LOGCAT, "onProgressUpdate"); } }
在 MainActivity 中進行測試
public class MainActivity extends Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyAsyncTask asyncTask = new
在模擬器上部署運行之后,查看Logcat 可以看到下面的日志
從日志中可以看到,幾個方法的執行順序依次為 : onPreExecute –>doInBackground –>onPostExecute
然后我們在doInBackground 方法中添加這句代碼 publishProgress();
@Override protected Void doInBackground(Void... params) { Log.d(LOGCAT, "doInBackground------------"); //調用該方法后,會執行 onPostExecute() 方法 publishProgress(); return null; }
再次運行,觀察logcat 輸出,可看到在 doInBackground() 方法中執行了 publishProgress()方法后會調用 onProgressUpdate() 方法,顧名思義就是更新進度條的方法
下面我們來看一個典型的異步操作的例子,網絡操作,從 Android4.0 之后,網絡操作就嚴禁被放入到主線程中執行.下面是一個采用在異步線程處理下載圖像
在UI線程設置圖像的例子
布局界面代碼比較簡單,如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" <ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" <ProgressBar android:id="@+id/pb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="gone" <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:onClick="loadImage" android:text="下載圖片" </RelativeLayout>
MainActivity代碼如下
public class MainActivity extends Activity private ImageView image;// 要展示的圖片 private ProgressBar pb;// 進度條 // 要加載的圖片的url String imageUrl = "https://www.baidu.com/img/2016_10_09logo_61d59f1e74db0be41ffe1d31fb8edef3.png"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); image = (ImageView) findViewById(R.id.iv); pb = (ProgressBar) findViewById(R.id.pb); } // 下載按鈕的點擊事件 public void loadImage(View view) { AsyncTaskTest asyncTaskTest = new AsyncTaskTest(); // execute()方法接受一個可變長數組的參數,可在 doInBackground()方法中獲取 asyncTaskTest.execute(imageUrl); } class AsyncTaskTest extends AsyncTask<String, Void, Bitmap> { Bitmap bitmap; // 下載開始前的一些初始化操作 @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); pb.setVisibility(View.VISIBLE);// 在下載之前將 Progress 顯示出來 } // 在此方法中進行網絡耗時操作,下載完成后會執行 onPostExecute 方法,并把返回值傳遞給它 @Override protected Bitmap doInBackground(String... params) { // 獲取傳遞進來的參數 String url = params[0]; Bitmap btm = null; URLConnection connection; InputStream is; try { connection = new URL(url).openConnection(); is = connection.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); // 通過 BitmapFactory.decodeStream 方法吧輸入流轉換為 bitmap 對象 bitmap = BitmapFactory.decodeStream(bis); is.close(); bis.close(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 為了看清楚進度條,人為加一個延時操作,便于觀察 try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return bitmap; } // doInBackground()方法執行完畢后會自動調用此方法, 此方法的參數是 doInBackground() 方法的返回值. @Override protected void onPostExecute(Bitmap result) { // TODO Auto-generated method stub super.onPostExecute(result); pb.setVisibility(View.GONE);// 隱藏進度條 image.setImageBitmap(result);// 顯示下載的網絡圖片
上面代碼注釋很詳細,不再多做解釋,只要搞懂了 AsyncTask 的幾個方法的作用于執行周期,上面的代碼很容易理解.
下面我們再通過一個模擬進度條的小例子,進一步認識AsyncTask 異步任務的用法
布局界面很簡單,如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" <ProgressBar android:id="@+id/pb" android:padding="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_centerVertical="true" <TextView android:id="@+id/tv_show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/pb" android:layout_centerHorizontal="true" android:text="0%" android:textAppearance="?android:attr/textAppearanceSmall" </RelativeLayout>
Activity 代碼也很簡單
public class progressBarTest extends Activity private ProgressBar pb; private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pro); pb = (ProgressBar) findViewById(R.id.pb); tv = (TextView) findViewById(R.id.tv_show); MyAsyncTask myAsyncTask = new MyAsyncTask(); myAsyncTask.execute(); } class MyAsyncTask extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... params) { //模擬進度的更新 for (int i = 0; i <= 100; i++) { // 更新進度條,重寫 onProgressUpdate()方法,參數為 publishProgress(i)的參數 publishProgress(i);// 此方法傳入的參數就是 AsyncTask<Void, Integer, // Void>的第二個指定的參數類型 // 睡眠200毫秒 try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); // 更新顯示數據 tv.setText(values[0] + "%"); // 更新進度條 pb.setProgress(values[0]);// 水平進度條的進度為百分制
是不是很簡單,但是不要高興太早,對于這個程序,當我們點擊下載,然后點擊返回,然后再點擊下載,進度條居然等了好久才開始更新
這是為啥呢?
其實AsyncTask 底層是通過線程池進行作用的,當一個線程沒有作用完畢的時候,其它線程即必須進入線程池進行等待,等到前面的線程完事后,才會輪到自己執行,所以,當我們返回再次進入的時候,因為前一個線程正在執行更新進度條操作,所以當前線程必須等待前一個AsyncTask執行完畢后自己才可以執行.
那么如何解決這個問題呢?
其實很簡單,AsyncTask 框架已經為我們考慮到了這個問題,我們可以通過 cancel() 方法來取消掉一個AsyncTask開啟的一個異步任務.此方法接受一個布爾值的參數,
我們要做的很簡單,重寫Activity的 onPause() 方法,把AsyncTask的聲明周期和Activity綁定到一起. 并且在 doInBackground() 方法中做異步判斷.代碼如下
@Override protected Void doInBackground(Void... params) { // 模擬進度的更新 for (int i = 0; i <= 100; i++) { // 當收到取消請求時,不要在更新進度條,直接break結束for循環 if (isCancelled()) { break; } // 更新進度條,重寫 onProgressUpdate()方法,參數為 publishProgress(i)的參數 publishProgress(i);// 此方法傳入的參數就是 AsyncTask<Void, Integer, // Void>的第二個指定的參數類型 // 睡眠200毫秒 try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; }
并且在 onProgressUpdate () 方法中也做同樣處理
@Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); // 當收到取消請求時,不要在更新進度條,直接return結束 if (isCancelled()) { return; } // 更新顯示數據 tv.setText(values[0] + "%"); // 更新進度條 pb.setProgress(values[0]);// 水平進度條的進度為百分制
好了一切都做完了,我們再次運行程序可以看到
這里有一個注意事項,是關于AsyncTask 的 cancel(true);方法.其實當我們調用了 AsyncTask的cancel(true)方法時,并不會中斷當前的線程,有人對此做出的解釋是
AsyncTask不會不考慮結果而直接結束一個線程。調用cancel()其實是給AsyncTask設置一個”canceled”狀態。這取決于你去檢查AsyncTask是否已經取消,之后決定是否終止你的操作。對于mayInterruptIfRunning——它所作的只是向運行中的線程發出interrupt()調用。在這種情況下,你的線程是不可中斷的,也就不會終止該線程。
說的不是很清楚,我們可以查看 cancel()方法的源代碼
public boolean cancel(boolean mayInterruptIfRunning) { if (!(state == NEW && U.compareAndSwapInt(this, STATE, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // in case call to interrupt throws exception if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) t.interrupt(); } finally { // final state U.putOrderedInt(this, STATE, INTERRUPTED); } } } finally { finishCompletion(); } return true; }
可以看到,這里只是調用了該線程的 t.interrupt(); 方法.對java線程中斷機制的理解在此就顯得非常重要了:
Java的中斷是一種協作機制。也就是說調用線程對象的interrupt方法并不一定就中斷了正在運行的線程,它只是要求線程自己在合適的時機中斷自己,
所以我們要想完全停掉這個線程,最好的做法是通過isCanceled()方法.做出顯影的判斷
到此,相信大家對“怎么構建AsyncTask”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。