android异步加载

转载请说明出处,欢迎转载。http://write.blog.csdn.net/postedit/51533261

本篇博客总结了慕课网关于异步加载图片的知识要点,和大家一起分享,有感觉听得不连贯的可以来看看。

看完本篇博客,你将学习到下面的知识:

1.怎样将一个url(也可以说是一个InputStream)转换为一个json字符串信息。

2.怎样运用插件写一个我认为完美的model类来为解析json做准备。

3.AsyncTask的基本用法。

4.Gosn的基本用法。

5.Adapter的常用优化写法。

6.通过LruCache缓存已经加载的图片。

7.listview高效加载复杂item布局。

8.编写json实体类的AS小插件。

好了,直接上代码,代码里面有详细的解释。

package com.robin.loadimageinlistview;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;

import com.google.gson.Gson;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.lv_main);
        String URL1 = "http://www.imooc.com/api/teacher?type=4&num=30";
        new MyAsyncTask().execute(URL1);
    }

    class MyAsyncTask extends AsyncTask<String, Void, List<NewsBean.BeanData>> {//<strong><span style="color:#ff0000;">(知识点3)</span></strong>

        @Override
        protected List<NewsBean.BeanData> doInBackground(String... params) {
            return getJsonData(params[0]);
        }

        @Override
        protected void onPostExecute(List<NewsBean.BeanData> beanDatas) {
            super.onPostExecute(beanDatas);
            NewsAdapter adapter = new NewsAdapter(MainActivity.this, beanDatas, listView);
            listView.setAdapter(adapter);
        }
    }

    /**
     * 通过URL获取json字符串<strong><span style="color:#ff0000;">(知识点2)</span></strong>
     *
     * @param url
     * @return List<NewsBean.BeanData>
     */
    private List<NewsBean.BeanData> getJsonData(String url) {
        String jsonString = null;
        try {
            jsonString = readStream(new URL(url).openStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        Gson gson = new Gson();
        if(jsonString==null||"".equals(jsonString))return null;//如果jsonString返回有问题,就不解析了。
        NewsBean newsBean = gson.fromJson(jsonString, NewsBean.class);//<span style="color:#ff0000;"><strong>(知识点4)</strong></span>需要将Gosn的包导入进project中。怎么导,就不详细说了,去Google
        return newsBean.getData();
    }

    /**
     * 怎样将一个url(也可以说是一个InputStream)转换为一个json字符串信息。<strong><span style="color:#ff0000;">(知识点1)</span></strong>
     *
     * @param is
     * @return String
     */
    private String readStream(InputStream is) {
        String result = "";
        InputStreamReader isr;
        String line;
        try {
            isr = new InputStreamReader(is, "utf-8");//字节流转化为字符流
            BufferedReader br = new BufferedReader(isr);
            while ((line = br.readLine()) != null) {
                result += line;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}

接下来开始放Adapter (知识点5)

package com.robin.loadimageinlistview;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.List;


public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
    private List<NewsBean.BeanData> newsBeans;
    private LayoutInflater mInflater;
    private ImageLoader imageLoader;
    private int mStart,mEnd;
    public static String[] URLS;//存放要加载的图片的url
    private boolean isFirst = false;//控制第一次进入listview的时候加载数据
    public NewsAdapter(Context context, List<NewsBean.BeanData> newsBeans, ListView listView) {//初始化数据
        this.newsBeans = newsBeans;
        this.mInflater = LayoutInflater.from(context);
        imageLoader = new ImageLoader(listView);// 确保只有一个LruCache
        
        URLS = new String[newsBeans.size()];
        for (int i=0;i<newsBeans.size();i++){//获取newsBeans中的图片的url
            URLS[i] = newsBeans.get(i).getPicSmall();
            
        }
        isFirst = true;
        listView.setOnScrollListener(this);
    }

    @Override
    public int getCount() {
        return newsBeans.size();
    }

    @Override
    public Object getItem(int position) {
        return newsBeans.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;//利用viewholder进行优化,这一点也是写类似listview的adapter的getView方法的一个模板。
        if(convertView==null){
            viewHolder = new ViewHolder();
            convertView = mInflater.inflate(R.layout.item_layout,null);
            viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
            viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
            viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
            convertView.setTag(viewHolder);
        }else{//如果convertView不为空就不用再次去加载布局了,因为加载布局耗时很长,造成listview的卡顿
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);
        String url = newsBeans.get(position).getPicSmall();
        viewHolder.ivIcon.setTag(url);//将url作为tag
        imageLoader.showImageByAsyncTask(viewHolder.ivIcon,url);
        viewHolder.tvTitle.setText(newsBeans.get(position).getName());
        viewHolder.tvContent.setText(newsBeans.get(position).getDescription());

        return convertView;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {//滑动状态改变时调用<span style="color:#ff0000;"><strong>(知识点7)</strong></span>
        if(scrollState==SCROLL_STATE_IDLE){//停止滚动,加载可见项
            imageLoader.loadImage(mStart,mEnd);
        }else{//停止加载
            imageLoader.cancelAllTask();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {//整个滑动的时候都会调用
        mStart = firstVisibleItem;//第一个可见元素
        mEnd =firstVisibleItem+visibleItemCount;//最后一个可见元素=第一个可见元素+可见元素的数量
        if(isFirst && visibleItemCount > 0){//第一次加载的时候调用,显示图片
            imageLoader.loadImage(mStart,mEnd);
            isFirst=false;
        }
    }

    class ViewHolder{
        public TextView tvTitle;
        public TextView tvContent;
        public ImageView ivIcon;
    }
}
  知识点7的一点解释,当item布局非常复杂的时候,这是用户区频繁滚动listview,listview会频繁调用getView方法去获取item,导致卡顿,然而,平常的使用过程中,我们发现一般用户滚动的时候不太注意内容,停止滚动的时候才回去看内容,因此我们考虑如果将显示内容的权利从getView中移交给滚动事件处理的话,这个问题就能得到解决。

接下来上最重要的控制图片缓存以及加载的类

package com.robin.loadimageinlistview;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;


public class ImageLoader {
    
    private LruCache<String,Bitmap> cache;//用于缓存图片
    private ListView listView;
    private Set<NewsAsyncTask> mTask;//管理AsyncTask

    public ImageLoader(ListView listView) {//初始化一些数据
        this.listView = listView;
        this.mTask = new HashSet<>();
        int maxMemry = (int) Runtime.getRuntime().maxMemory();//获取当前应用可用的最大内存
        int cacheSize = maxMemry/4;//以最大的四分之一作为可用的缓存大小
        this.cache = new LruCache<String,Bitmap>(cacheSize){//初始化LruCache
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();//每次存入缓存的大小,即bitmap的大小
            }
        };
        
    }

    /**
     * 将内容保存到LruCache
     * @param url
     * @param bitmap
     */
    public void addBitmapToCache(String url,Bitmap bitmap){
        if(getBitmapFromCache(url)==null){//如果没有保存的话就保存。    
            cache.put(url,bitmap);
        }
    }

    /**
     * 从LruCache中获取bitmap
     * @param url
     * @return Bitmap
     */
    public Bitmap getBitmapFromCache(String url){
     return this.cache.get(url);   
    }


    /**
     * 将图片url转化为bitmap
     * @param urlString
     * @return Bitmap
     */
    public Bitmap getBitmapFromUrl(String urlString){
        Bitmap bitmap;
        InputStream is=null;
        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(connection.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            connection.disconnect();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(is!=null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    
    public void showImageByAsyncTask(ImageView imageView,String url){//改进之后,获取图片的控制权由原来getview改成了滚动状态。
        Bitmap bitmap = getBitmapFromCache(url);//从缓存中获取图片
        if(bitmap==null){//如果没有就设置默认的图片
            imageView.setImageResource(R.mipmap.ic_launcher);
        }else{//如果有就设置当前的图片
            imageView.setImageBitmap(bitmap);
        }
       
    }
    
    private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
        private String url;

        public NewsAsyncTask(String url) {
            this.url = url;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            Bitmap bitmap = getBitmapFromUrl(url);//获取网络图片
            if(bitmap!=null){
                addBitmapToCache(url,bitmap); //将不在缓存的图片加载的缓存中去
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView =(ImageView) listView.findViewWithTag(url);
            if(imageView!=null&&bitmap!=null){//判断这个url所对应的imageview是否对应,对应的话才设置图片, 
                imageView.setImageBitmap(bitmap);
            }
            
        }
    }

    /**
     * 加载从start到end的所有图片
     * @param start
     * @param end
     */
    public void loadImage(int start,int end){
        for (int i= start;i<end;i++){//拿到数组中的图片对应的url
            String url = NewsAdapter.URLS[i];
            Bitmap bitmap = getBitmapFromCache(url);
            if(bitmap==null){//没有就要去下载
                NewsAsyncTask newsAsyncTask = new NewsAsyncTask(url);
                newsAsyncTask.execute(url);
                mTask.add(newsAsyncTask);
            }else{
                ImageView imageView =(ImageView) listView.findViewWithTag(url);//通过findViewWithTag找到imageview,这个tag就是imageview的url
                imageView.setImageBitmap(bitmap);
            }
        }
    }

    public void cancelAllTask() {
        if(mTask!=null){
            for (NewsAsyncTask task:mTask) {
                task.cancel(false);
            }
        }
    }

}

最后送上一个小插件 (知识点8),只恨自己执导的太晚,要是有个这个神奇,实体类就不在这么难写了。再次分享给大家,尤其是企业开发的时候,经常能用到,上 git地址,包括用法,安装很详细的。

https://github.com/zzz40500/GsonFormat

好了,大概就这些了,上传一张效果图

android异步加载_第1张图片


最后感谢大家,喜欢请点赞,有疑问,可以回复交流,有错误,请指出。Thx。

转载请说明出处,欢迎转载。http://write.blog.csdn.net/postedit/51533261

阿斯顿

你可能感兴趣的:(android,json,ListView,图片,缓存)