Android异步加载访问网络图片-解析json

来自:http://www.imooc.com/video/7871 
推荐大家去学习这个视频,讲解的很不错。 
慕课网提供了一个json网址可以用来学习:http://www.imooc.com/api/teacher?type=4&num=30。我们的任务就是建立一个listview,将json提供的一些参数,主要是name,picSmall,description显示出来,效果图如下: 

主要思路如下:listview中图片的加载,程序中使用了两种方式,一种是使用Thread类,一种是使用AsyncTask,为了提高listview滑动的效率,并节省流量,使用了LruCache类,更改加载图片的处理逻辑为:当滑动listview时不加载图片,停止滑动listview时加载界面可视范围内的图片。具体程序如下: 
1 NewsBean.Java listview每一项的封装类

 1 package com.example.imoocnews;
 2
 3 public class NewsBean {
 4     private String newsIconUrl;//图片的网址即picSmall
 5     private String newsTitle;//图片的标题即json中的name属性
 6     private String newsContent;//图片的内容即json中的description
 7
 8     public NewsBean(String newsIconUrl, String newsTitle, String newsContent)
 9     {
10         this.newsIconUrl = newsIconUrl;
11         this.newsTitle = newsTitle;
12         this.newsContent = newsContent;
13     }
14     public String getNewsIconUrl() {
15         return newsIconUrl;
16     }
17     public void setNewsIconUrl(String newsIconUrl) {
18         this.newsIconUrl = newsIconUrl;
19     }
20     public String getNewsTitle() {
21         return newsTitle;
22     }
23     public void setNewsTitle(String newsTitle) {
24         this.newsTitle = newsTitle;
25     }
26     public String getNewsContent() {
27         return newsContent;
28     }
29     public void setNewsContent(String newsContent) {
30         this.newsContent = newsContent;
31     }
32
33
34 }

2 适配器NewsAdapter.java

  1 package com.example.imoocnews;
  2
  3 import java.util.List;
  4
  5 import android.content.Context;
  6 import android.view.LayoutInflater;
  7 import android.view.View;
  8 import android.view.ViewGroup;
  9 import android.widget.BaseAdapter;
 10 import android.widget.ImageView;
 11 import android.widget.ListView;
 12 import android.widget.TextView;
 13 import android.widget.AbsListView;
 14
 15 /**
 16  * listview的适配器,包括上下文对象和数据源
 17  * 提高listview的效率:当listview滚动时不去加载可见项图片,停止滚动后再开始加载
 18  */
 19 public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
 20
 21     private LayoutInflater mInflater;
 22     private List<NewsBean> mList;
 23     private ImageLoader mImageLoader;
 24     private int mStart, mEnd;//listview屏幕可视范围内的第一条item和最后一个item
 25     public static String URLS[];//设置一个数组,用来保存所有图片的URL
 26     private boolean mFirstIn;//判断是否是第一次启动程序
 27
 28     public NewsAdapter(Context context, List<NewsBean> data, ListView listView) {
 29         mInflater = LayoutInflater.from(context);
 30         this.mList = data;
 31         mImageLoader = new ImageLoader(listView);//在这里初始化,能够保证只有一个imageloader的实例,即只有一个LruCache的实例
 32         URLS = new String[data.size()];
 33         for (int i = 0; i < data.size(); i++) {
 34             URLS[i] = data.get(i).getNewsIconUrl();//将data中的每一个URL赋值给数组
 35         }
 36         listView.setOnScrollListener(this);
 37         mFirstIn = true;//写在构造函数中,第一次调用newsAdapter时表示第一次启动程序,显示listview
 38     }
 39
 40     @Override
 41     public int getCount() {
 42         return mList.size();
 43     }
 44
 45     @Override
 46     public Object getItem(int position) {
 47         // TODO Auto-generated method stub
 48         return mList.get(position);
 49     }
 50
 51     @Override
 52     public long getItemId(int position) {
 53         // TODO Auto-generated method stub
 54         return position;
 55     }
 56
 57     @Override
 58     public View getView(int position, View convertView, ViewGroup parent) {
 59         ViewHolder holder = null;
 60         if (convertView == null) {
 61             convertView = mInflater.inflate(R.layout.item, null);
 62             holder = new ViewHolder();
 63             holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
 64             holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
 65             holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
 66             convertView.setTag(holder);
 67         }else {
 68             holder = (ViewHolder) convertView.getTag();
 69         }
 70         //holder.ivIcon.setImageResource(R.drawable.ic_launcher);
 71         String url = mList.get(position).getNewsIconUrl();
 72         holder.ivIcon.setTag(url);//给ImageView设置标志,即对应的图片网址
 73         //利用thread类实现异步加载图片,我们这里将其注释,使用AsyncTask的方式
 74         //new ImageLoader().showImageByThread(holder.ivIcon, mList.get(position).getNewsIconUrl());
 75         mImageLoader.showImages(holder.ivIcon, url);
 76
 77         holder.tvTitle.setText(mList.get(position).getNewsTitle());
 78         holder.tvContent.setText(mList.get(position).getNewsContent());
 79         return convertView;
 80     }
 81     class ViewHolder
 82     {
 83         public ImageView ivIcon;
 84         public TextView tvTitle, tvContent;
 85     }
 86     /*
 87      * 当listview滑动的时候调用
 88      */
 89     @Override
 90     public void onScroll(AbsListView view, int firstVisibleItem,
 91             int visibleItemCount, int totalItemCount) {
 92         mStart = firstVisibleItem;
 93         mEnd = mStart + visibleItemCount;
 94         //只在第一次加载的时候调用
 95         if (mFirstIn && visibleItemCount >0) {//表示第一次加载listview并且已经绘制了可见范围内的item
 96             mImageLoader.loadImages(mStart, mEnd);
 97             mFirstIn = false;//加载图片后即设为false
 98         }
 99     }
100
101     /*
102      * 当listview滑动状态变化时调用
103      */
104     @Override
105     public void onScrollStateChanged(AbsListView view, int scrollState) {
106         if (scrollState == SCROLL_STATE_IDLE) {//listview停止滚动
107             //加载可见项
108             mImageLoader.loadImages(mStart, mEnd);
109
110         }else {
111             //停止加载任务
112             mImageLoader.cancelAllTasks();
113         }
114
115     }
116
117 }

3 访问图片的实现ImageLoader.java

  1 package com.example.imoocnews;
  2
  3 import java.io.BufferedInputStream;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.net.HttpURLConnection;
  7 import java.net.MalformedURLException;
  8 import java.net.URL;
  9 import java.util.HashSet;
 10 import java.util.Set;
 11
 12 import android.graphics.Bitmap;
 13 import android.graphics.BitmapFactory;
 14 import android.os.AsyncTask;
 15 import android.os.Handler;
 16 import android.os.Message;
 17 import android.util.LruCache;
 18 import android.widget.ImageView;
 19 import android.widget.ListView;
 20
 21 public class ImageLoader {
 22     private ImageView mImageView;
 23     private String url;
 24     //当图片加载过后就将图片缓存到本地,下次便不用重新联网获取,直接从本地缓存获取即可,一个图片即string url --> bitmap
 25     private LruCache<String, Bitmap> mCache;
 26     private ListView mListView;
 27     private Set<NewsAsyncTask> mTasks;//从start到end范围每次执行加载图片任务的集合
 28
 29     public ImageLoader(ListView listView)
 30     {
 31         mListView = listView;
 32         mTasks = new HashSet<ImageLoader.NewsAsyncTask>();
 33         int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取最大可用内存
 34         int cacheSize = maxMemory/4;//设置缓存的大小
 35         mCache = new LruCache<String, Bitmap>(cacheSize){
 36             @Override
 37             protected int sizeOf(String key, Bitmap value) {
 38                 // 每次将图片存入缓存时返回图片的大小
 39                 return value.getByteCount();
 40             }
 41         };
 42     }
 43     /**
 44      * 将已联网获取成功的图片加入到缓存中
 45      * @param bitmap
 46      */
 47     public void addBitmapToCache(String url, Bitmap bitmap)
 48     {
 49         //在将图片缓存到本地之前要判断这个图片是否已经缓存过了
 50         if (getBitmapFromCache(url) == null) {
 51             mCache.put(url, bitmap);
 52         }
 53     }
 54     /**
 55      * 通过URL从缓存中取出相应的图片
 56      */
 57     public Bitmap getBitmapFromCache(String url)
 58     {
 59         return mCache.get(url);
 60     }
 61
 62     private Handler mHandler = new Handler(){
 63         public void handleMessage(Message msg) {
 64             super.handleMessage(msg);
 65             //通过tag使得ImageView和它对应的URL绑定,这样在上下滑动listview时ImageView显示的图片就始终是正确的
 66             //否则,由于listview的缓存机制,ImageView会先显示出上次加载成功时的图片,然后再显示正确的图片
 67             if (mImageView.getTag().equals(url)) {
 68                 mImageView.setImageBitmap((Bitmap) msg.obj);//使用handler在主线程中更新UI,并将URL对应的图片设置给控件imageview
 69             }
 70
 71         }
 72     };
 73
 74     /**
 75      * 通过使用Thread的方式从网络上获取图片
 76      */
 77     public void showImageByThread(ImageView imageView, final String iconUrl)
 78     {
 79         mImageView = imageView;
 80         url = iconUrl;
 81         new Thread(){
 82             @Override
 83             public void run() {
 84                 // 在新的进程中实现图片的加载
 85                 super.run();
 86                 //从url中获得bitmap,将bitmap发送给主线程
 87                 Bitmap bitmap = getBitmapFromUrl(iconUrl);
 88                 Message message = Message.obtain();
 89                 message.obj = bitmap;
 90                 mHandler.sendMessage(message);
 91             }
 92         }.start();
 93     }
 94     public Bitmap getBitmapFromUrl(String urlString)
 95     {
 96         InputStream is = null;
 97         Bitmap bitmap;
 98         try {
 99             URL url = new URL(urlString);
100             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
101             is = new BufferedInputStream(connection.getInputStream());
102             bitmap = BitmapFactory.decodeStream(is);
103             connection.disconnect();
104             //Thread.sleep(1000);
105             return bitmap;
106         } catch (MalformedURLException e) {
107             // TODO Auto-generated catch block
108             e.printStackTrace();
109         } catch (IOException e) {
110             // TODO Auto-generated catch block
111             e.printStackTrace();
112         } finally
113         {
114             try {
115                 is.close();
116             } catch (IOException e) {
117                 // TODO Auto-generated catch block
118                 e.printStackTrace();
119             }
120         }
121         return null;
122     }
123
124     /**
125      * 加载listview可见范围内的所有图片
126      *
127      */
128     public void loadImages(int start, int end)
129     {
130         for (int i = start; i < end; i++) {
131             String url = NewsAdapter.URLS[i];
132             //看是否能从缓存中取出对应的图片
133             Bitmap bitmap = getBitmapFromCache(url);
134             //如果缓存中没有,就要对每个url执行异步加载任务去获取图片
135             if (bitmap == null) {
136                 NewsAsyncTask task = new NewsAsyncTask(url);
137                 task.execute(url);
138                 mTasks.add(task);
139
140             }else {
141                 //如果缓存中存在此图片,直接将其设置给对应的imageview即可
142                 //因为我们之前给imageview设置的tag就是URL,可以利用findViewWithTag直接在这里获取到
143                 ImageView imageView = (ImageView) mListView.findViewWithTag(url);
144                 imageView.setImageBitmap(bitmap);
145             }
146         }
147     }
148     /**
149      * 取消所有正在进行的图片加载任务
150      */
151     public void cancelAllTasks()
152     {
153         if (mTasks != null) {
154             for(NewsAsyncTask task : mTasks)
155             {
156                 task.cancel(false);
157             }
158         }
159     }
160
161     public void showImages(ImageView imageView, String iconUrl)
162     {
163         //是否能从缓存中取出对应的图片
164         Bitmap bitmap = getBitmapFromCache(iconUrl);
165         if (bitmap == null) {
166             imageView.setImageResource(R.drawable.ic_launcher);//显示默认图片
167         }else {
168             //如果缓存中存在此图片,直接将其设置给对应的imageview即可
169             imageView.setImageBitmap(bitmap);
170         }
171
172     }
173     /**
174      * 使用AsyncTask实现图片的异步加载
175      */
176     class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>
177     {
178         //private ImageView mImageView;
179         private String mUrl;
180         public NewsAsyncTask(String url) {
181         //  mImageView = image;
182             mUrl = url;
183         }
184         @Override
185         protected Bitmap doInBackground(String... params) {
186             String url = params[0];
187             Bitmap bitmap = getBitmapFromUrl(url);//从网络上得到图片
188             if (bitmap != null) {
189                 addBitmapToCache(url, bitmap);//获取图片成功将图片存入缓存中
190             }
191             return bitmap;
192         }
193         @Override
194         protected void onPostExecute(Bitmap bitmap) {
195             // 后台获取图片的任务完成时调用此方法
196             super.onPostExecute(bitmap);
197             //给imageview设置图片
198             ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
199             if (imageView != null && bitmap != null) {
200                 imageView.setImageBitmap(bitmap);
201             }
202             mTasks.remove(this);
203         }
204     }
205
206 }

