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}.)

* *

Here is an example of subclassing:

*
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 contacts = new ArrayList();      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 mContacts = new ArrayList();  private class ContactsListLoader extends               AsyncTask<Object, Integer, Integer> {      int count = 0;      List mTempContacts = new ArrayList(DISPLAY_NUM);      @Override      protected void onPreExecute()     {          super.onPreExecute();          mTempContacts.clear();          mContacts.clear();      }      @Override      protected Integer doInBackground(Object... arg0)     {          List contacts = new ArrayList();          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();          }      }  

更多相关文章

  1. Android中库项目、jar包等的使用方法
  2. 深入理解Android自定义View
  3. Android下从网络上获取图片的方法 学习笔记
  4. android集成支付宝sdk
  5. AsyncTask使用详解及源码分析
  6. Android实现圆形、圆角和椭圆自定义图片View(使用BitmapShader图
  7. Android(安卓)Studio常用配置及使用技巧(二)
  8. Android(安卓)Studio导入第三方jar包或依赖工程的方法
  9. android下关联源码的最简单的方法

随机推荐

  1. Android样式开发
  2. Android中TextView中加图片,超链接,部分字
  3. Android(安卓)单元测试之JUnit和Mockito
  4. Android(安卓)背景渐变 渐变背景 shape
  5. Android安全加密:Https编程
  6. 在Android(安卓)Studio中下载Android(安
  7. Android:解决RadioGroup中RadioButton的图
  8. Android开发EditText属性
  9. 安卓课程九 布局属性说明
  10. android aidl 进程通信 调用错误