Android:UI更新方法五:利用AsyncTask更新UI

annkie發表於2013-01-12

關於AsyncTask的用法:

主要翻譯自:http://developer.android.com/reference/android/os/AsyncTask.html

3個範型引數:

Params啟動任務執行的輸入引數

Progress後臺任務執行的百分比

Result,後臺計算的結果型別

在一個非同步任務裡,不是所有的型別總被用。假如一個型別不被使用,可以簡單地使用Void型別:

private class MyTask extends AsyncTask<Void,Void,Void> {... }

4個重寫介面:

onPreExecute

執行在UI執行緒。

初始化task,比如顯示一個進度條。

doInBackground,:

執行在後臺執行緒

主要用來進行邏輯處理。時間可以較長,可以避免在UI執行緒ANR。耗時操作在這個方法中進行。

onPreExecute後立刻執行。Execute的引數傳入到這個介面。

正常情況,計算結果將被返回給onPostExecute。如果 cancel(boolean)呼叫,則返回結果給 onCancelled(Object)

在這個步驟也可以publishProgress(Progress...)釋出進度結果到UI執行緒,由onProgressUpdate(Progress...) 來處理。

onProgressUpdate

執行在UI執行緒。

呼叫publishProgress(Progress...)之後執行。這部分主要用來與使用者互動,比如顯示進度百分比給使用者。

onPostExecute.

執行在UI執行緒。

doInBackground 執行完以後將結果返回給這個介面。

 

 

AsyncTask是個抽象類,必須子類化才能使用。

至少重寫一個方法:doInBackground(Params...),大多數時候也會重寫:onPostExecute(Result)

為了儘快知道Cancel的結果,可以在 doInBackground(Object[])中測試isCancelled()

protected abstract Result doInBackground(Params... params);//抽象方法必須實現

執行緒規則

有一些執行緒規則必須去遵守,這個類才會正確的工作:

· AsyncTask類必須在UI執行緒載入。JELLY_BEAN 中將自動完成這步。

·任務例項必須建立在 UI執行緒 。 

· execute(Params...)必須在 UI執行緒上呼叫

· 不要手動呼叫onPreExecute(),onPostExecute(Result),doInBackground(Params...),onProgressUpdate(Progress...)

· 這個任務只執行一次(如果執行第二次將會丟擲異常)

 

使用限制:

1.      多工併發時可能存在問題

由於AsyncTask被設計成最大10個工作佇列(static),如果同時需要下載超過10個小圖片,就可能導致工作佇列溢位(建立超過10個例項)。

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);

 從 HONEYCOMB開始,任務被設計成單執行緒,以避免複雜的並行處理產生的問題。

解決方法:

自己使用執行緒池來實現(ThreadPoolExecutor ),並設定一個較大的工作佇列。如果需要一個可變的佇列,可以使用LinkedBlockingQueue 

 

2.      AsyncTask將導致Activity無法重建自己,即使設定onRetainNonConfigurationState引數。

如果是自己建立的執行緒,則可以。不過,如果自己建立執行緒,一旦Activity finish時就需要及時釋放。

3.      無法改變後臺執行緒的優先順序,因為設計是已經寫死。

4.      異常也無法很好的支援。

5.      執行緒池設計:5個核心執行緒和最大能支援128個執行緒,一旦超過5個執行緒存在,並空閒超過一定時間,空閒執行緒將被殺死。

 private static final int CORE_POOL_SIZE = 5;
    private static final int MAXIMUM_POOL_SIZE = 128;

  public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

6.      AsyncTask設計對於短時間(幾秒)的後臺操作表現優秀,但是如果是長時間的操作,則推薦使用java.util.concurrentAPI,如 ExecutorThreadPoolExecutor and FutureTask.

長時間的背景操作一般使用Service+Thread來實現,加上執行緒池,實現多執行緒的操作。


程式碼示例:

Activity_main.xml:

<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:background="#ff999999"
        android:text="@string/hello_world" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>

MainActivity.java