4. MainActivity.java

  1 package com.example.imoocnews;
  2
  3 import java.io.BufferedReader;
  4 import java.net.MalformedURLException;
  5 import java.net.URL;
  6 import java.io.IOException;
  7 import java.io.InputStream;
  8 import java.io.InputStreamReader;
  9 import java.io.UnsupportedEncodingException;
 10 import java.util.ArrayList;
 11 import java.util.List;
 12
 13 import org.json.JSONArray;
 14 import org.json.JSONException;
 15 import org.json.JSONObject;
 16
 17 import android.app.Activity;
 18 import android.os.AsyncTask;
 19 import android.os.Bundle;
 20 import android.util.Log;
 21 import android.view.Menu;
 22 import android.view.MenuItem;
 23 import android.widget.ListView;
 24
 25 public class MainActivity extends Activity {
 26
 27     private ListView mListView;
 28     private static String jsonURL = "http://www.imooc.com/api/teacher?type=4&num=30";//json数据网址
 29
 30     @Override
 31     protected void onCreate(Bundle savedInstanceState) {
 32         super.onCreate(savedInstanceState);
 33         setContentView(R.layout.activity_main);
 34         mListView = (ListView) findViewById(R.id.lv_main);
 35         new NewsAsyncTask().execute(jsonURL);
 36     }
 37     /**
 38      * 将URL网址上的json数据转化为我们所需的newsbean对象
 39      * @return
 40      */
 41     private List<NewsBean> getJsonData(String url)
 42     {
 43         List<NewsBean> newsBeanList = new ArrayList<NewsBean>();//保存解析出来的所有的数据
 44         try {
 45             //获取到json字符串
 46             String jsonString = readStream(new URL(url).openStream());//和url.openConnection().getInputStream()一样
 47             //Log.d("MainActivity", jsonString);
 48             //将获取到的json字符串变为jsonObject对象,打开网址可以看出data节点是一个jsonArray,array里面存放了一个个的jsonObject
 49             NewsBean newsBean;
 50             JSONObject jsonObject;
 51             String newsUrl = null;
 52             String newsTitle = null;
 53             String newsContent = null;
 54             jsonObject = new JSONObject(jsonString);
 55             JSONArray jsonArray = jsonObject.getJSONArray("data");
 56             for (int i = 0; i < jsonArray.length(); i++) {
 57                 jsonObject = jsonArray.getJSONObject(i);
 58                 newsUrl = jsonObject.getString("picSmall");//图片网址
 59                 newsTitle = jsonObject.getString("name");//title
 60                 newsContent = jsonObject.getString("description");//content
 61                 newsBean = new NewsBean(newsUrl, newsTitle, newsContent);
 62                 newsBeanList.add(newsBean);
 63             }
 64         } catch (MalformedURLException e) {
 65             e.printStackTrace();
 66         } catch (IOException e) {
 67             e.printStackTrace();
 68         } catch (JSONException e) {
 69             // TODO Auto-generated catch block
 70             e.printStackTrace();
 71         }
 72         return newsBeanList;
 73     }
 74
 75     /**
 76      * 解析网络返回的数据
 77      */
 78     private String readStream(InputStream is)
 79     {
 80         InputStreamReader isReader;
 81         String result = "";
 82         String line = "";
 83         try {
 84             isReader = new InputStreamReader(is, "utf-8");//将字节流转化为字符流
 85             BufferedReader buffReader = new BufferedReader(isReader);//从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取
 86             while ((line = buffReader.readLine()) != null) {
 87                 result += line;
 88             }
 89         } catch (UnsupportedEncodingException e) {
 90             e.printStackTrace();
 91         } catch (IOException e) {
 92             // TODO Auto-generated catch block
 93             e.printStackTrace();
 94         }
 95
 96         return result;
 97     }
 98     /**
 99      * 构造一个AsyncTask,传入String类型的URL,返回一个NewsBean对象,每一个对象就是
100      * listview中的每一行数据,包括一个icon,title,content
101      */
102     class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>>
103     {
104
105         @Override
106         protected List<NewsBean> doInBackground(String... params) {
107             return getJsonData(params[0]);
108         }
109
110         @Override
111         protected void onPostExecute(List<NewsBean> result) {
112             super.onPostExecute(result);
113             // 访问网络并解析json成功后返回结果,即我们设置的List<NewsBean>
114             NewsAdapter adapter = new NewsAdapter(MainActivity.this, result, mListView);
115             mListView.setAdapter(adapter);
116         }
117
118     }
119
120 }

