Android异步加载AsyncTask详解

来源:互联网 发布:测手速软件 编辑:程序博客网 时间:2024/05/20 02:23

在单线程模型中始终要记住两条法则:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包


首先来说说AsyncTask重写的4个方法:

(1)doInBackground()  //运行在后台线程中

(2)onPreExecute()  //运行在UI线程中

(3)onProgressUpdate()  //运行在UI线程中

(4)onPostExecute()  //运行在UI线程中

 详细的这几个方法怎么使用,具体不清楚的可以谷歌一下,好多同仁讲的好详细的;


为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground 接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。


     以上四点是我从网摘过来的,我觉得说的有道理,针对第4点,我有异议:即使多次调用,也不应该出现异常,因为AsyncTask类有对外公开的接口,cancel(true),isCancelled()。这两个方法,这两个方法配合使用就来控制AsyncTask可以手动退出。具体可以参照AsyncTask.java这个源码类中有例子的。

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. AsyncTask must be subclassed to be used. The subclass will override at least  
  2.  * one method ({@link #doInBackground}), and most often will override a  
  3.  * second one ({@link #onPostExecute}.)</p>  
  4.  *  
  5.  * <p>Here is an example of subclassing:</p>  
  6.  * <pre class="prettyprint">  
  7.  * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {  
  8.  *     protected Long doInBackground(URL... urls) {  
  9.  *         int count = urls.length;  
  10.  *         long totalSize = 0;  
  11.  *         for (int i = 0; i < count; i++) {  
  12.  *             totalSize += Downloader.downloadFile(urls[i]);  
  13.  *             publishProgress((int) ((i / (float) count) * 100));  
  14.  *             // Escape early if cancel() is called  
  15.  *             if (isCancelled()) break;  
  16.  *         }  
  17.  *         return totalSize;  
  18.  *     }  
  19.  *  
  20.  *     protected void onProgressUpdate(Integer... progress) {  
  21.  *         setProgressPercent(progress[0]);  
  22.  *     }  
  23.  *  
  24.  *     protected void onPostExecute(Long result) {  
  25.  *         showDialog("Downloaded " + result + " bytes");  
  26.  *     }  
  27.  * }  
这个是AsyncTask中给出的demo,看到
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. if (isCancelled()) break;  

这个就是用来判断是否退出后台线程,如果设置了cancel(true),就break跳出循环。在这个地方多说2点:

(1)终止线程最好不要用打断线程来做,这样的方式太粗暴了,而且不能保证代码的完整性,最好的处理方式就是在for循环,while循环中加入自己的判断标志位,就像AsyncTask这种方法来处理是最好的,这也是谷歌来指导我们怎么来处理终止线程的办法。

(2)也有同学感到疑惑,说我的代码就没有循环,怎么来加标志位,其实这个一般来说后台处理,大部分都是处理循环的逻辑,很少说一行代码或者十几行代码很耗时的,(当然网络相关的另说了,还有下载相关的,这个有其他方法来解决的)。即使有的话,比如调用jni,so库,返回就是慢。那就在几个耗时的方法的后面都加上标志位的判断;

通过上述方法就可以做出完整的方案设计,就能设计,当下次再次执行AsyncTask,先判断自己是否正在运行,如果在运行,就不执行或取消任务重新执行,这个要看具体的需求是什么了;

举个栗子:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. private void stopAyncTaskRunning() {  
  2.         if (mContactsListLoader != null  
  3.                 && mContactsListLoader.getStatus() == AsyncTask.Status.RUNNING) {  
  4.             mContactsListLoader.cancel(true); //if task is still running, stop it;  
  5.         }  
  6.   
  7.     }  

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. private void getContactsList() {  
  2.         stopAyncTaskRunning();  
  3.         mContactsListLoader = new ContactsListLoader();  
  4.         mContactsListLoader.executeOnExecutor(AsyncTask.THEAD_POOL_EXECUTOR);  
  5.     }  
当然,我的这个需求是下次进来的时候,就取消上次的任务,然后重新刷新数据。另外也不要忘记在doInBackground()中的循环语句中加入
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.         protected Integer doInBackground(Object... arg0) {  
  3.               
  4.             List<Contact> contacts = new ArrayList<Contact>();  
  5.             ContentResolver cr = mContext.getContentResolver();  
  6.             Cursor c = cr.query(ContactsContract.Contacts.CONTENT_URI,  
  7.                     PROJECTION_CONTACT, nullnullnull);  
  8.             if (c != null) {  
  9.                 c.moveToPosition(-1);  
  10.                 while (c.moveToNext()) {  
  11.                     if (isCancelled()) {  
  12.                         break;  
  13.                     }  
  14. 。。。 。。。  
  15. }  
这样,逻辑按照需求来写,需求是什么样子的,逻辑就相应的怎么处理;


    再来说说AsyncTask坑人的地方,就是在Android3.0以后的版本,AsyncTask的执行方法分为2个了:

    (1)execute()
    (2)executeOnExecutor()

    如果用AsyncTask调用(1)的时候,就表示串行执行线程,如果这个Activity中有4个fragment,而且每个fragment都有一个AsyncTask,这样的话用(1)的话,就必须顺序执行,等一个执行完,第二个才执行。如果用方法(2),则可以串行执行,这个UI效果就很好了。线程池可以用系统的,也可以用我们自定义的线程池;

另外对系统默认线程池中执行线程数的一些说明,如下:

下面的5代表corePoolSize,10代表阻塞队列的长度,128代表maximumPoolSize
1:如果线程池的数量小于5,则创建新的线程并执行
2:如果线程数大于5且小于5+10(阻塞队列大小),则将第6~15的线程加入阻塞队列,待线程池中的5个正在运行的线程有某个结束后,取出阻塞队列的线程执行。
3:如果线程数为16~128,则运行的线程数为num-10
4:如果线程数大于128,则舍弃。

详细的的介绍可以参考博客 Android实战技巧:深入解析AsyncTask这篇讲解的很详细;

   

   最后要说明的就是数据要加载一部分就刷新UI,给用户一个好的用户体验;举个栗子,

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. private static final int DISPLAY_NUM = 10;  
  2.     private List<Contact> mContacts = new ArrayList<Contact>();  
  3.       
  4.     private class ContactsListLoader extends  
  5.             AsyncTask<Object, Integer, Integer> {  
  6.   
  7.         int count = 0;  
  8.         List<Contact> mTempContacts = new ArrayList<Contact>(DISPLAY_NUM);  
  9.   
  10.         @Override  
  11.         protected void onPreExecute() {  
  12.             super.onPreExecute();  
  13.             mTempContacts.clear();  
  14.             mContacts.clear();  
  15.         }  
  16.   
  17.         @Override  
  18.         protected Integer doInBackground(Object... arg0) {  
  19.   
  20.             List<Contact> contacts = new ArrayList<Contact>();  
  21.             if (c != null) {  
  22.                 c.moveToPosition(-1);  
  23.                 while (c.moveToNext()) {  
  24.                     if (isCancelled()) {  
  25.                         break;  
  26.                     }  
  27.                     ... ...  
  28.                       
  29.                     contacts.add(contact);  
  30.                     mTempContacts.add(contact);  
  31.                     if (++count >= DISPLAY_NUM) {  
  32.                         publishProgress();  
  33.                         count = 0;  
  34.                     }  
  35.                 }  
  36.             }  
  37.             return contacts.size();  
  38.         }  
  39.   
  40.         @Override  
  41.         protected void onProgressUpdate(Integer... values) {  
  42.             super.onProgressUpdate(values);  
  43.             mContacts.addAll(mTempContacts);  
  44.             mTempContacts.clear();  
  45.             mAdapter.notifyDataSetChanged();  
  46.         }  
  47.   
  48.         @Override  
  49.         protected void onPostExecute(Integer size) {  
  50.             if (isCancelled())  
  51.                 return;  
  52.             if (size > 0) {  
  53.                 if (mTempContacts.size() > 0  
  54.                         && mTempContacts.size() != DISPLAY_NUM) {  
  55.                     mContacts.addAll(mTempContacts);  
  56.                 }  
  57.             } else {  
  58.                 if (mTempContacts.size() > 0) {  
  59.                     mContacts.addAll(mTempContacts);  
  60.                 }  
  61.             }  
  62.             mAdapter.notifyDataSetChanged();  
  63.         }  
  64.   
  65.     }  
0 0
原创粉丝点击