-
Android异步加载 异步加载:用异步的方式加载数据,原因无非两个:1.提高用户体验,用户在加载数据的时候不会感觉到明显的卡顿;2.安卓的要求。 异步加载最常用的两种方式:多线程\线程池;AsyncTask。 其中AsyncTask的底层也是通过线程池实现,所以二者异曲同工。查看全部
-
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() 移交给 滑动监听事件。查看全部
-
4.修改onPostExecute,原有内容注释掉,改为: ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl); if (imageView != null && bitmap !=null){ imageView.setImageBitmap(bitmap); } 在ImageView imageView=(ImageView) mListView.findViewWithTag(url); 我们不再使用getView()去触发下载,而是使用ListView滚动的时候去触发下载任务。查看全部
-
异步加载——图片加载优化 1. 创建一个静态String[]将所有要显示的图片的地址保存起来 public static String[] URLStrings;//用来保存当前所有需要显示的图片的url地址 public NewsAdapter(Context context, List<NewsBean> mListData) { super(); this.mList = mListData; mInflater = LayoutInflater.from(context); mImageLoader = new ImageLoader(); URLStrings = new String[mListData.size()]; for (int i = 0; i<mListData.size(); i++){ URLStrings[i] = mListData.get(i).imageUrl; }} 2. 创建一个方法,加载从start到end的所有图片 public void loadImages(int start, int end){ for (int i = start; i < end; i++){ //因为URLStrings是静态的,所以可以直接使用 String url = NewsAdapter.URLStrings[i]; //从缓存中获取对应的图片并判断 Bitmap bitmap = getBitmapFromCache(url); if(bitmap == null){ NewsAsyncTask task = new NewsAsyncTask(url); task.execute(url); mTasks.add(task); }else { //从缓存中直接获取数据让iamgeView显示,这里imageView是从url的tag得来的 ImageView imageView = (ImageView) mListView.findViewWithTag(url); imageView.setImageBitmap(bitmap); }}} 3. 改写showImageByAsyncTask(),让显示图片的操作交给scroll的监听事件,当bitmap不在缓存时,也不去加载图片查看全部
-
实际项目中的 Item 可能非常复杂,仅仅使用前面所学的异步加载过程,效果可能并不好。这是因为 ListView的滚动对画面流畅度的要求是非常高的,当你做异步加载的时候,虽然加载是在新的线程中实行的,并没有阻塞 UI 线程,但当我们加载好之后,去更新 UI 线程,就会导致 UI线程发生一次“重绘”,而如果这次“重绘”正好发生在 ListView滚动的时候,就会导致滚动的“卡顿”,这种体验并不好。 那么,如何提高 ListView在滚动时候的异步加载的效率问题? 1. ListView滑动停止后才加载可见项; 2. ListView滑动时,取消所有加载项。 实际上,滚动的时候加载可见项是没有意义的。 ------------------ NewsAdapter 实现接口 AbsListView.OnScrollListener,重写如下两个方法: onScrollStateChanged:当 ListView的滑动状态改变的时候会调用此方法。 onScroll:在整个滑动状态中都会去调用。 实现步骤: 判断ListView当前的滚动状态,如果它处于滚动过程中,则取消所有的正在加载的任务,让ListView安静地滚动,当它滚动完毕之后,我们再根据当前 ListView 所反映的第一项和最后一项,去加载这之间所有的项目,也就是在ListView停止滚动之后加载所有可见项。查看全部
-
LruCache类似Map,底层也是通过 HashMap实现的,所以也有 put与get 方法。 创建完两个方法(//将数据存入缓存中,增加到缓存public void addBitmapToCache(String url,Bitmap bitmap){//判断缓存是否存在 if (getBitmapFromCache(url)==null) {mCaches.put(url, bitmap);}} //从缓存中获取数据public Bitmap getBitmapFromCache(String url){return mCaches.get(url);} )后,需要对之前的方法做一些修改: 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.show。。 这样就保证只有一个 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方法。查看全部
-
使用asynctask实现异步加载图片查看全部
-
在异步加载中,我们通常使用两种方式去避免ListView的缓存所造成的ListView的Item的错乱: 1. 在BaseAdapter中设置对应的Tag,将一个 url 或者其他可以表明身份的信息与相应的Item绑定,并在真正加载的时候,去判断这样一个身份信息是否正确,只有正确的情况下才会去设置图片(if (mImageView.getTag().equals(mUrl)) )。 2. 使用成员变量(mImageView=iamgeView;mUrl=url;),将对应的数据进行缓存,从而避免由于网络下载时间不确定所导致的时序上的混乱,这种方式与我们在 BaseAdapter去使用ViewHolder这样的一个模式去缓存View的方式是一样的。我们通过成员变量将每次调用方法所传进来的参数缓存起来,从而避免时序上的顺序错误。 这两种方式在异步加载中是使用得非常广泛的,大部分的异步加载错误都可以通过这两种方式去解决。 在子线程中将Bitmap以message的形式发送出去: Bitmap bitmap = getBitmapFromURL(url);//从 url中获取 bitmap。 Message message = Message.obtain();//此方法使用现有的、已回收的Message,提高Message的使用效率。 message.obj = bitmap;//将信息的内容(obj)设置为 bitmap。 handler.sendMessage(message);//调用Handler将载有bitmap的Message发送出去。 注意这里的 Handler 要选择android.os.Handler的。查看全部
-
我们发现异步加载出现图片错位的情况,图片的排列顺序有问题。 图片显示错位的原因:ListView自身的缓存机制。正确的ListView没有显示正确的URL。 解决办法:在适配器中给每一个图片设置一个标识Tag,Tag本身就是图片对应的url,也就是将图片和 url 进行了绑定。 如图,只有当Tag等于图片自身的url的时候才加载图片,否则保持原样。避免了缓存图片对正确图片的影响。查看全部
-
将图片的地址 urlString 转变为 Bitmap。 基本上和网络相关的操作都需要捕捉异常。 1. 将 urlString(字符串类型)转变为 URL 类型对象url。 2. 通过 url 获取连接对象 connection。方法:url.openConnection()。 3. 通过连接对象获得输入流,并用BufferedInputStream包装: is = new BufferedInputStream(connection.getInputStream()); 4. 将输入流解析为Bitmap:bitmap = BitmapFactory.decodeStream(is); 5. 释放资源:connection.disconnect(); is.close();查看全部
-
异步加载——实现ListView图文混排逻辑总结 第一步 onCreate中new一个NewsAsyncTask 将url传递给getJsonData 实现异步访问 第二步 getJsonData将url转换为NewsBean对象 第三步 onPostExecute讲NewsBean的数据传递给NewsAdapter 第四步 NewsAdapter构造一个ListView数据源 并将数据源设置给ListView 异步加载的第一层:通过AsyncTask访问网络,获取json或者XML字符串,然后解析他们产生若干object,将每个object放入到ListVIew中(adapter需要使用view holder pattern去写),AsyncTask中的访问网络获取json或者XML字符串,并且产生若干个object的工作就是在doInBackground()方法中进行的,所以这个方法总的来说就是用来准备数据源的。查看全部
-
本节课目标:文艺方式重现BaseAdpter 1、自定义Adpter继承BaseAdpter; 2、定义变量:List<NewsBean>;LayoutInflater; 3、重写构造函数NewsAdpter(Context context, List<NewsBean> data)。 4、文艺方式重写getView()方法。 4.1、自定义类ViewHolder,映射相关的view对象 最后在 onPostExecute()方法中将生成的 newsBean添加到 mListView:通过适配器加载: NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeen); mListView.setAdapter(adapter);查看全部
举报
0/150
提交
取消