您的位置:首页 > 移动开发 > Android开发

Android:AsyncTask的二次使用

2015-10-03 13:16 453 查看
通过这个Demo的练习,我对AsyncTask不再像之前那样陌生了。而且通过这个Demo,我对ListView/GridView去加载多图片这种需求有了更好的方式,也就是接下来这个Demo想要展示的内容。

之前对List去加载多图片这样的需求,要么是子线程去搞一下,要么直接在主线程搞。当然,在主线程搞的前提是,是加载本地图片,不是网络图片。没有去使用什么Lrucache,也没有使用滑动时停止加载,不滑动时进行数据加载的策略。糟糕的用户体验,糟糕应用。不得不说,不去看别人的优秀代码,永远不知道自己的代码写的有多烂。

这次,同样是根据慕课网的讲解,来写的一个Demo。内容上,可能会有一点点小的偏差。但是不影响整体效果。

ok,不多说,还是上代码吧。

这个Demo只有一个页面,这个页面就是一页新闻的页面。里面显示的就是一条条的新闻。每条新闻有一个标题,和一个内容描述,以及一个小图片。

对于新闻内容的获取,使用了一个
AsyncTask
去加载。然后先给每个条目只显示一张默认图片+新闻标题和内容。这样,也算是基本完成了新闻页面的展示。只是每个新闻的图标没有去展示。

然后是对新闻的每个条目的图片的获取,还是通过
AsyncTask
去获取的。不过和对新闻内容的获取的区别是:新闻内容是一次性获取下来,也就是说,对于新闻内容的获取,只需要调用一次
AsyncTask
execute
方法;但是对于新闻的每个条目的图片的获取,需要多次去执行
AsyncTask
execute
方法,每次执行,获取一个条目的图片。

当然,对于新闻条目的图片,如果仅仅是这样的多次获取,还是不够好的,因为这样以来,每次去滑动,都会去加载屏幕上显示的条目的图片。而且,已经加载过的图片,仍然会去加载,这样就很浪费流量了。于是,就做了一个缓存策略。通过
Lrucache
,去缓存加载过的图片,然后,在去网络加载图片之前,先看看缓存中有没有该图片,如果有了,就不去加载了,以提高用户体验。

但是这样还是不够的。因为当ListView的布局比较复杂,需要显示的内容比较多的时候,当用户去滑动屏幕,而这时候,如果有图片加载完成,会去显示到屏幕上,就是这样的一个过程,可能会导致屏幕的卡顿。这样也不是好的用户体验。于是,就做了一个滑动策略:也就是,当用户去滑动屏幕的时候,并不去加载图片资源,就让他看看新闻的标题和内容就好。当用户停止滑动的时候,就去加载对应的图片资源,显示正确的图片。(当然,如果是已经加载好的图片,会在滑动的同时,直接显示出来,这样也不会导致滑动的卡顿)。并且,由于第一次打开页面的时候,不会触发滑动停止的状态,于是,在第一次打开页面的时候,去主动加载一个屏幕需要显示的图片内容,后面的,就是根据滑动停止状态才去加载,并且在滑动的过程中,会去取消掉,所有的,准备加载的任务。这样,就可以在滑动的时候开心的滑,在加载的时候,安心的加载。

以上,就是对这个Demo的比较详细的介绍。

代码区:

xml布局:

a.
Actiivty
的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MoocActivity" >

<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>

</RelativeLayout>


* b `listview`的Item的布局:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
android:id="@+id/imageview"
android:layout_width="64dp"
android:layout_height="64dp"
android:contentDescription="@string/action_settings"
android:src="@drawable/ic_launcher" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/imageview"
android:orientation="vertical"
android:paddingLeft="4dp" >

<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Title"
android:textSize="16sp" />

<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
android:text="Content"
android:textSize="12sp" />
</LinearLayout>

</RelativeLayout>


java代码:

Activity
的代码:

package com.duck.moocAsyncTask;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

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.widget.ListView;

public class MoocActivity extends Activity {

private static final String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
private ListView mListView;

private NewsAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mooc);
mListView = (ListView) findViewById(R.id.listview);
new NewsAsyncTask().execute(URL);
}

