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

关于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



你可能感兴趣的:(Android:UI更新方法五:利用AsyncTask更新UI)