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()

  1. 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个实例)。

 

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

 

 HONEYCOMB开始,任务被设计成单线程,以避免复杂的并行处理产生的问题。

解决方法:

自己使用线程池来实现(ThreadPoolExecutor ),并设定一个较大的工作队列。如果需要一个可变的队列,可以使用LinkedBlockingQueue 

 

2.      AsyncTask将导致Activity无法重建自己,即使设置onRetainNonConfigurationState参数。

如果是自己创建的线程,则可以。不过,如果自己创建线程,一旦Activity finish时就需要及时释放。

3.      无法改变后台线程的优先级,因为设计是已经写死。

4.      异常也无法很好的支持。

5.      线程池设计:5个核心线程和最大能支持128个线程,一旦超过5个线程存在,并空闲超过一定时间,空闲线程将被杀死。

  1. private static final int CORE_POOL_SIZE = 5;  
  2.    private static final int MAXIMUM_POOL_SIZE = 128;  
  3.   
  4.  public static final Executor THREAD_POOL_EXECUTOR  
  5.            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  
  6.                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  

6.      AsyncTask设计对于短时间(几秒)的后台操作表现优秀,但是如果是长时间的操作,则推荐使用java.util.concurrentAPI,如 ExecutorThreadPoolExecutor and FutureTask.

长时间的背景操作一般使用Service+Thread来实现,加上线程池,实现多线程的操作。


代码示例:

Activity_main.xml:

 

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"  
  6.     tools:context=".MainActivity" >  
  7.   
  8.     <TextView  
  9.         android:id="@+id/textView1"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="50dp"  
  12.         android:background="#ff999999"  
  13.         android:text="@string/hello_world" />  
  14.   
  15.     <Button  
  16.         android:id="@+id/button1"  
  17.         android:layout_width="wrap_content"  
  18.         android:layout_height="wrap_content"  
  19.         android:text="Button" />  
  20.   
  21. </LinearLayout>  


MainActivity.java

 

  1. package com.example.updateui;  
  2.   
  3. import android.os.AsyncTask;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.app.Activity;  
  8. import android.util.Log;  
  9. import android.view.Menu;  
  10. import android.view.View;  
  11. import android.view.Window;  
  12. import android.view.View.OnClickListener;  
  13. import android.widget.Button;  
  14. import android.widget.TextView;  
  15.   
  16. public class MainActivity extends Activity  
  17. {  
  18.     private static final String TAG = MainActivity.class.getSimpleName();  
  19.     private static final int REFRESH_ACTION = 1;  
  20.     private Button mButton;  
  21.     private TextView mTextView;  
  22.     private int mCount = 0;  
  23.   
  24.     @Override  
  25.     protected void onCreate(Bundle savedInstanceState)  
  26.     {  
  27.         super.onCreate(savedInstanceState);  
  28.   
  29.         // 在标题栏显示进度条  
  30.         requestWindowFeature(Window.FEATURE_PROGRESS);  
  31.   
  32.         setContentView(R.layout.activity_main);  
  33.   
  34.         final String[] urls = { "http://avatar.csdn.net/F/4/B/1_annkie.jpg",  
  35.                 "http://static.blog.csdn.net/images/medal/holdon_s.gif",  
  36.                 "http://avatar.csdn.net/F/4/B/1_annkie.jpg",  
  37.                 "http://static.blog.csdn.net/images/medal/holdon_s.gif",  
  38.                 "http://avatar.csdn.net/F/4/B/1_annkie.jpg",  
  39.                 "http://static.blog.csdn.net/images/medal/holdon_s.gif" };  
  40.   
  41.         mTextView = (TextView) findViewById(R.id.textView1);  
  42.         mTextView.setText("Click Button to start");  
  43.   
  44.         mButton = (Button) findViewById(R.id.button1);  
  45.         mButton.setOnClickListener(new OnClickListener()  
  46.         {  
  47.             @Override  
  48.             public void onClick(View arg0)  
  49.             {  
  50.                 // 必须在UI线程创建实例和调用execute  
  51.                 new DownloadFileTask().execute(urls);  
  52.             }  
  53.         });  
  54.     }  
  55.   
  56.     // 模拟下载代码  
  57.     static class Downloader  
  58.     {  
  59.         static long downloadFile(String url)  
  60.         {  
  61.             Log.i(TAG, "url:" + url);  
  62.             try  
  63.             {  
  64.                 Thread.sleep(1000);  
  65.             }  
  66.             catch (InterruptedException e)  
  67.             {  
  68.                 // TODO Auto-generated catch block  
  69.                 e.printStackTrace();  
  70.             }  
  71.             return url.hashCode();// 随便返回的值  
  72.         }  
  73.     }  
  74.   
  75.     private class DownloadFileTask extends AsyncTask<String, Integer, Long>  
  76.     {  
  77.         // 在UI线程执行  
  78.         @Override  
  79.         protected void onPreExecute()  
  80.         {  
  81.             // 第一个执行方法  
  82.             Log.i(TAG, "onPreExecute");  
  83.             mTextView.setText("Starting...");  
  84.             super.onPreExecute();  
  85.         }  
  86.   
  87.         // 在工作线程执行  
  88.         @Override  
  89.         protected Long doInBackground(String... urls)  
  90.         {  
  91.             // 第二个执行方法,onPreExecute()执行完后执行  
  92.             Log.i(TAG, "doInBackground");  
  93.             int count = urls.length;  
  94.             long totalSize = 0;  
  95.             for (int i = 0; i < count; i++)  
  96.             {  
  97.                 totalSize += Downloader.downloadFile(urls[i]);  
  98.                 publishProgress((int) ((i / (float) count) * 100));// 更新进度条  
  99.                 Log.i(TAG, "publishProgress");  
  100.                 // Escape early if cancel() is called  
  101.                 if (isCancelled())  
  102.                 {  
  103.                     break;  
  104.                 }  
  105.             }  
  106.   
  107.             return totalSize;  
  108.         }  
  109.   
  110.         // 在UI线程执行  
  111.         @Override  
  112.         protected void onProgressUpdate(Integer... progress)  
  113.         {  
  114.             // 这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数  
  115.             // 但是这里取到的是一个数组,所以要用progress[0]来取值  
  116.             // 第n个参数就用progress[n]来取值  
  117.   
  118.             Log.i(TAG, "onProgressUpdate");  
  119.             Log.i(TAG, "progress:" + progress[0]);  
  120.             // 刷新UI界面  
  121.             mTextView.setText("Percent:" + progress[0] + "/100");  
  122.             setProgress(progress[0] * 100);  
  123.             super.onProgressUpdate(progress);  
  124.         }  
  125.   
  126.         // 在UI线程执行  
  127.         @Override  
  128.         protected void onPostExecute(Long result)  
  129.         {  
  130.             // doInBackground返回时触发,  
  131.             // 这里的result就是上面doInBackground执行后的返回值  
  132.   
  133.             Log.i(TAG, "onPostExecute");  
  134.             Log.i(TAG, "result:" + result);  
  135.             mTextView.setText("Downloaded " + result + " bytes");  
  136.             // setProgress(10000-1);//如果不想进度条消失,可以设置9999  
  137.             setProgress(10000);// 设置为10000进度条将自动消失  
  138.             super.onPostExecute(result);  
  139.         }  
  140.     }  
  141. }  

 

