Android异步加载AsyncTask详解
来源:互联网 发布:iphone制作视频软件 编辑:程序博客网 时间:2024/05/20 01:11
曾看见有人说过,觉得很有道理,分享一下:
技术分为术和道两种:
(1)具体做事的方法是术;
(2)做事的原理和原则是道;
最近项目发现个重大问题,结果打log跟踪查是AsyncTask导致的。如果对AsyncTask了解的不够深入透彻,那写代码就是埋雷。以后不定在哪个时间爆炸。首先我们要了解,谷歌为什么发明AsyncTask,AsyncTask到底是用来解决什么问题的?Android有一个原则---单线程模型的原则:UI操作并不是线程安全的并且这些操作必须在UI线程中执行。所以谷歌就制造AsyncTask,AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。
在单线程模型中始终要记住两条法则:
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这个源码类中有例子的。
- 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 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;
if (isCancelled()) break;
这个就是用来判断是否退出后台线程,如果设置了cancel(true),就break跳出循环。在这个地方多说2点:
(1)终止线程最好不要用打断线程来做,这样的方式太粗暴了,而且不能保证代码的完整性,最好的处理方式就是在for循环,while循环中加入自己的判断标志位,就像AsyncTask这种方法来处理是最好的,这也是谷歌来指导我们怎么来处理终止线程的办法。
(2)也有同学感到疑惑,说我的代码就没有循环,怎么来加标志位,其实这个一般来说后台处理,大部分都是处理循环的逻辑,很少说一行代码或者十几行代码很耗时的,(当然网络相关的另说了,还有下载相关的,这个有其他方法来解决的)。即使有的话,比如调用jni,so库,返回就是慢。那就在几个耗时的方法的后面都加上标志位的判断;
通过上述方法就可以做出完整的方案设计,就能设计,当下次再次执行AsyncTask,先判断自己是否正在运行,如果在运行,就不执行或取消任务重新执行,这个要看具体的需求是什么了;
举个栗子:
- private void stopAyncTaskRunning() {
- if (mContactsListLoader != null
- && mContactsListLoader.getStatus() == AsyncTask.Status.RUNNING) {
- mContactsListLoader.cancel(true); //if task is still running, stop it;
- }
- }
private void stopAyncTaskRunning() {if (mContactsListLoader != null&& mContactsListLoader.getStatus() == AsyncTask.Status.RUNNING) {mContactsListLoader.cancel(true); //if task is still running, stop it;}}
- private void getContactsList() {
- stopAyncTaskRunning();
- mContactsListLoader = new ContactsListLoader();
- mContactsListLoader.executeOnExecutor(AsyncTask.THEAD_POOL_EXECUTOR);
- }
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 c = cr.query(ContactsContract.Contacts.CONTENT_URI,
- PROJECTION_CONTACT, null, null, null);
- if (c != null) {
- c.moveToPosition(-1);
- while (c.moveToNext()) {
- if (isCancelled()) {
- break;
- }
- 。。。 。。。
- }
@Overrideprotected Integer doInBackground(Object... arg0) {List<Contact> contacts = new ArrayList<Contact>();ContentResolver cr = mContext.getContentResolver();Cursor c = cr.query(ContactsContract.Contacts.CONTENT_URI,PROJECTION_CONTACT, null, null, null);if (c != null) {c.moveToPosition(-1);while (c.moveToNext()) {if (isCancelled()) {break;}。。。 。。。}这样,逻辑按照需求来写,需求是什么样子的,逻辑就相应的怎么处理;
再来说说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,给用户一个好的用户体验;举个栗子,
- 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);
- while (c.moveToNext()) {
- 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 (size > 0) {
- if (mTempContacts.size() > 0
- && mTempContacts.size() != DISPLAY_NUM) {
- mContacts.addAll(mTempContacts);
- }
- } else {
- if (mTempContacts.size() > 0) {
- mContacts.addAll(mTempContacts);
- }
- }
- mAdapter.notifyDataSetChanged();
- }
- }
private static final int DISPLAY_NUM = 10;private List<Contact> mContacts = new ArrayList<Contact>();private class ContactsListLoader extendsAsyncTask<Object, Integer, Integer> {int count = 0;List<Contact> mTempContacts = new ArrayList<Contact>(DISPLAY_NUM);@Overrideprotected void onPreExecute() {super.onPreExecute();mTempContacts.clear();mContacts.clear();}@Overrideprotected Integer doInBackground(Object... arg0) {List<Contact> contacts = new ArrayList<Contact>();if (c != null) {c.moveToPosition(-1);while (c.moveToNext()) {if (isCancelled()) {break;}... ...contacts.add(contact);mTempContacts.add(contact);if (++count >= DISPLAY_NUM) {publishProgress();count = 0;}}}return contacts.size();}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);mContacts.addAll(mTempContacts);mTempContacts.clear();mAdapter.notifyDataSetChanged();}@Overrideprotected void onPostExecute(Integer size) {if (isCancelled())return;if (size > 0) {if (mTempContacts.size() > 0&& mTempContacts.size() != DISPLAY_NUM) {mContacts.addAll(mTempContacts);}} else {if (mTempContacts.size() > 0) {mContacts.addAll(mTempContacts);}}mAdapter.notifyDataSetChanged();}}这个就是我的模型,大家看懂后,就可以套到自己的代码中去了。这是我用来动态加载刷新UI的逻辑,刷新出10个数据就刷新一次,这样就可以避免次次刷新影响效率,又能保证用户不必等到数据都加载完才能看到数据。一举两得。
下载地址:http://blog.csdn.net/wdaming1986/article/details/40828453
- Android--AsyncTask异步加载详解
- Android异步加载AsyncTask详解
- Android异步加载AsyncTask详解
- Android异步加载AsyncTask详解
- Android AsyncTask异步加载
- Android 异步加载图片-AsyncTask
- Android 异步加载图片-AsyncTask
- Android 异步加载数据 AsyncTask
- android 异步加载解决方案 AsyncTask
- Android之AsyncTask异步加载
- Android中AsyncTask异步加载
- Android异步加载之AsyncTask
- Android异步任务AsyncTask详解
- android异步类AsyncTask详解
- Android异步加载数据 AsyncTask demo application
- (Android Studio)AsyncTask异步加载实现计数器
- Android:ListView数据异步加载、Handler、AsyncTask
- android异步加载之Handler、AsyncTask(一)
- [dEw] 简单文件系统设计笔记
- 在共享DLL中使用MFC 和在静态库中使用MFC区别
- 南阳题目72-Financial Management
- 2016.3.22
- c语言:顺序表的实现(二 ) 就地逆置,有序合并,大小调整。
- Android异步加载AsyncTask详解
- VS中警告等级的含义
- hdu1228A+B
- 设计模式,行为模式(观察者模式)
- Jenkins(二)linux slave构建
- php 异常Exception
- Oracle数据库学习(四)--高级查询
- 最简单的slidingMenu
- java jdbc线程池的使用