Android异步加载

用异步的方式去加载数据

为什么要使用异步加载?

1.提高用户的体验,用户在加载数据的时候不会感到明显的卡顿

2.Android UI单线程模型,所有的耗时操作会另起线程,否则会阻塞UI线程

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

1.基于多线程,通过多线程或者线程池进行异步加载;

2.通过Android 封装好的AsyncTask,AsyncTask的底层是基于线程池实现的

本文主要介绍异步加载网络图片

1.获取Json数据,将url对应的Json数据转换成NewsBean对象

2.实现网络的异步访问,在onPostExecute中设置数据源

 

/**
	 * 获取Json数据,将url对应的Json数据转换成NewsBean对象
	 * @param url
	 * @return
	 */
	private List<NewsBean> getJsonData(String url) {
		List<NewsBean> newsBeanList=new ArrayList<NewsBean>();
		try {
			String jsonString=readStream(new URL(url).openStream());
			JSONObject jsonObject;
			NewsBean newsBean;
			try {
				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");
					newsBeanList.add(newsBean);
				}
			} catch (JSONException e) {
				e.printStackTrace();
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return newsBeanList;
	}

将url转换成bitmap

 

 public Bitmap getBitmapFromUrl(String urlString){
	   Bitmap bitmap = null;
	   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();
	} catch (MalformedURLException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}finally{
		try {
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	   return bitmap;
   }

 

 

 

 

 

在异步加载中通常使用两种方式,避免ListView缓存特性造成ListView item的错乱或者错位:

 

1通过在BaseAdapter中设置对应的tag将url或者身份验证信息,将item绑定,在真正加载的时候去判断url或者身份验证信息是否正确,只有在正确的时候才去加载相关数据

设置tag:viewHolder.ivIcon.setTag(url);//解决convertView重用,先缓存图片,再加载目标图片,刷新的效果

判断tag://解决convertView重用,先缓存图片,再加载目标图片,刷新的效果
if(mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap((Bitmap)msg.obj);
}

2.使用成员变量,将对应item数据进行缓存,从而避免由于网络下载时间不确定导致时序上的混乱;

线程加载网络图片:

 

 /**
    * 子线程不能更新UI,通过Handler发送并处理消息,更新UI
   */
   private Handler handler=new Handler(){

	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		//解决convertView重用,先缓存图片,再加载目标图片,刷新的效果
		if(mImageView.getTag().equals(mUrl)){
		mImageView.setImageBitmap((Bitmap)msg.obj);
		}
	}
	   
   };

 

 
/**
    * 通过开启线程加载显示网络图片
    * @param image
    * @param url
    */
   public void showImageByThread(ImageView image,final String url){
	   mImageView=image;//未初始化,空指针异常,传递一个ImageView
	   mUrl=url;
	   new Thread(){

		@Override
		public void run() {
			super.run();
			Bitmap bitmap=getBitmapFromUrl(url);
			Message message=Message.obtain();
			message.obj=bitmap;
			handler.sendMessage(message);
		}
		   
	   }.start();
   }


AsyncTask异步加载显示网络图片:

 

 

 /**
    * 通过异步加载显示网络图片
    * @param image
    * @param url
    */
   public void showImageByAsyncTask(ImageView image,String url){
	   
	   new NewsImageAsyncTask(image,url).execute(url);
	   
   }
   
   private class NewsImageAsyncTask extends AsyncTask<String,Void,Bitmap>{
	 /**
	  * 传递一个ImageView,url
	  */
	 public NewsImageAsyncTask(ImageView iamgview,String url){
		 mImageView=iamgview;
		 mUrl=url;
	 }
     /**
      * doInBackground运行在子线程中,不可以更新UI
      * 解析url转换成bitmap
      */
	@Override
	protected Bitmap doInBackground(String... params) {
		return getBitmapFromUrl(params[0]);
	}
	/**
	 * onPostExecute运行在主线程中,直接加载显示网络图片
	 */
     @Override
    protected void onPostExecute(Bitmap result) {
    	super.onPostExecute(result);
    	//解决convertView重用,先缓存图片,再加载目标图片,刷新的效果
    	if(mImageView.getTag().equals(mUrl)){
    	mImageView.setImageBitmap(result);
    	}
    }
   }

上面实现了线程和异步加载两种方式,加载网络图片,可是每次都要重新加载,很消耗流量,用户体验不好,于是需要使用缓存,缓存主要使用Lru算法,Android提供了LruCache类实现这个缓存算法。

LruCache将内容保存在内存中,并以一定的方法去管理内容,从而做到在一定的阈值之内保证能够缓存所有的内容数据,超出一定的范围之后,将近期最少使用的内容寄宿出去,从而实现缓存管理。

 

  /**
    * 初始化LruCache
    */
   public ImageLoader(){
	   //获取最大的可用内存
	   int maxMemroy=(int) Runtime.getRuntime().maxMemory();
	   //设置缓存大小
	   int cacheSize=maxMemroy / 4;
	   //初始化LruCache
	   mCaches=new LruCache<String, Bitmap>(cacheSize){
		   /**
		    * 默认返回元素个数,重写获取每个存储对象的大小
		    */
		   @Override
		   protected int sizeOf(String key, Bitmap value) {
			//在每次存入缓存的调用,返回bitmap的大小
			return value.getByteCount();
		}
	   };
   }
   /**
    * 将图片存入缓存
    * 首先判断内存中是否已经缓存了图片
    * @param url
    * @param bitmap
    */
    public void addBitmapToCache(String url, Bitmap bitmap){
    	//判断内存中是否已经缓存了图片
    	if(getBitmapFromCache(url)==null){
    		mCaches.put(url, bitmap);
    	}
    }
    /**
     * 从缓存中获取图片
     * @param url
     * @return
     */
    public Bitmap getBitmapFromCache(String url){
		
    	return mCaches.get(url);
    	
    }

我在写代码的时候重用了Imageview和url以至于一直得不到加载的效果

 

 

private class NewsImageAsyncTask extends AsyncTask<String,Void,Bitmap>{
	 /**
	  * 传递一个ImageView,url
	  */
	 private ImageView mImageView;
	 private String mUrl;
	 public NewsImageAsyncTask(ImageView iamgview,String url){
		 mImageView=iamgview;
		 mUrl=url;
	 }
     /**
      * doInBackground运行在子线程中,不可以更新UI
      * 解析url转换成bitmap,将下载的bitmap存到缓存区域的内存中
      */
	@Override
	protected Bitmap doInBackground(String... params) {
		String url=params[0];
		//从网络获取图片
		Bitmap bitmap=getBitmapFromUrl(url);
		//将不在缓存区域的图片加入缓存
		addBitmapToCache(url,bitmap);
		return bitmap;
	}
	/**
	 * onPostExecute运行在主线程中,直接加载显示网络图片
	 */
     @Override
    protected void onPostExecute(Bitmap result) {
    	super.onPostExecute(result);
    	//解决convertView重用,先缓存图片,再加载目标图片,刷新的效果
    	if(mImageView.getTag().equals(mUrl)){
    	  mImageView.setImageBitmap(result);
    	}
    }
   }
  


我把mImageView和mUrl定义成了全局变量,以至于图片的顺序显示不正常,真是郁闷呀
当ListView的item比较复杂的时候,我们上述的做法会显的比较卡顿,需要进一步优化

 

优化方案:

A.ListView滑动停止后才加载可预见项

B.ListView滑动时,取消所有加载项

总结:

通过异步加载,避免阻塞UI线程

通过LruCache,将已下载图片放到内存中

通过判断ListView滑动状态,决定何时加载图片

除了ListView,其他控件(GridView)也可以使用异步加载

异步加载

 /**
    * 加载可见的item
    * @param start
    * @param end
    */
   public void loadImage(int start ,int end){
        for(int i=start;i<end;i++){
          String url=NewsAdapter.Urls[i];
          //从缓存中取出对应的图片
          Bitmap bitmap =getBitmapFromCache(url);
          //如果缓存中没有图片,就去加载图片
          if(bitmap==null){
             NewsImageAsyncTask task=new NewsImageAsyncTask(url);
             task.execute(url);
             mTask.add(task);
           }else{
            //解决convertView重用,先缓存图片,再加载目标图片,刷新的效果
            ImageView mImageView =(ImageView) mListView.findViewWithTag(url);
            if(mImageView!=null && bitmap!=null){
                mImageView.setImageBitmap(bitmap) ;
            }
              }
        }
   }
   /**
    * 取消task中的所以任务
    */
    public void cancelAllTasks(){
      if(mTask!=null){
         for(NewsImageAsyncTask task:mTask){
		     task.cancel(false);
        }
      }
    }
​
   }
   

 

