一、为什么要使用异步加载?

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&num=30),打开后为json数据,开发者可以通过使用Json格式化工具进行清楚的查看json数据的信息

3.1.3:书写解析网页JSON数据的代码

package cn.edu.bzu.async_listview;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.net.MalformedURLException;
import java.net.URL; 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.util.Log;
import android.widget.ListView; public class MainActivity extends Activity { private ListView mListView;
private static String URL="http://www.imooc.com/api/teacher?type=4&num=30"; //慕课网提供的api链接
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView=(ListView) findViewById(R.id.lv_main);
new NewAsyncTask().execute(URL);
} /**
*实现网络的异步访问
*/
class NewAsyncTask extends AsyncTask<String, Void, List<NewsBean> >{ //参数:params:传入值 progress :进程 Result:返回值 @Override
protected List<NewsBean> doInBackground(String... params) {
return getJsonData(params[0]); //得到从url读取的JSON数据
}
@Override
protected void onPostExecute(List<NewsBean> newsBean) {
// 将生成的newsBean设置给ListView
super.onPostExecute(newsBean);
NewsAdapter adapter=new NewsAdapter(MainActivity.this, newsBean);//创建适配器对象
mListView.setAdapter(adapter);
}
}
/**
* 将url对应的json数据转换为我们所封装的NewsBean对象
* @param url
* @return newsList
*/
private List<NewsBean> getJsonData(String url) {
List<NewsBean> newsBeanList=new ArrayList<NewsBean>();
try {
String jsonString=readStream(new URL(url).openStream()); //此句功能与url.openConnection().getInputStream()相同,可根据URL直接联网获取数据,返回值类型 InputStream;
//Log.d("json",jsonString ); 打印读取的json信息
//解析json数据
JSONObject jsonObject;
NewsBean newsBean; //用于封装jsonObject jsonObject=new JSONObject(jsonString); //json数据添加到jsonObject中
JSONArray jsonArray=jsonObject.getJSONArray("data"); //取出json中的data数据,data为一个数组类型
for(int i=0;i<jsonArray.length();i++){
//取出data中的数据
jsonObject=jsonArray.getJSONObject(i);
newsBean=new NewsBean();
newsBean.imgIconUrl=jsonObject.getString("picSmall");
newsBean.newsTitle=jsonObject.getString("name");
newsBean.newsContent=jsonObject.getString("description");
newsBeanList.add(newsBean);
} } catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return newsBeanList;
} /**
* 通过inputStream解析网页所返回的数据
* @param is
* @return result
*/
private String readStream(InputStream is){
InputStreamReader isr;
String result="";
try {
String line=""; //每行的数据
isr=new InputStreamReader(is,"utf-8"); //字节流转换为字符流
BufferedReader br=new BufferedReader(isr); //将字符流以buffer的形式读取出来
while((line=br.readLine())!=null){
result+=line; //拼接到result中
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
return result;
} }

思路解析:

1) 一个内部类继承AsyncTask,书写未实现的方法,其中在方法中有一个doBackground()方法,在其方法中书写得到Json数据的方法

2) 书写通过inputStream解析网页所返回的数据,只有拿到网页中的json数据才能实现解析的操作

3)将url对应的json数据转换为我们所封装的NewsBean对象,在这个方法中,我们通过第二步拿到了json数据,然后进行json数据的解析,并且封装到实体类对象中,这样你的实体类中就有解析的json数据了

4)创建ListView的适配器

5)在继承AsyncTask的类中书写onPostExecute()方法,在这个方法中,实现绑定适配器,加载数据源的操作

6)在onCreate方法中执行这个异步操作:new NewAsyncTask().execute(URL);并且传入url地址

7)在清单文件中添加联网权限 <use-permission  android:name="android.permission.INTERNET"/>

3-2  实现在ListView中添加网络中图片

&由于加载图片是一种耗时操作,所以我们可以通过新建Thread的方法或者继承AsyncTask来实现

3.2.1:我们通过新线程的方式来实现加载网络图片,新建ImageLoader.java