class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {

private static final boolean DEBUG = false;

@Override
protected void onPostExecute(List<NewsBean> result) {
super.onPostExecute(result);
adapter = new NewsAdapter(result, MoocActivity.this,mListView);
mListView.setAdapter(adapter);
}

@Override
protected List<NewsBean> doInBackground(String... params) {
String url = params[0];

return getDatasFromUrl(url);
}

private List<NewsBean> getDatasFromUrl(String url) {
try {
URLConnection connection = new URL(url).openConnection();
InputStream is = connection.getInputStream();

String jsonString = getStringFromStream(is);
return getNewsFromJson(jsonString);

} catch (Exception e) {
e.printStackTrace();
}
return null;
}

private List<NewsBean> getNewsFromJson(String jsonString) {
List<NewsBean> newsBeanList = new ArrayList<NewsBean>();
try {
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("data");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject object = jsonArray.getJSONObject(i);
String url = object.getString("picSmall");
String title = object.getString("name");
String content = object.getString("description");
NewsBean bean = new NewsBean();
bean.iconUrl = url;
bean.title = title;
bean.content = content;
newsBeanList.add(bean);
}
} catch (JSONException e) {
e.printStackTrace();
}
if (DEBUG) {
// 数据获取OK!
System.out.println("newsBeanList: " + newsBeanList);
System.out.println("\n \n newsBeanList.SIZE: "
+ newsBeanList.size());
System.out.println("\n \n newsBeanList.3: "
+ newsBeanList.get(3).title);
}
return newsBeanList;
}

private String getStringFromStream(InputStream is) {
String json = "";
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
try {
while ((line = br.readLine()) != null) {
json += line;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return json;
}

}
}


Adapter
代码:

package com.duck.moocAsyncTask;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class NewsAdapter extends BaseAdapter implements OnScrollListener {

private static boolean firstIn;
private List<NewsBean> mList;
private LayoutInflater mInflater;

private ImageLoader loader;
private int start;
private int end;
public static String[] URLS;

public NewsAdapter(List<NewsBean> newsBeans, Context context,
ListView listView) {
firstIn = true;
URLS = new String[newsBeans.size()];
for (int i = 0; i < newsBeans.size(); i++) {
URLS[i] = newsBeans.get(i).iconUrl;
}
mList = newsBeans;
mInflater = LayoutInflater.from(context);
loader = new ImageLoader();
listView.setOnScrollListener(this);
}

@Override
public int getCount() {
return mList.size();
}

@Override
public NewsBean getItem(int position) {
return mList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.ivIcon = (ImageView) convertView
.findViewById(R.id.imageview);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
holder.tvContent = (TextView) convertView
.findViewById(R.id.tv_content);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// show data
NewsBean bean = getItem(position);
holder.ivIcon.setImageResource(R.drawable.ic_launcher);
holder.ivIcon.setTag(bean.iconUrl);// 绑定tag
// new ImageLoader(holder.ivIcon, bean.iconUrl)
// .disPlayByThread(bean.iconUrl);
loader.disPlayByAsyncTask(holder.ivIcon, bean.iconUrl);
holder.tvTitle.setText(bean.title);
holder.tvContent.setText(bean.content);
return convertView;
}

class ViewHolder {
ImageView ivIcon;
TextView tvTitle;
TextView tvContent;
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
/**
* <ul>
* <li>如果当前是空闲状态,就去加载图片;</li>
* <li>如果当前是滑动状态,就停止所有的加载任务</li>
* </ul>
*/
if (scrollState == SCROLL_STATE_IDLE) {
loader.loadImages(start, end, view);
} else {
loader.cancelAllTasks();
}
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
start = firstVisibleItem;
end = firstVisibleItem + visibleItemCount;
if (firstIn && visibleItemCount > 0) {
// TODO:visibleItemCount > 0 这个条件不能少,不然预加载没效果!!!
// 首次进入页面,并且界面上面的Item已经加载出来了,才去加载一个屏幕Item的的图片
loader.loadImages(start, end, view);
firstIn = false;
}
}
}


Bean
的代码:

package com.duck.moocAsyncTask;

public class NewsBean {

public String iconUrl;
public String title;
public String content;
}


ImageLoader
的代码:

“`

package com.duck.moocAsyncTask;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Set;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.LruCache;
import android.widget.AbsListView;
import android.widget.ImageView;

public class ImageLoader {

private ImageView mImageView;
private String mUrl;

private LruCache<String, Bitmap> mCache;

private Set<LoadImageAsyncTask> mTasks;
public ImageLoader() {
mTasks = new HashSet<ImageLoader.LoadImageAsyncTask>();
long maxMemory = Runtime.getRuntime().maxMemory();
int maxSize = (int) (maxMemory / 4);
mCache = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}

public void addBitmapToCache(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}

public Bitmap getBitmapFromCache(String url) {
return mCache.get(url);
}

private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (mImageView.getTag().equals(mUrl)) {
mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
};
private AbsListView mListview;

protected Bitmap getBitmapFromUrl(String urlString) {
URLConnection connection = null;
InputStream is = null;
try {
connection = new URL(urlString).openConnection();
is = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);

return bitmap;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

public void disPlayByThread(ImageView imageView, final String url) {
mImageView = imageView;
mUrl = url;
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromUrl(url);
Message message = Message.obtain();
message.obj = bitmap;
handler.sendMessage(message);
}
}).start();
}

public void disPlayByAsyncTask(ImageView imageView, String url) {
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
// new LoadImageAsyncTask(imageView, url).execute(url);
// /加载图片,不再使用该方法。该方法仅仅用来显示图片
imageView.setImageResource(R.drawable.ic_launcher);
}
}

class LoadImageAsyncTask extends AsyncTask<String, Void, Bitmap> {

private ImageView mImageView;
private String mUrl;

// public LoadImageAsyncTask(ImageView imageView, String url) {
// mImageView = imageView;
// mUrl = url;
// }
public LoadImageAsyncTask(String url) {
mUrl = url;
}

@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = getBitmapFromUrl(url);
if (getBitmapFromCache(url) == null) {
addBitmapToCache(url, bitmap);
}
return bitmap;
}

@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
// if (mImageView.getTag().equals(mUrl)) {
// mImageView.setImageBitmap(result);
// }

ImageView imageView = (ImageView) mListview.findViewWithTag(mUrl);
if (imageView != null && result != null){
imageView.setImageBitmap(result);
}
//task执行结束了
mTasks.remove(this);//TODO: 容易忘记的一点
}
}

/**
* 加载从start 到end的所有图片
*
* @param start
* @param end
*/
public void loadImages(int start, int end, AbsListView listView) {
this.mListview = listView;
for (int i = start; i < end; i++) {
String url = NewsAdapter.URLS[i];
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap != null) {
ImageView imageView = (ImageView) listView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
} else {
LoadImageAsyncTask task = new LoadImageAsyncTask(url);
task.execute(url);
mTasks.add(task);
}
}
}

public void cancelAllTasks() {
if(mTasks!=null){
for (LoadImageAsyncTask task :mTasks) {
task.cancel(false);
}
}
}
}


简单介绍一个以上代码分别表达的意思:

Activity
就不用说了,就是显示ListView,获取数据,将数据放进
Adapter
,让
ListView
去显示获取到的网络数据。

Adapter
也没什么说的,就是将传递过来的数据集合
List
里面的数据放到每个
Item
里面去展示。不过需要注意的是:1.在
getView
方法里面去调用了
ImageLoader
的显示图片的方法,这个方法的作用就是将加载完成的图片显示出来,不涉及异步任务的操作。2.在
listView
OnScrollListener
的重写方法中,做了两个操作:一,.在滑动的状态,取消所有的,正在执行的异步任务,在非滑动状态,去加载当前屏幕所需要显示的图片。二,在首次加载出页面上ListView的条目的时候,去加载当前屏幕的这个条目需要的图片。

ImageLoader
的作用,就有点重要了。首先是里面有一个
Lrucache
,每次在加载到图片的时候,都会去将图片存放到
Lrucache
中;然后,
ImageLoader
中有一个
loadImages(int start, int end, AbsListView listView)
的方法,这个方法的左右就是去加载当前屏幕上,需要展示的所有图片。而加载图片是一个网络的操作,于是这里面的内部类
AsyncTask
的作用就是去网络获取图片。然后,为了不去加载已经加载过的图片,在
loadImages
里面会有一个判断,如果当前图片已经存在,就直接去显示到
ListView
对应的
ImageView
上面,如果,当前图片不在缓存中,就去开启异步任务去加载,顺便,将开启的任务放到
Task集合
中去(为什么要搞这么一个集合呢?因为在滑动的时候,需求停止所有的加载任务,就需要使用到这个集合)。最后,在这个内部类
AsyncTask
中,除了根据
url
去加载网络图片,将图片放入缓存之外,在
onPostExecute
方法中,还有去将图片显示到
ListView
对应的
ImageView
中去。并且,去
Task集合
中去移除当前的
Task
.

大体上,关于这个Demo的内容就这些了。完整项目下载

有不足之处,欢迎指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: