Android异步加载之AsyncTask

AsyncTask是什么?谷歌为什么要发明AsyncTask?

Android有一个原则—单线程模型的原则:UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

所以谷歌就制造AsyncTask,AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。

单线程模型中谨记:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包

简单来说:AsyncTask是线程与handler的结合体。 除了 doInBackground 之外,所有的回调都是在主线程里

AsyncTask的四个方法的运行环境:
  • doInBackground(); //运行在后台线程中
  • onPreExecute(); //运行在UI线程中
  • onProgressUpdate(); //运行在UI线程中
  • onPostExecute() //运行在UI线程中
方法的调用顺序:
  • onPreExecute 在调用doInBackground 调用
  • doInBackground 子线程调用
  • onProgressUpdate 如果doInBackground 里调用了publishProgress会在主线程里被调用
  • onPostExecute doInBackground执行完毕之后,会在主线程里被调用
示例:
static class MyAsyncTask extends AsyncTask
{
    /**
     * doInBackground 执行之前会被回调
     */
    @Override
    protected void onPreExecute() 
    {
        super.onPreExecute();
        Log.e("MyAsyncTask","onPreExecute" + Thread.currentThread().getName());
    }

    /**
     * 进度更新的回调
     * @param values
     */
    @Override
    protected void onProgressUpdate(Object[] values)
    {
        Log.e("MyAsyncTask",String.format("onProgressUpdate total=%d, progress=%d",values[0],values[1]) + Thread.currentThread().getName());
        super.onProgressUpdate(values);
    }

    /**
     * 任务执行之后
     * @param o
     */
    @Override
    protected void onPostExecute(Object o)
    {
        Log.e("MyAsyncTask","onPostExecute  " + Thread.currentThread().getName() +"     " +o);
        Log.e("MyAsyncTask","onPostExecute  " + Thread.currentThread().getName());
    }

    /**
     * 在子线程里执行
     * @param params
     * @return
     */
    @Override
    protected Object doInBackground(Object[] params) 
    {
        Log.e("MyAsyncTask","doInBackground" + Thread.currentThread().getName());
        String str = "hehe";
        int count = 10;
        while(count -- > 0)
        {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            publishProgress(10,count);
        }

        return str;
    }
}
使用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传入的参数

关于第四点,其实我个人存在异议:即使经过多次调用,也不应该会出现异常 ——>AsyncTask有多外提供公开的接口:cancel(true);isCancelled(); 这两个方法主要配合AsyncTask可以手动退出(参考AsyncTask.java源码):

AsyncTask must be subclassed to be used. The subclass will override at least  
* one method ({@link #doInBackground}), and most often will override a  
* second one ({@link #onPostExecute}.)</p>  
*  
* <p>Here is an example of subclassing:</p>  
* <pre class="prettyprint">  
* private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {  
*     protected Long doInBackground(URL... urls) {  
*         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));  
*             // Escape early if cancel() is called  
*             if (isCancelled()) break;  
*         }  
*         return totalSize;  
*     }  
*  
*     protected void onProgressUpdate(Integer... progress) {  
*         setProgressPercent(progress[0]);  
*     }  
*  
*     protected void onPostExecute(Long result) {  
*         showDialog("Downloaded " + result + " bytes");  
*     }  
* }  

这是AsyncTask给出的demo:

if (isCancelled()) break; 

这个就是判断是否退出后台线程,如果设置了cancel为true,就back跳出循环,但注意:

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

  • 但有时我们的代码并没有循环,怎么加入判断标志位?其实这个一般来说后台处理,大部分都是处理循环的逻辑,很少说一行代码或者十几行代码很耗时的,(当然网络相关的另说了,还有下载相关的,这个有其他方法来解决的)。即使有的话,比如调用jni,so库,返回就是慢。那就在几个耗时的方法的后面都加上标志位的判断;通过上述方法就可以做出完整的方案设计,就能设计,当下次再次执行AsyncTask,先判断自己是否正在运行,如果在运行,就不执行或取消任务重新执行,这个要看具体的需求是什么了

示例:
private void stopAyncTaskRunning() 
{  
    if (mContactsListLoader != null  
            && mContactsListLoader.getStatus()
            == AsyncTask.Status.RUNNING) 
    {  
        //if task is still running, stop it;  
        mContactsListLoader.cancel(true); 
    }        
}  
private void getContactsList() 
{  
    stopAyncTaskRunning();  
    mContactsListLoader = new ContactsListLoader();  
    mContactsListLoader.executeOnExecutor(AsyncTask.THEAD_POOL_EXECUTOR);  
}  

当然,我的这个需求是下次进来的时候,就取消上次的任务,然后重新刷新数据。另外也不要忘记在doInBackground()中的循环语句中加入

@Override  
protected Integer doInBackground(Object... arg0)
{  
    List<Contact> contacts = new ArrayList<Contact>();  
    ContentResolver cr = mContext.getContentResolver();  
    Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,  
    PROJECTION_CONTACT, null, null, null);  
    if (cursor != null) 
    {  
        cursor.moveToPosition(-1);  
        while (cursor.moveToNext())  
        if (isCancelled())  
        break;  
    }  
}  

这样,逻辑按照需求来写,需求是什么样子的,逻辑就相应的怎么处理


AsyncTask一点令人无语之处,在Android3.0之后的版本,AsyncTask的执行方法分成了两个:

  • execute()
  • executeOnExecutor()

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

对系统默认线程池中执行的线程数的说明:
下面的5代表corePoolSize,10代表阻塞队列的长度,128代表maximumPoolSize

  1. 如果线程池的数量小于5,则创建新的线程并执行
  2. 如果线程数大于5且小于5+10(阻塞队列大小),则将第6~15的线程加入阻塞队列,待线程池中的5个正在运行的线程有某个结束后,取出阻塞队列的线程执行。
  3. 如果线程数为16~128,则运行的线程数为num-10
  4. 如果线程数大于128,则舍弃。
数据要加载一部分就刷新UI,给用户一个好的用户体验;eg:
private static final int DISPLAY_NUM = 10;  
private List<Contact> mContacts = new ArrayList<Contact>();  

private class ContactsListLoader extends  
             AsyncTask<Object, Integer, Integer> 
{  

    int count = 0;  
    List<Contact> mTempContacts = new ArrayList<Contact>(DISPLAY_NUM);  

    @Override  
    protected void onPreExecute() 
    {  
        super.onPreExecute();  
        mTempContacts.clear();  
        mContacts.clear();  
    }  

    @Override  
    protected Integer doInBackground(Object... arg0) 
    {  
        List<Contact> contacts = new ArrayList<Contact>();  
        if (c != null) 
        {  
            c.moveToPosition(-1);  
            {
                if (isCancelled()) 
                {  
                    break;  
                }  

                contacts.add(contact);  
                mTempContacts.add(contact);  
                if (++count >= DISPLAY_NUM)
                {  
                    publishProgress();  
                    count = 0;  
                }  
            }  
        }  
        return contacts.size();  
    }  

    @Override  
    protected void onProgressUpdate(Integer... values)
    {  
        super.onProgressUpdate(values);  
        mContacts.addAll(mTempContacts);  
        mTempContacts.clear();  
        mAdapter.notifyDataSetChanged();  
    }  

    @Override  
    protected void onPostExecute(Integer size) 
    {  
        if (isCancelled())  
        return;  
        {  
            if (mTempContacts.size() > 0  
            && mTempContacts.size() != DISPLAY_NUM) 
            {  
                mContacts.addAll(mTempContacts);  
            }  
            } else 
            {  
                if (mTempContacts.size() > 0) 
                {  
                    mContacts.addAll(mTempContacts);  
                }  
            }  
            mAdapter.notifyDataSetChanged();  
        }  
    }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值