package cn.edu.bzu.async_listview;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import android.os.Message; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.ImageView; /**
* 用于处理图片的加载
* @author monster
*
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
/**
* UI主线程
*/
private Handler mHandler=new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
//通过设置tag属性避免缓存图片对正确图片的影响
if(mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap((Bitmap) msg.obj);
}
};
};
/**
* 通过多线程的方式加载图片
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView,final String url){
mImageView=imageView; //将ImageView保存进成员变量中
mUrl=url;
new Thread(){
@Override
public void run() {
super.run();
Bitmap bitmap=getBitmapFromURL(url);
Message message=Message.obtain();
message.obj=bitmap;
mHandler.sendMessage(message); //将内容发送到Handle线程中
}
}.start();
}
/**
* 通过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(); //打开链接 //注意是:HttpURLConnection而不是HttpsURLConnection
is=new BufferedInputStream(connection.getInputStream());
bitmap=BitmapFactory.decodeStream(is); //将这个流转换为bitmap
connection.disconnect(); //资源释放
return bitmap;
} catch (java.io.IOException e) {
e.printStackTrace();
}finally{
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null; }
}

思路解析:

1)新建一个线程来实现加载图片

2)创建加载图片的方法,方法的参数为图片的url,这个url可以通过解析刚才的json数据得到

HttpURLConnection connection=(HttpURLConnection) url.openConnection(); //打开链接   //注意是:HttpURLConnection而不是HttpsURLConnection

通过这条语句,将连接转化成流,然后得到流,最后将流转换为bitmap对象

3)得到bitmap对象后,我们新建Handler线程,在这个线程中进行图片的更换,由于bitmap在我们的新线程中,所以我们通过handler的消息传递进行将bitmap对象传入到主线程中去

4)由于ListView的缓存机制,所以我们通过在适配器为图片设置tag的方法从而实现图片的正确加载,避免导致图片的来回替换

在Handler中,我们通过通过设置判断tag属性,来判断图片的url是否相等

附录:ListView适配器的代码:

package cn.edu.bzu.async_listview;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView; public class NewsAdapter extends BaseAdapter {
private List<NewsBean> mList;
private LayoutInflater mInflater; public NewsAdapter(Context context,List<NewsBean> data){
mList=data;
mInflater=LayoutInflater.from(context);
}
@Override
public int getCount() {
return mList.size();
} @Override
public Object getItem(int position) {
return mList.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int positon, View convertView, ViewGroup parent) {
ViewHolder viewHolder=null;
if(convertView==null){
viewHolder=new ViewHolder();
convertView=mInflater.inflate(R.layout.listview_item, null); //布局转化为视图
viewHolder.ivIcon=(ImageView) convertView.findViewById(R.id.iv_icon);
viewHolder.tvTitle=(TextView) convertView.findViewById(R.id.tv_title);
viewHolder.tv_Content=(TextView) convertView.findViewById(R.id.tv_content);
convertView.setTag(viewHolder);
}else{
viewHolder=(ViewHolder) convertView.getTag();
}
viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher); String url=mList.get(positon).imgIconUrl;
viewHolder.ivIcon.setTag(url);
new ImageLoader().showImageByThread(viewHolder.ivIcon,url); //图片id,图片的链接
viewHolder.tvTitle.setText(mList.get(positon).newsTitle);
viewHolder.tv_Content.setText(mList.get(positon).newsContent); return convertView;
}
class ViewHolder{
public TextView tvTitle,tv_Content;
public ImageView ivIcon;
}
}

3.2.2 :我们通过使用继承AsyncTask的方法来实现图片的异步加载

首先我们需要创建一个方法:showImageByAsyncTask,并且传入值为ImageView以及图片的url

其次新建一个类继承AsyncTask,这个类为匿名内部类,为ImageLoder中的类:

private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
private ImageView mImageView;
private String mUrl;
public NewsAsyncTask(ImageView imageView,String url){
mImageView=imageView;
mUrl=url;
}
@Override
protected Bitmap doInBackground(String... params) {
return getBitmapFromURL(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if(mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap(bitmap);
}
}
}

然后仅仅需要修改NewsAdapter中为图片控件赋值的代码即可:

		//new ImageLoader().showImageByThread(viewHolder.ivIcon,url);  //图片id,图片的链接  --->>使用多线程的方法
new ImageLoader().showImageByAsyncTask(viewHolder.ivIcon,url); //使用继承AsyncTask的方式实现图片的异步加载

至此,我们为控件赋值以及异步加载数据的功能已经实现,我们来看下效果:

四、LruCache缓存机制(使用内存空间换取效率)

问题分析:上述的程序中存在一个问题,问题是当用户每次刷新的时候,都需要从网络中读取数据并且进行加载,所以这样增加了用户的流量费用,不利于用户的使用

那么如何提高用户的体验??---->>>>使用缓存

Lru算法:

Lru:Least Recently Used 近期最少使用算法

Android提供了LruCache类来实现这个缓存算法

思路:1.声明LruCache对象

2.在构造方法中实现创建LruCache对象,在这里需要实现它的一个内部方法,用于告诉系统图片的大小,在此之前,你需要得到运行的最大内存,然后用最大内存的一部分

3.添加两个方法:一个将图片加入到缓存,一个从缓存中读取图片

---->>将图片加入到缓存,从网络中读取数据,如果图片不为空,则加入到缓存

----->>从缓存中读取图片,通过键值对的形式读取图片

4.在异步加载图片的使用,首先需要从缓存中读取图片,如果图片为空的话,加载图片,加载出来的图片添加到缓存中

5.在adapter中创建ImageLoader对象,实现添加适配器的时候自动加载缓存机制

附:代码

package cn.edu.bzu.async_listview;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import android.os.AsyncTask;
import android.os.Message; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.util.LruCache;
import android.widget.ImageView; /**
* 用于处理图片的加载
* @author monster
*
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private LruCache<String, Bitmap> mCaches ; //用户图片的缓存 public ImageLoader(){
int maxMemory=(int) Runtime.getRuntime().maxMemory(); //获取最大可用内存
int cacheSize=maxMemory/4; //缓存的大小
mCaches=new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
//在每次存入缓存的时候调用
return value.getByteCount(); //告诉系统,存入的图片的大小
}
};
}
/**
* 把bitmap加入到缓存中
* @param url
* @param bitmap
*/
public void addBitmapToCache(String url,Bitmap bitmap){
if(getBitmapFromCache(url)==null){
mCaches.put(url, bitmap);
}
} /**
* 把图片从缓存中取出来
* @param url
* @return bitmap
*/
public Bitmap getBitmapFromCache(String url){
return mCaches.get(url);
}
/**
* UI主线程
*/
private Handler mHandler=new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
//通过设置tag属性避免缓存图片对正确图片的影响
if(mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap((Bitmap) msg.obj);
}
};
};
/**
* 通过多线程的方式加载图片
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView,final String url){
mImageView=imageView; //将ImageView保存进成员变量中
mUrl=url;
new Thread(){
@Override
public void run() {
super.run();
Bitmap bitmap=getBitmapFromURL(url);
Message message=Message.obtain();
message.obj=bitmap;
mHandler.sendMessage(message); //将内容发送到Handle线程中
}
}.start();
}
/**
* 通过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(); //打开链接 //注意是:HttpURLConnection而不是HttpsURLConnection
is=new BufferedInputStream(connection.getInputStream());
bitmap=BitmapFactory.decodeStream(is); //将这个流转换为bitmap
connection.disconnect(); //资源释放
return bitmap;
} catch (java.io.IOException e) {
e.printStackTrace();
}finally{
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 通过AsyncTask的方式异步加载图片
* @param imageView
* @param url
*/
public void showImageByAsyncTask(ImageView imageView,String url){
Bitmap bitmap=getBitmapFromCache(url); //从缓存中取出图片
if(bitmap==null){
new NewsAsyncTask(imageView,url).execute(url);
}else{
imageView.setImageBitmap(bitmap);
} }
private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
private ImageView mImageView;
private String mUrl;
public NewsAsyncTask(ImageView imageView,String url){
mImageView=imageView;
mUrl=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);
if(mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap(bitmap);
}
}
}
}