Logcat:

    1. 01-12 09:06:05.661: I/MainActivity(868): onPreExecute  
    2. 01-12 09:06:05.681: I/MainActivity(868): doInBackground  
    3. 01-12 09:06:05.681: I/MainActivity(868): url:http://avatar.csdn.net/F/4/B/1_annkie.jpg  
    4. 01-12 09:06:06.726: I/MainActivity(868): publishProgress  
    5. 01-12 09:06:06.726: I/MainActivity(868): url:http://static.blog.csdn.net/images/medal/holdon_s.gif  
    6. 01-12 09:06:06.726: I/MainActivity(868): onProgressUpdate  
    7. 01-12 09:06:06.731: I/MainActivity(868): progress:0  
    8. 01-12 09:06:07.737: I/MainActivity(868): publishProgress  
    9. 01-12 09:06:07.737: I/MainActivity(868): url:http://avatar.csdn.net/F/4/B/1_annkie.jpg  
    10. 01-12 09:06:07.737: I/MainActivity(868): onProgressUpdate  
    11. 01-12 09:06:07.737: I/MainActivity(868): progress:16  
    12. 01-12 09:06:08.792: I/MainActivity(868): publishProgress  
    13. 01-12 09:06:08.792: I/MainActivity(868): url:http://static.blog.csdn.net/images/medal/holdon_s.gif  
    14. 01-12 09:06:08.792: I/MainActivity(868): onProgressUpdate  
    15. 01-12 09:06:08.792: I/MainActivity(868): progress:33  
    16. 01-12 09:06:09.795: I/MainActivity(868): publishProgress  
    17. 01-12 09:06:09.795: I/MainActivity(868): url:http://avatar.csdn.net/F/4/B/1_annkie.jpg  
    18. 01-12 09:06:09.795: I/MainActivity(868): onProgressUpdate  
    19. 01-12 09:06:09.795: I/MainActivity(868): progress:50  
    20. 01-12 09:06:10.860: I/MainActivity(868): onProgressUpdate  
    21. 01-12 09:06:10.860: I/MainActivity(868): progress:66  
    22. 01-12 09:06:10.881: I/MainActivity(868): publishProgress  
    23. 01-12 09:06:10.881: I/MainActivity(868): url:http://static.blog.csdn.net/images/medal/holdon_s.gif  
    24. 01-12 09:06:11.924: I/MainActivity(868): onProgressUpdate  
    25. 01-12 09:06:11.924: I/MainActivity(868): progress:83  
    26. 01-12 09:06:11.943: I/MainActivity(868): publishProgress  
    27. 01-12 09:06:11.952: I/MainActivity(868): onPostExecute  
    28. 01-12 09:06:11.952: I/MainActivity(868): result:8475395646 
posted @ 2016-11-19 15:16  天涯海角路  阅读(91)  评论(0)    收藏  举报