异步加载<笔记>

什么是异步加载:

就是用异步的方式去加载数据

使用异步加载的原因

1.android是单线程模型
2.耗时操作都必须放在单独的线程中去做,而不能去阻塞UI线程

异步加载最常用的两种方式

1.多线程\线程池
2.AsyncTask(底层也是基于线程池来实现的)

实现ListView图文混排

创建一个Item_layout布局,即ListView每个项的布局

有如下总结:
textview中可以设置Maxline属性设置文字的行数,
garivity设置该view的内容相对于该view的位置,
layout_garivity属性的意思是设置该view相对于父容器的位置。

拿到Json格式的数据

慕课Json格式的API网址:

 private static final String urlPath = "http://www.imooc.com/api/teacher?type=4&num=30";

创建一个NewsBean对象来与Item_Layout文件中的数据形成映射

NewsBean.java

public class NewsBean
{
    public String newsIconUrl;
    public String newsTitle;
    public String newsContent;
}

创建一个AsyncTask来完成异步任务

1、其中第1泛型参数传入一个URL对象,第三个泛型参数返回一个元素为NewsBean的List集合(将传到BaseAdapter中为其NewsBean设值)
在doInBackground方法的返回值为

return getJsonData(params[0]);
2、在getJsonData中向readStream方法传入一个字节流对象is,返回Json格式数据的字符流数据。

源码如下:

private String readStream(InputStream is) {
            InputStreamReader isr;
            String result = "";
            try {
                isr = new InputStreamReader(is, "utf-8");
                BufferedReader br = new BufferedReader(isr);
                String line = "";
                while ((line = br.readLine()) != null) {
                    result += line;
                }

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }

3、而getJsonData方法拿到字符流后,将其转化成一个元素为NewsBean的List集合newsBeamsList。

private List<NewsBean> getJsonData(String url) {
            List<NewsBean> newsBeamsList = new ArrayList<NewsBean>();
            try {
                String jsonString = readStream(new URL(urlPath).openStream());

                JSONObject jsonObject;
                NewsBean newsBean;

                jsonObject = new JSONObject(jsonString);
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                for (int i = 0; i < jsonArray.length(); i++) {
                    jsonObject = jsonArray.getJSONObject(i);
                    newsBean = new NewsBean();
                    newsBean.newsIconUrl = jsonObject.getString("picSmall");
                    newsBean.newsTitle = jsonObject.getString("name");
                    newsBean.newsContent = jsonObject.getString("description");
                    newsBeamsList.add(newsBean);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return newsBeamsList;
        }

4、在onPostExecute方法中这个集合将作为参数传递给BaseAdapter。

图片异步加载

上一个例子中用默认图片给icon赋值:

viewHolder.icon.setImageResource(R.mipmap.ic_launcher);

解析到的JSON格式的数据传给NewsBean.newsIconUrl为图片的URL,

接下来用异步加载的方式来加载图片:

多线程的方式进行异步加载:

新建类ImageLoader.java

1、实现getBitmapFromURL方法:

public Bitmap getBitmapFromURL(String urlString) {
        Bitmap bitmap = null;
        InputStream is = null;
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            is = new BufferedInputStream(conn.getInputStream());

            bitmap = BitmapFactory.decodeStream(is);
            conn.disconnect();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return bitmap;
    }

2、showImgByThread方法


private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            if (mImageView.getTag().equals(mUrl)) {
                mImageView.setImageBitmap((Bitmap) msg.obj);
            }
        }
    };
---------------------------------------------------
public void showImgByThread(ImageView imageView, final String url) {
        mImageView = imageView;
        this.mUrl = url;

        new Thread() {
            public void run() {
                super.run();
                Bitmap bitmap = getBitmapFromURL(url);
                Message message = Message.obtain();
                message.obj = bitmap;
                mHandler.sendMessage(message);
            }
        }.start();
    }

使用AsyncTask进行异步加载:

private class NewsAynsTask extends AsyncTask<String, Void, Bitmap> {
        private ImageView mImageView;
        private String mUrl;

        public NewsAynsTask(String url) {
            mImageView = imageView;
            mUrl = url;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            Bitmap bitmap = getBitmapFromURL(url);            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if (imageView.getTag().equals(mUrl) && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }


-----------------------------------------------
//在showImgByAysncTask方法中调用如下:
new NewsAsyncTask(imageView, url).execute(url);

使用LURCache进行优化

由于在滑动的过程中要重复的从网络下载图片,还有手机网速和用户流量的因素,我们引入LURCache进行优化。

LRU算法:Least Recently Used 近期最少使用算法
Android提供了LRUCache类来实现这个缓存算法
(暂且将LRUCache认为成一个Map集合即可)

为了不让重复创建LRUCache需要在BaseAdapter中初始化一个
ImageLoader类的全局对象:private ImageLoader mImgLoader;
千万不要在getView方法中创建ImageLoader对象;

ImageLoader构造函数的修改

在ImageLoader类构造函数中初始化以下数据。还要实现get、put方法

        //获取最大可用内存
    int maxMemory = (int) Runtime.getRuntime().maxMemory();     
    int cacheSize = maxMemory / 4;
    mCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            // 在每次存入缓存时调用(value所占空间的大小)
            // 默认返回value的个数
            return value.getByteCount();
        }
    };

--------------------------------------------------------
//以get、put方法
 public void addBitmapToCache(String url, Bitmap bitmap) {
        if (getBitmapFromCache(url) == null) {
            mCache.put(url, bitmap);
        }
    }
public Bitmap getBitmapFromCache(String url) {
        return mCache.get(url);
    }

修改showImgByThread方法

......

new Thread() {
            public void run() {
                super.run();
                Message message = null;
                //---->获取cache中的值<----
                Bitmap bitmap = getBitmapFromCache(url);
                //---->判断是否需要下载<----
                if (bitmap == null) {
                    bitmap = getBitmapFromURL(url);
                }
                message = Message.obtain();
                message.obj = bitmap;
                mHandler.sendMessage(message);
            }
        }.start();
......

修改showImgByAysncTask方法

public void showImgByAysncTask(ImageView imageView, String url) {
        //---->获取cache中的值<----
        Bitmap bitmap = getBitmapFromCache(url);
        //---->判断是否需要下载<----
        if (bitmap == null) {
            new NewsAsyncTask(imageView, url).execute(url);
        } else {
            imageView.setImageBitmap(bitmap);
        }
    }

修改NewsAynsTask类的doInBackground方法

protected Bitmap doInBackground(String... params) {
            String url = params[0];
            Bitmap bitmap = getBitmapFromURL(url);
            if (bitmap != null) {
                //把图片加入缓存
                addBitmapToCache(url, bitmap);
            }
            return bitmap;
        }

滚动时的高效优化

对于一个listItem很复杂的app滑动过程中有时画面会显得很卡顿,是由于滑动过程中数据还在加载引起的

解决办法:
ListView滑动停止后才加载可见项 和
ListView滑动时,取消所有的加载项

在适配器类中的滑动监听器

在BaseAdapter构造函数中增加了一个listView参数,ImageLoader类要用

public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
    public static String[] URLS;   ...
    public NewsAdapter(Context context, List<NewsBean> data, ListView listView)
        {
            mList = data;
            mInflater = LayoutInflater.from(context);
            mImgLoader = new ImageLoader(listView);
            //将所有图片的URL存入到URLS数组中
            URLS = new String[data.size()];
            for(int i = 0; i < data.size(); i++)
            {
                URLS[i] = mList.get(i).newsIconUrl;
            }
            //为ListView设置监听器
            listView.setOnScrollListener(this);
            //设置标志位判断ListView是否第一次显示界面
            mFirstIn = true;
        }
......
--------------------------------------------------------
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        //处于停止状态
        if (scrollState == SCROLL_STATE_IDLE) {
            //加载可见项
            mImgLoader.loadImages(mStart, mEnd);
        } else {
            //停止加载
            mImgLoader.cancelAllTasks();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = firstVisibleItem + visibleItemCount;

        //首次加载时使用
        if (mFirstIn == true && visibleItemCount > 0) {
            mImgLoader.loadImages(mStart, mEnd);
            mFirstIn = false;
        }
    }
}

ImageLoader构造方法的修改

private ListView mListView;
private Set< NewsAynsTask > mtask;
//传一个ListView
public ImageLoader(ListView listView){…}

ImageLoader类方法修改

1、增加loadImages方法

public void loadImages(int start, int end) {
        for (int i = start; i < end; i++) {
            String url = NewsAdapter.URLS[i];
            Bitmap bitmap = getBitmapFromCache(url);
            if (bitmap == null) {
                NewsAynsTask task = new NewsAynsTask(url);
                task.execute(url);
                mtask.add(task);
            } else        
            {
                ImageView imageView = (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }
    }

2、修改AsyncTask类传入的值(无必要)

AsyncTask类构造函数修改如下:
public NewsAynsTask(ImageView imageView, String url)
—>public NewsAynsTask(String url)
老师讲的时候如上修改了传入的参数,我认为没有必要:
1、最主要一点,降低了NewsAynsTask类的复用性
2、在onPostExecute方法中用
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
找到需要更新的ImageView对象:
而在ImageLoader中同样可以做到。

3、修改showImgByAysncTask方法(可去掉)

public void showImgByAysncTask(ImageView imageView, String url) {
    imageView.setImageBitmap(R.mipmap.ic_launcher);
    }

由于将加载图片的权利移交给了loadImages方法,而且loadImages方
法也有从cache中去除图片的操作,故这里的showImgByAysncTask方法
只能是在ListView第一次显示并且onScroll没有加载出来的时候加载默认的图片的作用了(并且在getView方法中已经将图片设置为默认了)

4、增加cancelAllTasks方法

public void cancelAllTasks() {
        if (mtask != null) {
            for (NewsAynsTask task : mtask) {
                task.cancel(false);
            }
        }
    }

onScrollStateChanged方法中调用,用来取消线程池中的异步任务

总结

1、通过异步加载,避免阻塞UI线程
2、通过LruCache,将已下载的图片放到内存中
3、通过判断ListView的滑动状态,决定何时加载图片
4、不仅仅是ListView,任何控件都可以使用异步加载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值