五:源代码下载:

https://github.com/monsterLin/Async_ListView

Android异步加载的更多相关文章

  1. 演化理解 Android 异步加载图片

    原文:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038738.html#3018499 在学习"Android异步加载图像小结&q ...

  2. [转载]Android 异步加载解决方案

    2013-12-25 11:15:47 Android 异步加载解决方案,转载自: http://www.open-open.com/lib/view/open1345017746897.html 请 ...

  3. 演化理解 Android 异步加载图片(转)

    演化理解 Android 异步加载图片(转)http://www.cnblogs.com/CJzhang/archive/2011/10/20/2218474.html

  4. android 异步加载框架 原理完全解析

    一.手写异步加载框架MyAsycnTask(核心原理) 1.我为大家手写了一个异步加载框架,涵盖了异步加载框架核心原理. MyAsycnTask.java import android.os.Hand ...

  5. 实例演示Android异步加载图片

    本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...

  6. 实例演示Android异步加载图片(转)

    本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...

  7. [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html  这个可以实现ImageView异步加载 ...

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

    来自:http://www.imooc.com/video/7871 推荐大家去学习这个视频,讲解的很不错. 慕课网提供了一个json网址可以用来学习:http://www.imooc.com/api ...

  9. Android异步加载图像(含线程池,缓存方法)

    研究了android从网络上异步加载图像: (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法. 在主线程中new 一个Han ...

随机推荐

  1. 解决ssh-connect-to-host-github-com-port-22-connection-timed-out

    PC:~$ ssh git@github.com ssh: connect to host github.com port 22: Connection timed out 解决办法:(linux下) ...

  2. C#.NET 大型通用信息化系统集成快速开发平台 4.0 版本 - 拆分表、联系方式的拆分?

    当用户数据有接近10万时,而且多表的关联也比较频繁时,能把大表拆为小表,也会提高系统的性能,I/O.运算性能.当然以后用户数据会更大可能会到30-40万以上,所有有能力时适当拆表,分分合合,合合分分也 ...

  3. Kafka是分布式发布-订阅消息系统

    Kafka是分布式发布-订阅消息系统 https://www.biaodianfu.com/kafka.html Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apa ...

  4. TinyFrame升级之十:WCF Rest Service注入IOC的心

    由于在实际开发中,Silverlight需要调用WebService完成数据的获取,由于之前我们一直采用古老的ASMX方式,生成的代理类不仅难以维护,而且自身没有提供APM模式的调用方式,导致在Sin ...

  5. Eclipse调试常用技巧(转)

    Eclipse调试常用技巧 转自http://daimojingdeyu.iteye.com/blog/633824 1. 条件断点 断点大家都比较熟悉,在Eclipse Java 编辑区的行头双击就 ...

  6. php json 格式控制

    本文同步至我的个人博客:http://www.52cik.com/2015/12/23/php-json-format.html 关于 json 这个问题,陆陆续续有新手朋友找我问,比如为什么我输出的 ...

  7. hibernate Expression详解

    关键字: hibernate expression hibernate Expression详解Expression.gt:对应SQL条件中的"field > value " ...

  8. 系统升级日记(3)- 升级SharePoint解决方案和Infopath

    最近一段时间在公司忙于将各类系统进行升级,其最主要的目标有两个,一个是将TFS2010升级到TFS2013,另外一个是将SharePoint 2010升级到SharePoint 2013.本记录旨在记 ...

  9. HTC Vive 与Leap Motion 出现位置错误的问题

    Leap Motion已经支持VR, 但是官方没有支持HTC Vive的例子. 按照官方的文档, 其实是有问题的: https://developer.leapmotion.com/documenta ...

  10. [ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.4:create (default-cli) on project standalone-pom: Unable to parse configuration of 3: mojo org.apache.maven.plugins:

    问题: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.4:create (defau ...