Android:AsyncTask的二次使用

  • 通过这个Demo的练习,我对AsyncTask不再像之前那样陌生了。而且通过这个Demo,我对ListView/GridView去加载多图片这种需求有了更好的方式,也就是接下来这个Demo想要展示的内容。
  • 之前对List去加载多图片这样的需求,要么是子线程去搞一下,要么直接在主线程搞。当然,在主线程搞的前提是,是加载本地图片,不是网络图片。没有去使用什么Lrucache,也没有使用滑动时停止加载,不滑动时进行数据加载的策略。糟糕的用户体验,糟糕应用。不得不说,不去看别人的优秀代码,永远不知道自己的代码写的有多烂。
  • 这次,同样是根据慕课网的讲解,来写的一个Demo。内容上,可能会有一点点小的偏差。但是不影响整体效果。
  • ok,不多说,还是上代码吧。
  • 这个Demo只有一个页面,这个页面就是一页新闻的页面。里面显示的就是一条条的新闻。每条新闻有一个标题,和一个内容描述,以及一个小图片。
    • 对于新闻内容的获取,使用了一个AsyncTask去加载。然后先给每个条目只显示一张默认图片+新闻标题和内容。这样,也算是基本完成了新闻页面的展示。只是每个新闻的图标没有去展示。
    • 然后是对新闻的每个条目的图片的获取,还是通过AsyncTask 去获取的。不过和对新闻内容的获取的区别是:新闻内容是一次性获取下来,也就是说,对于新闻内容的获取,只需要调用一次AsyncTaskexecute 方法;但是对于新闻的每个条目的图片的获取,需要多次去执行AsyncTaskexecute 方法,每次执行,获取一个条目的图片。
    • 当然,对于新闻条目的图片,如果仅仅是这样的多次获取,还是不够好的,因为这样以来,每次去滑动,都会去加载屏幕上显示的条目的图片。而且,已经加载过的图片,仍然会去加载,这样就很浪费流量了。于是,就做了一个缓存策略。通过Lrucache ,去缓存加载过的图片,然后,在去网络加载图片之前,先看看缓存中有没有该图片,如果有了,就不去加载了,以提高用户体验。
    • 但是这样还是不够的。因为当ListView的布局比较复杂,需要显示的内容比较多的时候,当用户去滑动屏幕,而这时候,如果有图片加载完成,会去显示到屏幕上,就是这样的一个过程,可能会导致屏幕的卡顿。这样也不是好的用户体验。于是,就做了一个滑动策略:也就是,当用户去滑动屏幕的时候,并不去加载图片资源,就让他看看新闻的标题和内容就好。当用户停止滑动的时候,就去加载对应的图片资源,显示正确的图片。(当然,如果是已经加载好的图片,会在滑动的同时,直接显示出来,这样也不会导致滑动的卡顿)。并且,由于第一次打开页面的时候,不会触发滑动停止的状态,于是,在第一次打开页面的时候,去主动加载一个屏幕需要显示的图片内容,后面的,就是根据滑动停止状态才去加载,并且在滑动的过程中,会去取消掉,所有的,准备加载的任务。这样,就可以在滑动的时候开心的滑,在加载的时候,安心的加载。
  • 以上,就是对这个Demo的比较详细的介绍。


  • 代码区:

  • xml布局:
    • a. Actiivty的布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MoocActivity" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</RelativeLayout>
* b `listview`的Item的布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:contentDescription="@string/action_settings"
        android:src="@drawable/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/imageview"
        android:orientation="vertical"
        android:paddingLeft="4dp" >

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:text="Title"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:maxLines="3"
            android:text="Content"
            android:textSize="12sp" />
    </LinearLayout>