源码在这里:http://download.csdn.net/detail/hnyzwtf/9418993

时间: 2024-10-14 06:56:26

Android异步加载访问网络图片-解析json的相关文章

Android异步加载全解析之Bitmap

Android异步加载全解析之Bitmap 在这篇文章中,我们分析了Android在对大图处理时的一些策略--Android异步加载全解析之大图处理  戳我戳我 那么在这篇中,我们来对图像--Bitmap进行一个更加细致的分析,掌握Bitmap的点点滴滴. 引入 Bitmap这玩意儿号称Android App头号杀手,特别是3.0之前的版本,简直就是皇帝般的存在,碰不得.摔不得.虽然后面的版本Android对Bitmap的管理也进行了一系列的优化,但是它依然是非常难处理的一个东西.在Androi

Android异步加载全解析之使用AsyncTask

Android异步加载全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅可以对并发线程进行管理,更可以提高他们执行的效率,优化整个App.当然我们可以自己创建一个线程池,不过这样是很烦的,要创建一个高效的线程池还是挺费事的,不过,Android系统给我吗提供了AsyncTask这样一个类,来帮助我们快速实现多线程开发,它的底层实现,其实就是一个线程池. AsyncTask初探 AsyncTask,顾名思义就是用来做异步处理的.通过AsyncTask,

