在 AsyncTask 提到了基本的用途,這篇紀錄關於 get() 的內容和實驗結果。
AsyncTask 的 get() 方法在 google develop 上的描述如下
public final Result get () Waits if necessary for the computation to complete, and then retrieves its result. ------------------------------------------------------------------------------------------------------------------------- public final Result get (long timeout, TimeUnit unit) Waits if necessary for at most the given time for the computation to complete, and then retrieves its result.
使用 get(long timeout, TimeUnit unit) 通常是用來作 timeout 的用途。
有幾個的重點如下:
1. get() 會 block UI thread,無論使用哪種方式都不可避免,因此要避免使用在 UI thread 上 。
public class TimeOutActivity extends Activity implements OnClickListener, IActivity { public static final String TAG = TimeOutActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); runTestTask(); } private void runTestTask() { TestTask task = new TestTask(); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); try { task.get(3, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } class TestTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { for(int i=0; i<10; ++i){ Log.d(TAG,"i:"+i); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } }
第18行呼叫 task.get(3, TimeUnit.SECONDS) 會讓 UI thread block 3秒,
無論第16行使用 AsyncTask.THREAD_POOL_EXECUTOR 或是 AsyncTask.SERIAL_EXECUTOR 都會讓 UI Thread block。
2. get() 作為 Timeout 用途可以用在非 UI thread 上,但要小心執行 AsyncTask 的參數選擇
(AsyncTask.THREAD_POOL_EXECUTOR or AsyncTask.SERIAL_EXECUTOR)
public class TimeOutActivity extends Activity implements OnClickListener, IActivity { public static final String TAG = TimeOutActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); executeCountTimeTask(); } class TestTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { executeCountTimeTask(); return null; } private void executeCountTimeTask() { SimpleTask simpleTask = new SimpleTask(); simpleTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); try { simpleTask.get(3, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } } class SimpleTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { for (int i = 0; i < 10; ++i) { Log.d(TAG, "i:" + i); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } } }
第14和第39行分別不同的 AsyncTask,第14行的 TestTask為外層的 Task,其 executeCountTimeTask() 會再建立另一個 task(SimpleTask)
因為第26行的參數為 AsyncTask.THREAD_POOL_EXECUTOR,代表 Task 上的任務是可以被並行執行。
也就是說雖然第28行呼叫了 get() 方法,但因為 Task 任務是並行的,因此 get() 並不會 block 第26行的動作。
LogCat如下
i:0 i:1 i:2 java.util.concurrent.TimeoutException i:3 i:4 i:5 i:6 i:7 i:8 i:9
可以看到 get() 和 executeOnExecutor() 並不會互相干擾,TimeException 的訊息也會順利發出。
另外若是executeOnExecutor的動作在 Timeout 的時間(3 seconds)內完成,TimeException不會發出。
若是將第26行的參數改為 AsyncTask.SERIAL_EXECUTOR,代表 Task 上的任務是循序執行。
simpleTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
也就是說 get() 會先執行,等到執行完畢才會進行 executeOnExecutor() 的動作。
LogCat如下
//wait 3 seconds java.util.concurrent.TimeoutException i:0 i:1 i:2 i:3 i:4 i:5 i:6 i:7 i:8 i:9
等待3秒後才會丟出 TimeoutException ,接著才會執行 executeOnExecutor()
其實使用這種 double asynctask 的作法未竟完善,
1. new AsyncTask() 的動作建議要在 UI thread 上,但 SimpleTask 的建構式卻是在 TestTask中建立。
3. 若要做 Timeout 的機制,其實可以在 SimpleTask 的 onPreExecute() 建立另一個計算時間的機制(CountDownTimer),如下
class SimpleTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { for (int i = 0; i < 10; ++i) { Log.d(TAG, "i:" + i); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onPreExecute() { super.onPreExecute(); new CountDownTimer(3000, 1000) { @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { Log.d(TAG,"timeout !!!"); } }; } }
一樣可以達到 Timeout 作用。