-
多看几次 会有不同的收获查看全部
-
LruCahce本质是LinkedHashmap查看全部
-
Android 异步加载的总结 1. 通过异步加载,避免阻塞UI线程,从而实现了网络的异步访问。这是基础,也是 Android 要求我们这么做的。 2. 通过 LruCache,将已经下载的图片放到内存中,以一个缓存避免多次重复的下载,提高用户的体验。当然,这里只介绍了内存缓存,我们还可以使用 DiskLruCache ,将内存中的图片保存到硬盘中,保存到存储器中,做为一个持久化的缓存,这也称作“二级缓存”。而内存缓存便叫做“一级缓存”。 3. 通过判断 ListView 的滑动状态,决定什么时候该加载图片,什么时候不该加载图片,而让ListView平滑地去滚动。在这一点上我们是通过对 ListView 的滑动状态的监听,去实现这样的操作,将加载图片的控制权由 getView() 转移到我们的滑动事件,从而让复杂的 ListView 也能实现流畅的异步加载。 4. 不仅仅是 ListView ,任何控件都可以使用异步加载,这里以 ListView 作为例子的原因是因为它比较常用。异步加载不仅仅用于获取网络图片,所有的耗时操作我们都可以认为是异步加载,通过异步的方式,去将这些耗时操作与主线程分开,从而提升用户体验,这也是异步加载的核心所在。查看全部
-
上节课中我们看到图片最初并没有加载,需要滑动才加载,因为我们把加载图片的控制权交给了onScrollStateChanged ,也就是说只有滑动状态改变,才触发加载任务。那么预加载的问题该如何解决呢?我们可以把启动时的图片加载交给onScroll(因为它一直被调用)去执行。步骤: 1. 在NewsAdapter中设置一个布尔变量,判断当前是不是第一次启动:private boolean mFirstIn; 在初始化的时候(就在构造方法里面)把 mFirstIn设置为true。 2. 在 onScroll() 中进行判断: if( mFirstIn && visibleItemCount>0){ mImageLoader.loadImages(mStart,mEnd); mFirstIn = false; } 为什么要写visibleItemCount>0?因为 onScroll()会被多次调用,而初始化调用的时候visibleItemCount是等于0的,这时候 Item 还没有被加载,所以我们要将这个过程跳过去。这就是为什么我们这里要使用两个条件:当前列表是第一次显示、而且 ListView 的 Item 已经画出来了。查看全部
-
步骤: 1.在 NewsAdaper 中修改 onScrollStateChanged(): 如果停止滚动则加载可见项:mImageLoader.loadImages(mStart,mEnd); 如果滚动,则取消所有加载任务:mImageLoader.cancelAllTasks(); 2. 在ImageLoader中添加方法cancelAllTasks(): public void cancelAllTasks(){ if (mTask != null){ for (NewsAsyncTask task:mTask) { task.cancel(false);//任务取消 } } } 3. 修改 NewsAdapter 的有参构造方法,添加参数 ListView listView,并传递该参数: mImageLoader = new ImageLoader(listView); 另外,别忘了注册监听事件:listView.setOnScrollListener(this); 4. 修改 MainActivity 中的 onPostExecute: NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeen,mListView); 5. 修改 ImageLoader 的 onPostExecute,在最后添加:mTask.remove(this); //设置完bitmap,任务结束,所以要移除该任务(写在if之外) --------------------------- 核心:将显示图片的控制权由原来的 getView() 移交给 滑动监听事件。查看全部
-
对 NewsAdapter 的修改: 1. 添加成员变量,同时也是一个静态的数组: public static String[] URLS; //保存所有当前所获得的图片的url地址 2. 修改之前写的有参构造方法,用for循环为静态数组赋值: public NewsAdapter(Context context,List<NewsBean> datas){ mList=datas; mInflater=LayoutInflater.from(context); mImageLoader = new ImageLoader(mStart,mEnd); URLS = new String[datas.size()]; for (int i=0;i<datas.size();i++){ URLS[i] = datas.get(i).getNewsIconUrl(); } }查看全部
-
本节步骤 1. 在ImageLoader中添加成员变量: private ListView mListView; //保存从start到end的图片 private Set<NewsAsyncTask> mTask; //用于任务管理 2.修改原有的无参构造方法,改为有参,参数是ListView listView。并做初始化: mListView = listView; mTask = new HashSet<>(); 3.构建新的方法,加载从start到end的所有图片 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) { NewsAsyncTask task = new NewsAsyncTask(url); task.execute(url); mTask.add(task); //加入任务管理表 }else { ImageView imageView = (ImageView) mListView.findViewWithTag(url); imageView.setImageBitmap(bitmap); } } } 4.修改onPostExecute,原有内容注释掉,改为: ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl); if (imageView != null && bitmap !=null){ imageView.setImageBitmap(bitmap); } 5. 修改showImageByAsyncTask(),参见另外一篇笔记。 6. 修改 NewsAdapter。 额外补充:ListView的findViewWithTag,通过Tag找到ImageView。那么Tag又是什么呢,就是url。查看全部
-
这里对 showImageByAsyncTask做了修改: 将 new NewsAsyncTask(url).execute(url); 改成 imageView.setImageResource(R.mipmap.ic_launcher); 使用默认图片,将显示的控制权移交给有参的构造方法ImageLoader(int start,int end)。 我们不再使用getView()去触发下载,而是使用ListView滚动的时候去触发下载任务。查看全部
-
实际项目中的 Item 可能非常复杂,仅仅使用前面所学的异步加载过程,效果可能并不好。这是因为 ListView的滚动对画面流畅度的要求是非常高的,当你做异步加载的时候,虽然加载是在新的线程中实行的,并没有阻塞 UI 线程,但当我们加载好之后,去更新 UI 线程,就会导致 UI线程发生一次“重绘”,而如果这次“重绘”正好发生在 ListView滚动的时候,就会导致滚动的“卡顿”,这种体验并不好。 那么,如何提高 ListView在滚动时候的异步加载的效率问题? 1. ListView滑动停止后才加载可见项; 2. ListView滑动时,取消所有加载项。 实际上,滚动的时候加载可见项是没有意义的。 ------------------ NewsAdapter 实现接口 AbsListView.OnScrollListener,重写如下两个方法: onScrollStateChanged:当 ListView的滑动状态改变的时候会调用此方法。 onScroll:在整个滑动状态中都会去调用。 实现步骤: 判断ListView当前的滚动状态,如果它处于滚动过程中,则取消所有的正在加载的任务,让ListView安静地滚动,当它滚动完毕之后,我们再根据当前 ListView 所反映的第一项和最后一项,去加载这之间所有的项目,也就是在ListView停止滚动之后加载所有可见项。查看全部
-
addBitmapToCache()和 getBitmapFromCache() 方法。查看全部
-
LruCache类似Map,底层也是通过 HashMap实现的,所以也有 put与get 方法。 创建完两个方法(addBitmapToCache 和 getBitmapFromCache )后,需要对之前的方法做一些修改: 1. 在showImageByAsyncTask()先判断缓存中是否存在该对象,有则直接从缓存中取出,没有才创建: Bitmap bitmap = getBitmapFromCache(url); if (bitmap==null) { new NewsAsyncTask(imageView, url).execute(url);//启动异步加载 }else { imageView.setImageBitmap(bitmap);//直接使用缓存中的bitmap,无需异步加载 } 2. 在doInBackground()中,原先只是下载图片,现在要把下载的图片缓存到 LruCache 里面: Bitmap bitmap = getBitmapFromURL(params[0]); if(bitmap!=null){ //bitmap非空表示确实下载好了,这时将其缓存起来 addBitmapToCache(params[0],bitmap); } return bitmap; 3. 在 NewsAdapter 添加成员变量 ImageLoader mImageLoader; 并在构造方法中初始化:mImageLoader = new ImageLoader(); 然后把getView()里面实现异步加载的这句代码: new ImageLoader().showImageByAsyncTask(viewHolder.iv_icon,url); 改成 mImageLoader.showImageByAsyncTask(viewHolder.iv_icon,url); 这样就保证只有一个 LruCache。 ------------------ 使用缓存,是典型的以空间换时间,以内存换效率。查看全部
-
使用 LruCache: 1. 在我们定义的类 ImageLoader里面添加成员变量: private LruCache<String,Bitmap> mCaches; 这里第一个参数(Key)就是缓存的图片的名字,选择URL地址作为名字较好,所以类型写String,而第二个参数(Value)是我们要保存的对象,类型填Bitmap。 2. 在构造方法中初始化 LruCache。在使用LruCache之前,我们需要设定一个可用范围,毕竟不可能将所有内存都作为缓存,这里要将一部分内存转化为我们所需要的缓存空间: 首先获取当前应用可用的内存大小: int maxMemory = (int)Runtime.getRuntime().maxMemory(); 然后设定一个我们所需要的缓存的大小: int cacheSize = maxMemory / 4; 接着初始化 LruCache,通过匿名内部类的方式重写里面的sizeOf()方法,该方法是为了获取每一个缓存对象的大小,我们必须重写这个方法,去加载正确的内存大小,不然它默认返回元素的个数,这样就不对了。sizeOf()在每次存入缓存的时候会调用,我们就需要在加入缓存的时候,给出一个准确的大小,也就是Bitmap的大小,这里直接 return value.getByteCount();将bitmap的实际大小保存进去: mCache = new LruCache<String, Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { //在每次存入缓存的时候调用 return value.getByteCount(); } }; 这样,LruCache就初始化好了。 我们要如何使用呢?在使用之前需要创建两个方法,分别是“将内容保存到LruCache”和“从LruCache中获取内容”,也就是set/get方法。查看全部
-
上节课我们滑动ListView,发觉曾经加载的图片还是要重新加载,仅仅只是滑动就要不停地反复加载,这样就浪费了流量,用户也会抱怨。 解决方法:使用缓存,将已经加载的图片缓存起来,而不用每次都去加载。 使用缓存的好处:1.让ListView的滑动更加流畅;2.节省流量,提高用户体验。 LruCache:将内容保存在内存中,并以一定的方法管理这些内容,做到在一定范围之内保证能够缓存所有的内容数据,而在超出一定范围之后,将近期最少使用的内容剔除出去,从而实现一个非常优秀的缓存管理。查看全部
-
异步加载图片——AsyncTask的编写 图中的方式仍然存在缓存带来的显示错误的问题,和前面的多线程在未优化之前出现的问题一样,毕竟 AsyncTask 本身就基于多线程。解决方式和多线程类似,先在 NewsAsyncTask 类中添加成员变量 mUrl,在有参构造方法中传入url并赋值给 mUrl。然后在onPostExecute()方法中修改为: if (mImageView.getTag().equals(mUrl)) { mImageView.setImageBitmap(bitmap);//将Bitmap设置给ImageView } 只有当Tag等于图片自身的url的时候才加载图片,否则保持原样。避免了缓存图片对正确图片的影响。查看全部
-
在异步加载中,我们通常使用两种方式去避免ListView的缓存所造成的ListView的Item的错乱: 1. 在BaseAdapter中设置对应的Tag,将一个 url 或者其他可以表明身份的信息与相应的Item绑定,并在真正加载的时候,去判断这样一个身份信息是否正确,只有正确的情况下才会去设置图片(图中橙色部分)。 2. 使用成员变量(图中蓝色部分),将对应的数据进行缓存,从而避免由于网络下载时间不确定所导致的时序上的混乱,这种方式与我们在 BaseAdapter去使用ViewHolder这样的一个模式去缓存View的方式是一样的。我们通过成员变量将每次调用方法所传进来的参数缓存起来,从而避免时序上的顺序错误。 这两种方式在异步加载中是使用得非常广泛的,大部分的异步加载错误都可以通过这两种方式去解决。查看全部
举报
0/150
提交
取消