package com.example.updateui;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity
{
	private static final String TAG = MainActivity.class.getSimpleName();
	private static final int REFRESH_ACTION = 1;
	private Button mButton;
	private TextView mTextView;
	private int mCount = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		// 在標題欄顯示進度條
		requestWindowFeature(Window.FEATURE_PROGRESS);

		setContentView(R.layout.activity_main);

		final String[] urls = { "http://avatar.csdn.net/F/4/B/1_annkie.jpg",
				"http://static.blog.csdn.net/images/medal/holdon_s.gif",
				"http://avatar.csdn.net/F/4/B/1_annkie.jpg",
				"http://static.blog.csdn.net/images/medal/holdon_s.gif",
				"http://avatar.csdn.net/F/4/B/1_annkie.jpg",
				"http://static.blog.csdn.net/images/medal/holdon_s.gif" };

		mTextView = (TextView) findViewById(R.id.textView1);
		mTextView.setText("Click Button to start");

		mButton = (Button) findViewById(R.id.button1);
		mButton.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View arg0)
			{
				// 必須在UI執行緒建立例項和呼叫execute
				new DownloadFileTask().execute(urls);
			}
		});
	}

	// 模擬下載程式碼
	static class Downloader
	{
		static long downloadFile(String url)
		{
			Log.i(TAG, "url:" + url);
			try
			{
				Thread.sleep(1000);
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return url.hashCode();// 隨便返回的值
		}
	}

	private class DownloadFileTask extends AsyncTask<String, Integer, Long>
	{
		// 在UI執行緒執行
		@Override
		protected void onPreExecute()
		{
			// 第一個執行方法
			Log.i(TAG, "onPreExecute");
			mTextView.setText("Starting...");
			super.onPreExecute();
		}

		// 在工作執行緒執行
		@Override
		protected Long doInBackground(String... urls)
		{
			// 第二個執行方法,onPreExecute()執行完後執行
			Log.i(TAG, "doInBackground");
			int count = urls.length;
			long totalSize = 0;
			for (int i = 0; i < count; i++)
			{
				totalSize += Downloader.downloadFile(urls[i]);
				publishProgress((int) ((i / (float) count) * 100));// 更新進度條
				Log.i(TAG, "publishProgress");
				// Escape early if cancel() is called
				if (isCancelled())
				{
					break;
				}
			}

			return totalSize;
		}

		// 在UI執行緒執行
		@Override
		protected void onProgressUpdate(Integer... progress)
		{
			// 這個函式在doInBackground呼叫publishProgress時觸發,雖然呼叫時只有一個引數
			// 但是這裡取到的是一個陣列,所以要用progress[0]來取值
			// 第n個引數就用progress[n]來取值

			Log.i(TAG, "onProgressUpdate");
			Log.i(TAG, "progress:" + progress[0]);
			// 重新整理UI介面
			mTextView.setText("Percent:" + progress[0] + "/100");
			setProgress(progress[0] * 100);
			super.onProgressUpdate(progress);
		}

		// 在UI執行緒執行
		@Override
		protected void onPostExecute(Long result)
		{
			// doInBackground返回時觸發,
			// 這裡的result就是上面doInBackground執行後的返回值

			Log.i(TAG, "onPostExecute");
			Log.i(TAG, "result:" + result);
			mTextView.setText("Downloaded " + result + " bytes");
			// setProgress(10000-1);//如果不想進度條消失,可以設定9999
			setProgress(10000);// 設定為10000進度條將自動消失
			super.onPostExecute(result);
		}
	}
}

Logcat:

01-12 09:06:05.661: I/MainActivity(868): onPreExecute
01-12 09:06:05.681: I/MainActivity(868): doInBackground
01-12 09:06:05.681: I/MainActivity(868): url:http://avatar.csdn.net/F/4/B/1_annkie.jpg
01-12 09:06:06.726: I/MainActivity(868): publishProgress
01-12 09:06:06.726: I/MainActivity(868): url:http://static.blog.csdn.net/images/medal/holdon_s.gif
01-12 09:06:06.726: I/MainActivity(868): onProgressUpdate
01-12 09:06:06.731: I/MainActivity(868): progress:0
01-12 09:06:07.737: I/MainActivity(868): publishProgress
01-12 09:06:07.737: I/MainActivity(868): url:http://avatar.csdn.net/F/4/B/1_annkie.jpg
01-12 09:06:07.737: I/MainActivity(868): onProgressUpdate
01-12 09:06:07.737: I/MainActivity(868): progress:16
01-12 09:06:08.792: I/MainActivity(868): publishProgress
01-12 09:06:08.792: I/MainActivity(868): url:http://static.blog.csdn.net/images/medal/holdon_s.gif
01-12 09:06:08.792: I/MainActivity(868): onProgressUpdate
01-12 09:06:08.792: I/MainActivity(868): progress:33
01-12 09:06:09.795: I/MainActivity(868): publishProgress
01-12 09:06:09.795: I/MainActivity(868): url:http://avatar.csdn.net/F/4/B/1_annkie.jpg
01-12 09:06:09.795: I/MainActivity(868): onProgressUpdate
01-12 09:06:09.795: I/MainActivity(868): progress:50
01-12 09:06:10.860: I/MainActivity(868): onProgressUpdate
01-12 09:06:10.860: I/MainActivity(868): progress:66
01-12 09:06:10.881: I/MainActivity(868): publishProgress
01-12 09:06:10.881: I/MainActivity(868): url:http://static.blog.csdn.net/images/medal/holdon_s.gif
01-12 09:06:11.924: I/MainActivity(868): onProgressUpdate
01-12 09:06:11.924: I/MainActivity(868): progress:83
01-12 09:06:11.943: I/MainActivity(868): publishProgress
01-12 09:06:11.952: I/MainActivity(868): onPostExecute
01-12 09:06:11.952: I/MainActivity(868): result:8475395646



相關文章