线程加载:

 

 

 /**
    * 加载可见的item
    * @param start
    * @param end
    */
   public void loadImage(int start ,int end,final ImageCallBack mCallBack){
	   for(int i=start;i<end;i++){
		   final String url=NewsAdapter.Urls[i];
		   //从缓存中获取图片对象
		   Bitmap bitmap=getBitmapFromCache(url);
		   /**
		    * 子线程不能更新UI,通过Handler发送并处理消息,更新UI
		   */
		   final Handler handler=new Handler(){

			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				 mCallBack.onImageLoader((Bitmap)msg.obj,url);
				}
		   };
		   if(bitmap==null){
		   new Thread(){

			@Override
			public void run() {
				super.run();
				Bitmap bitmap=getBitmapFromUrl(url);
				Message message=Message.obtain();
				message.obj=bitmap;
				handler.sendMessage(message);
				//将图片存储到缓存区域
				addBitmapToCache(url,bitmap);
			}
			   
		   }.start();
		  }else {
			  mCallBack.onImageLoader(bitmap,url);
		  }
	   }
   }


Android异步加载网络图片(线程):http://pan.baidu.com/s/1bn2VVOz

 

Android异步加载网络图片(AsyncTask):http://pan.baidu.com/s/1ntEMQ1b

Android异步加载网络图片,listview滑动优化(线程):http://pan.baidu.com/s/1hZpvC

Android异步加载网络图片,listview滑动优化(AsyncTask):http://pan.baidu.com/s/1pJiUHB1

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值