</RelativeLayout>
  • java代码:

    • Activity的代码:
    package com.duck.moocAsyncTask;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.app.Activity;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.widget.ListView;
    
    public class MoocActivity extends Activity {
    
        private static final String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
        private ListView mListView;
    
        private NewsAdapter adapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_mooc);
            mListView = (ListView) findViewById(R.id.listview);
            new NewsAsyncTask().execute(URL);
        }
    
        class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {
    
            private static final boolean DEBUG = false;
    
            @Override
            protected void onPostExecute(List<NewsBean> result) {
                super.onPostExecute(result);
                adapter = new NewsAdapter(result, MoocActivity.this,mListView);
                mListView.setAdapter(adapter);
            }
    
            @Override
            protected List<NewsBean> doInBackground(String... params) {
                String url = params[0];
    
                return getDatasFromUrl(url);
            }
    
            private List<NewsBean> getDatasFromUrl(String url) {
                try {
                    URLConnection connection = new URL(url).openConnection();
                    InputStream is = connection.getInputStream();
    
                    String jsonString = getStringFromStream(is);
                    return getNewsFromJson(jsonString);
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
    
            private List<NewsBean> getNewsFromJson(String jsonString) {
                List<NewsBean> newsBeanList = new ArrayList<NewsBean>();
                try {
                    JSONObject jsonObject = new JSONObject(jsonString);
                    JSONArray jsonArray = jsonObject.getJSONArray("data");
                    for (int i = 0; i < jsonArray.length(); i++) {
                        JSONObject object = jsonArray.getJSONObject(i);
                        String url = object.getString("picSmall");
                        String title = object.getString("name");
                        String content = object.getString("description");
                        NewsBean bean = new NewsBean();
                        bean.iconUrl = url;
                        bean.title = title;
                        bean.content = content;
                        newsBeanList.add(bean);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                if (DEBUG) {
                    // 数据获取OK!
                    System.out.println("newsBeanList: " + newsBeanList);
                    System.out.println("\n \n newsBeanList.SIZE: "
                            + newsBeanList.size());
                    System.out.println("\n \n newsBeanList.3: "
                            + newsBeanList.get(3).title);
                }
                return newsBeanList;
            }
    
            private String getStringFromStream(InputStream is) {
                String json = "";
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                String line;
                try {
                    while ((line = br.readLine()) != null) {
                        json += line;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return json;
            }
    
        }
    }
    
    • Adapter代码:

      package com.duck.moocAsyncTask;
      
      import java.util.List;
      
      import android.content.Context;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.AbsListView;
      import android.widget.AbsListView.OnScrollListener;
      import android.widget.BaseAdapter;
      import android.widget.ImageView;
      import android.widget.ListView;
      import android.widget.TextView;
      
      public class NewsAdapter extends BaseAdapter implements OnScrollListener {
      
          private static boolean firstIn;
          private List<NewsBean> mList;
          private LayoutInflater mInflater;
      
          private ImageLoader loader;
          private int start;
          private int end;
          public static String[] URLS;
      
          public NewsAdapter(List<NewsBean> newsBeans, Context context,
                  ListView listView) {
              firstIn = true;
              URLS = new String[newsBeans.size()];
              for (int i = 0; i < newsBeans.size(); i++) {
                  URLS[i] = newsBeans.get(i).iconUrl;
              }
              mList = newsBeans;
              mInflater = LayoutInflater.from(context);
              loader = new ImageLoader();
              listView.setOnScrollListener(this);
          }
      
          @Override
          public int getCount() {
              return mList.size();
          }
      
          @Override
          public NewsBean getItem(int position) {
              return mList.get(position);
          }
      
          @Override
          public long getItemId(int position) {
              return position;
          }
      
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
      
              ViewHolder holder;
              if (convertView == null) {
                  holder = new ViewHolder();
                  convertView = mInflater.inflate(R.layout.item, null);
                  holder.ivIcon = (ImageView) convertView
                          .findViewById(R.id.imageview);
                  holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
                  holder.tvContent = (TextView) convertView
                          .findViewById(R.id.tv_content);
                  convertView.setTag(holder);
              } else {
                  holder = (ViewHolder) convertView.getTag();
              }
              // show data
              NewsBean bean = getItem(position);
              holder.ivIcon.setImageResource(R.drawable.ic_launcher);
              holder.ivIcon.setTag(bean.iconUrl);// 绑定tag
              // new ImageLoader(holder.ivIcon, bean.iconUrl)
              // .disPlayByThread(bean.iconUrl);
              loader.disPlayByAsyncTask(holder.ivIcon, bean.iconUrl);
              holder.tvTitle.setText(bean.title);
              holder.tvContent.setText(bean.content);
              return convertView;
          }
      
          class ViewHolder {
              ImageView ivIcon;
              TextView tvTitle;
              TextView tvContent;
          }
      
          @Override
          public void onScrollStateChanged(AbsListView view, int scrollState) {
              /**
               * <ul>
               * <li>如果当前是空闲状态,就去加载图片;</li>
               * <li>如果当前是滑动状态,就停止所有的加载任务</li>
               * </ul>
               */
              if (scrollState == SCROLL_STATE_IDLE) {
                  loader.loadImages(start, end, view);
              } else {
                  loader.cancelAllTasks();
              }
          }
      
          @Override
          public void onScroll(AbsListView view, int firstVisibleItem,
                  int visibleItemCount, int totalItemCount) {
              start = firstVisibleItem;
              end = firstVisibleItem + visibleItemCount;
              if (firstIn && visibleItemCount > 0) {
                  // TODO:visibleItemCount > 0 这个条件不能少,不然预加载没效果!!!
                  // 首次进入页面,并且界面上面的Item已经加载出来了,才去加载一个屏幕Item的的图片
                  loader.loadImages(start, end, view);
                  firstIn = false;
              }
          }
      }
      
    • Bean的代码:

      package com.duck.moocAsyncTask;
      
      public class NewsBean {
      
          public String iconUrl;
          public String title;
          public String content;
      }
      
    • ImageLoader 的代码:

    “`

    package com.duck.moocAsyncTask;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.HashSet;
    import java.util.Set;
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.AsyncTask;
    import android.os.Handler;
    import android.os.Message;
    import android.util.LruCache;
    import android.widget.AbsListView;
    import android.widget.ImageView;
    
    public class ImageLoader {
    
        private ImageView mImageView;
        private String mUrl;
    
        private LruCache<String, Bitmap> mCache;
    
        private Set<LoadImageAsyncTask> mTasks;
        public ImageLoader() {
            mTasks = new HashSet<ImageLoader.LoadImageAsyncTask>();
            long maxMemory = Runtime.getRuntime().maxMemory();
            int maxSize = (int) (maxMemory / 4);
            mCache = new LruCache<String, Bitmap>(maxSize) {
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getByteCount();
                }
            };
        }
    
        public void addBitmapToCache(String url, Bitmap bitmap) {
            mCache.put(url, bitmap);
        }
    
        public Bitmap getBitmapFromCache(String url) {
            return mCache.get(url);
        }
    
        private Handler handler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                if (mImageView.getTag().equals(mUrl)) {
                    mImageView.setImageBitmap((Bitmap) msg.obj);
                }
            }
        };
        private AbsListView mListview;
    
        protected Bitmap getBitmapFromUrl(String urlString) {
            URLConnection connection = null;
            InputStream is = null;
            try {
                connection = new URL(urlString).openConnection();
                is = connection.getInputStream();
                Bitmap bitmap = BitmapFactory.decodeStream(is);
    
                return bitmap;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
        public void disPlayByThread(ImageView imageView, final String url) {
            mImageView = imageView;
            mUrl = url;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap bitmap = getBitmapFromUrl(url);
                    Message message = Message.obtain();
                    message.obj = bitmap;
                    handler.sendMessage(message);
                }
            }).start();
        }
    
        public void disPlayByAsyncTask(ImageView imageView, String url) {
            Bitmap bitmap = getBitmapFromCache(url);
            if (bitmap != null) {
                imageView.setImageBitmap(bitmap);
            } else {
                // new LoadImageAsyncTask(imageView, url).execute(url);
                // /加载图片,不再使用该方法。该方法仅仅用来显示图片
                imageView.setImageResource(R.drawable.ic_launcher);
            }
        }
    
        class LoadImageAsyncTask extends AsyncTask<String, Void, Bitmap> {
    
            private ImageView mImageView;
            private String mUrl;
    
            // public LoadImageAsyncTask(ImageView imageView, String url) {
            // mImageView = imageView;
            // mUrl = url;
            // }
            public LoadImageAsyncTask(String url) {
                mUrl = url;
            }
    
            @Override
            protected Bitmap doInBackground(String... params) {
                String url = params[0];
                Bitmap bitmap = getBitmapFromUrl(url);
                if (getBitmapFromCache(url) == null) {
                    addBitmapToCache(url, bitmap);
                }
                return bitmap;
            }
    
            @Override
            protected void onPostExecute(Bitmap result) {
                super.onPostExecute(result);
                // if (mImageView.getTag().equals(mUrl)) {
                // mImageView.setImageBitmap(result);
                // }
    
                ImageView imageView = (ImageView) mListview.findViewWithTag(mUrl);
                if (imageView != null && result != null){
                    imageView.setImageBitmap(result);
                }
                //task执行结束了
                mTasks.remove(this);//TODO: 容易忘记的一点
            }
        }
    
        /**
         * 加载从start 到end的所有图片
         * 
         * @param start
         * @param end
         */
        public void loadImages(int start, int end, AbsListView listView) {
            this.mListview = listView;
            for (int i = start; i < end; i++) {
                String url = NewsAdapter.URLS[i];
                Bitmap bitmap = getBitmapFromCache(url);
                if (bitmap != null) {
                    ImageView imageView = (ImageView) listView.findViewWithTag(url);
                    imageView.setImageBitmap(bitmap);
                } else {
                    LoadImageAsyncTask task = new LoadImageAsyncTask(url);
                    task.execute(url);
                    mTasks.add(task);
                }
            }
        }
    
        public void cancelAllTasks() {
            if(mTasks!=null){
                for (LoadImageAsyncTask task :mTasks) {
                    task.cancel(false);
                }
            }
        }
    }
    

  • 简单介绍一个以上代码分别表达的意思:

    1. Activity就不用说了,就是显示ListView,获取数据,将数据放进Adapter,让ListView去显示获取到的网络数据。
    2. Adapter 也没什么说的,就是将传递过来的数据集合List里面的数据放到每个Item 里面去展示。不过需要注意的是:1.在getView 方法里面去调用了ImageLoader 的显示图片的方法,这个方法的作用就是将加载完成的图片显示出来,不涉及异步任务的操作。2.在listViewOnScrollListener 的重写方法中,做了两个操作:一,.在滑动的状态,取消所有的,正在执行的异步任务,在非滑动状态,去加载当前屏幕所需要显示的图片。二,在首次加载出页面上ListView的条目的时候,去加载当前屏幕的这个条目需要的图片。
    3. ImageLoader 的作用,就有点重要了。首先是里面有一个Lrucache ,每次在加载到图片的时候,都会去将图片存放到Lrucache 中;然后,ImageLoader 中有一个loadImages(int start, int end, AbsListView listView) 的方法,这个方法的左右就是去加载当前屏幕上,需要展示的所有图片。而加载图片是一个网络的操作,于是这里面的内部类AsyncTask 的作用就是去网络获取图片。然后,为了不去加载已经加载过的图片,在loadImages 里面会有一个判断,如果当前图片已经存在,就直接去显示到ListView 对应的ImageView 上面,如果,当前图片不在缓存中,就去开启异步任务去加载,顺便,将开启的任务放到Task集合 中去(为什么要搞这么一个集合呢?因为在滑动的时候,需求停止所有的加载任务,就需要使用到这个集合)。最后,在这个内部类AsyncTask 中,除了根据url去加载网络图片,将图片放入缓存之外,在onPostExecute方法中,还有去将图片显示到ListView对应的ImageView 中去。并且,去Task集合 中去移除当前的Task .
  • 大体上,关于这个Demo的内容就这些了。完整项目下载

  • 有不足之处,欢迎指正。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值