Android异步加载全解析之引入一级缓存

Android异步加载全解析之引入缓存 为啥要缓存 通过对图像的缩放,我们做到了对大图的异步加载优化,但是现在的App不仅是高清大图,更是高清多图,动不动就是图文混排,以图代文,如果这些图片都加载到内存中,必定会OOM.因此,在用户浏览完图像后,应当立即将这些废弃的图像回收,但是,这又带来了另一个问题,也就是当用户在浏览完一次图片后,如果还要返回去再进行重新浏览,那么这些回收掉的图像又要重新进行加载,保不准就要那些无聊到蛋疼的人在那一边看你回收GC,一边看你重新加载.这两件事情,肯定是互相矛盾的

Android异步加载全解析之IntentService

Android异步加载全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask,再不济也使用的Thread,那么这个IntentService是个什么鬼. 相对与前面我们提到的这两种异步加载的方式来说,IntentService有一个最大的特点,就是--IntentService不受大部分UI生命周期的影响,它为后台线程提供了一个更直接的操作方式.不过,IntentService的不足主要体现在以下几点: 不可以直接和UI做

Android异步加载全解析之使用多线程

异步加载之使用多线程 初次尝试 异步.异步,其实说白了就是多任务处理,也就是多线程执行,多线程那就会有各种问题,我们一步步来看,首先,我们创建一个class--ImageLoaderWithoutCaches,从命名上,大家也看出来,这个类,我们实现的是不带缓存的图像加载,不多说,我们再创建一个方法--showImageByThread,通过多线程来加载图像: /** * Using Thread * @param imageView * @param url */ public void sh

Android异步加载全解析之大图处理

Android异步加载全解析之大图处理 异步加载中非常重要的一部分就是对图像的处理,这也是我们前面用异步加载图像做演示例子的原因.一方面是因为图像处理不好的话会非常占内存,而且容易OOM,另一方面,图像也比文字要大,加载比较慢.所以,在讲解了如何进行多线程.AsyncTask进行多线程加载后,先暂停下后面的学习,来对图像的异步处理进行一些优化工作. 为什么要对图像处理 为什么要对图像进行处理,这是一个很直接的问题,一张图像,不管你拿手机.相机.单反还是什么玩意拍出来,它就有一定的大小,但是在不同

Android异步加载全解析之开篇瞎扯淡

Android异步加载 概述 Android异步加载在Android中使用的非常广泛,除了是因为避免在主线程中做网络操作,更是为了避免在显示时由于时间太长而造成ANR,增加显示的流畅性,特别是像ListView.GridView这样的控件,如果getView的时间太长,就会造成非常严重的卡顿,非常影响性能. 本系列将展示在Android中如何进行异步加载操作,并使用ListView来作为演示的对象. 如何下载图像 下载自然是需要使用网络,使用网络就不能在主线程,在主线程就会爆炸.所以我们必须要在

Android异步加载

Android异步加载 一.为什么要使用异步加载? 1.Android是单线程模型 2.耗时操作阻碍UI线程 二.异步加载最常用的两种方式 1.多线程.线程池 2.AsyncTask 三.实现ListView图文混排 3-1 实现读取网页中的json数据到ListView中 (图片首先为默认图片) 3.1.1:主布局只有一个ListView和一个listView_item的布局 3.1.2:网页json数据的链接(http://www.imooc.com/api/teacher?type=4&n

Android异步加载学习笔记之一:用AsyncTask加载服务器json数据

我们知道在Android开发中,UI主线程不能执行耗时太久的操作,Activity一般是不超过5s,BroadCaseReceiver一般不超过10s,因为这些耗时操作不仅仅阻塞UI线程操作,还可能导致用户不想见到的ANR,所以我们需要使用异步操作. 我们通常用的异步操作有两种方式: 1:多线程或线程池异步加载, 2,AsyncTask异步任务操作(底层也是用的线程池). 数据来源于慕课网:json数据地址:http://www.imooc.com/api/techer?type=4&num=3