• Android异步载入AsyncTask具体解释


         曾看见有人说过。认为非常有道理。分享一下:

      技术分为术和道两种:

      (1)具体做事的方法是术。

      (2)做事的原理和原则是道。


           近期项目发现个重大问题。结果打log跟踪查是AsyncTask导致的。假设对AsyncTask了解的不够深入透彻。那写代码就是埋雷。以后不定在哪个时间爆炸。首先我们要了解,谷歌为什么发明AsyncTask,AsyncTask究竟是用来解决什么问题的?Android有一个原则---单线程模型的原则:UI操作并非线程安全的并且这些操作必须在UI线程中运行

    所以谷歌就制造AsyncTaskAsyncTask扩展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中给出的demo,看到
     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 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;
    					}
    。

    。。

    。。 }

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


        再来说说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();
    		}
    
    	}
    这个就是我的模型,大家看懂后。就能够套到自己的代码中去了。这是我用来动态载入刷新UI的逻辑,刷新出10个数据就刷新一次,这样就能够避免次次刷新影响效率,又能保证用户不必等到数据都载入完才干看到数据。一举两得。

          要想了解很多其它AsyncTask,能够參考源代码。假设有人了解的更深入,欢迎童鞋拍砖留言;

  • 相关阅读:
    Kubernetes实战总结
    Kubernetes实战总结
    Kubesnetes实战总结
    Kubernetes实战总结
    【转载】Nginx、HAProxy、LVS三者的优缺点
    Kubernetes实战总结
    【解决】 Streaming server stopped unexpectedly: listen tcp: lookup localhost on 114.114.114.114:53: no such host
    Kubernetes实战总结
    (转载)常用正则表达式大全——包括校验数字、字符、一些特殊的需求
    css选择器参考手册
  • 原文地址:https://www.cnblogs.com/llguanli/p/6852824.html
Copyright © 2020-2023  润新知