Android里的AsyncTask是一个很好用的异步加载数据的工具类,我们是可以定义一个抽象类,其继承于AsyncTask,可接收不同类型的参数。定义如下:
import android.annotation.WorkerThread;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.MainThread;
import android.util.Log;
import java.util.concurrent.Executor;
/**
* {@link AsyncTask} that defaults to executing on its own single threaded Executor Service.
*
* @param <Params> 执行时需要的参数,传给doInBackground使用.
* @param <Progress> the type of the progress units published during the background computation.
* @param <Result> doInBackground执行完后返回的数据类型,它会被系统作为参数传给
* onPostExecute。
*/
public abstract class AsyncDbTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private static final String TAG = "AsyncDbTask";
private static final boolean DEBUG = true;
private final Executor mExecutor;
boolean mCalledExecuteOnDbThread;
protected AsyncDbTask(Executor mExecutor) {
this.mExecutor = mExecutor;
}
@SafeVarargs
@MainThread
public final void executeOnDbThread(Params... params) {
mCalledExecuteOnDbThread = true;
executeOnExecutor(mExecutor, params);
}
public abstract static class LoadSkywayClientDBTask<Params, Progress, Result> extends AsyncDbTask<Params, Progress, Result> {
private final ContentResolver mContentResolver;
private final Uri mUri;
private final String[] mProjection;
private final String mSelection;
private final String[] mSelectionArgs;
private final String mOrderBy;
public LoadSkywayClientDBTask(Executor executor,
ContentResolver contentResolver,
Uri uri, String[] projection,
String selection,
String[] selectionArgs,
String orderBy) {
super(executor);
mContentResolver = contentResolver;
mUri = uri;
mProjection = projection;
mSelection = selection;
mSelectionArgs = selectionArgs;
mOrderBy = orderBy;
}
@Override
public Result doInBackground(Params... params) {
Log.d(TAG, "LoadSkywayClientDBTask's doInBackground called in " + Thread.currentThread().getName());
if (!mCalledExecuteOnDbThread) {
IllegalStateException e = new IllegalStateException(this + " should only be executed using executeOnDbThread, "
+ "but it was called on thread "
+ Thread.currentThread());
Log.w(TAG, e);
}
if (isCancelled()) {
// This is guaranteed to never call onPostExecute because the task is canceled.
return null;
}
if (DEBUG) {
Log.v(TAG, "Starting query for " + this);
}
try (Cursor c = mContentResolver.query(mUri, mProjection, mSelection, mSelectionArgs, mOrderBy)) {
if (c != null && !isCancelled()) {
Result result = onQuery(c);
if (DEBUG) {
Log.v(TAG, "Finished query for " + this);
}
c.close();
return result;
} else {
if (c == null) {
Log.e(TAG, "Unknown query error for " + this);
} else {
if (DEBUG) {
Log.d(TAG, "Canceled query for " + this);
}
c.close();
}
return null;
}
} catch (Exception e) {
Log.d("wujiang", "doInBackground: error = " + e.getMessage());
}
return null;
}
/**
* Return the result from the cursor.
*
* <p><b>Note</b> This is executed on the DB thread by {@link #doInBackground(Params...)}
*/
@WorkerThread
protected abstract Result onQuery(Cursor c);
@Override
public String toString() {
return this.getClass().getName() + "(" + mUri + ")";
}
}
}
假如我现在有个需求,要从一个数据库中加载数据,这个数据库存储数据的字段有channelid、name、logoUrl等等,现在只想查找这三个字段的数据,然后放到一个HaspMap里。我们可以定义一个LoadChannelListTask类,其继承于AsyncDbTask.LoadSkywayClientDBTask。
public class LoadChannelListTask extends AsyncDbTask.LoadSkywayClientDBTask<Void, Void, HashMap<String, SkywayChannelEntity>> {
protected LoadChannelListTask(Executor executor,
ContentResolver contentResolver,
Uri uri, String[] projection,
String selection,
String[] selectionArgs,
String orderBy) {
super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy);
}
@Override
protected void onPostExecute(HashMap<String, SkywayChannelEntity> skywayChannelEntityHashMap) {
Log.d("wujiang", "LoadChannelListTask's onPostExecute: call this fun in " + Thread.currentThread().getName());
mSkywayChannelList.clear();
if (skywayChannelEntityHashMap != null) {
mSkywayChannelList.putAll(skywayChannelEntityHashMap);
}
/** 打印 **/
/*Set<Map.Entry<String, SkywayChannelEntity>> setEntry = mSkywayChannelList.entrySet();
Iterator<Map.Entry<String, SkywayChannelEntity>> iterator = setEntry.iterator();
while (iterator.hasNext()) {
Map.Entry<String, SkywayChannelEntity> entry = iterator.next();
String channelId = entry.getKey();
SkywayChannelEntity skywayChannelEntity = entry.getValue();
Log.d("wujiang", "channelId = " + channelId);
Log.d("wujiang", "channelName = " + skywayChannelEntity.getChannelName());
Log.d("wujiang", "channelLogoUrl = " + skywayChannelEntity.getChannelLogoUrl());
}*/
}
@Override
protected HashMap<String, SkywayChannelEntity> onQuery(Cursor c) {
HashMap<String, SkywayChannelEntity> hashMap = new HashMap();
if (c != null) {
//Log.d("wujiang", "onQuery: count = " + c.getCount());
while (c.moveToNext()) {
if (isCancelled()) {
Log.d("wujiang", "LoadChannelListTask's onQuery: isCancelled ");
return hashMap;
}
String channelId = c.getString(c.getColumnIndex("channelid"));
String channelName = c.getString(c.getColumnIndex("name"));
String channelLogoUrl = c.getString(c.getColumnIndex("logoUrl"));
SkywayChannelEntity skywayChannelEntity = new SkywayChannelEntity();
skywayChannelEntity.setChannelId(channelId);
skywayChannelEntity.setChannelName(channelName);
skywayChannelEntity.setChannelLogoUrl(channelLogoUrl);
hashMap.put(channelName, skywayChannelEntity);
}
}
if (hashMap == null) {
Log.d("wujiang", "LoadChannelListTask's onQuery: hashMap = null ");
}
return hashMap;
}
}
SkywayChannelEntity.java如下:
public class SkywayChannelEntity {
String channelId;
String channelName;
String channelLogoUrl;
public SkywayChannelEntity() {
}
public SkywayChannelEntity(String channelId, String channelName, String channelLogoUrl) {
this.channelId = channelId;
this.channelName = channelName;
this.channelLogoUrl = channelLogoUrl;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getChannelName() {
return channelName;
}
public void setChannelName(String channelName) {
this.channelName = channelName;
}
public String getChannelLogoUrl() {
return channelLogoUrl;
}
public void setChannelLogoUrl(String channelLogoUrl) {
this.channelLogoUrl = channelLogoUrl;
}
}
AsyncDbTask.LoadSkywayClientDBTask<Void, Void, HashMap<String, SkywayChannelEntity>>里,第一个Void表示不需要任何参数,第二个Void表示不需要显示进度,HashMap<String, SkywayChannelEntity>表示doInBackground的返回类型,即onPostExecute形参类型。这里没有实现doInBackground方法,是因为我们这里查询时没有设置一些特定的过滤条件(稍后会介绍设置过滤条件的例子),那执行时就调用了父类的doInBackground。
那怎么使用LoadChannelListTask呢?代码如下:
private ExecutorService DB_EXECUTOR = Executors.newSingleThreadExecutor(new NamedThreadFactory("query-skywayclient-db"));
private static final String[] SKYWAY_CHANNEL_LIST_PROJECTION = {
"channelid",
"name",
"logoUrl"
};
public void loadChannelListFromSkywayClient() {
Log.d("wujiang", "loadChannelListFromSkywayClient: call this fun in " + Thread.currentThread().getName());
LoadChannelListTask loadChannelListTask = new LoadChannelListTask(DB_EXECUTOR,
mContext.getContentResolver(),
SkyWayClientUtils.CHANNELLIST_URI,
SKYWAY_CHANNEL_LIST_PROJECTION,
null, null, null);
loadChannelListTask.executeOnDbThread();
}
SkyWayClientUtils.CHANNELLIST_URI为数据库的URI,不用多说;SKYWAY_CHANNEL_LIST_PROJECTION就是要查询的字段。这样执行完成后,数据就保存到了mSkywayChannelList里。
OK,我们继续。
假如有个数据库,里面存储了很多EPG的数据,多的时候几万条。何为EPG?简单点说,就是指的一个电视台的时刻表,即几点到几点播放什么内容,比如CCTV1 19:00-19:30播放新闻联播,19:30-20:30播放动物世界,CCTV5 19:00-19:30 播放什么,19:30-20:00播放什么,等等。那么我们数据库里可能就存储了很多台从今天开始的7天内的EPG信息。现在有需求,需要查询一些指定台指定时间范围内的EPG数据,如我想查找CCTV1、CCTV3、CCTV5、深圳卫视,湖南卫视等诸多台今天的EPG信息。我这里就定义了一个类:LoadChannelProgramsTask。
private HashMap<Integer, ArrayList<SkywayProgramEntity>> mChannelsProgramMap;
public class LoadChannelProgramsTask extends AsyncDbTask.LoadSkywayClientDBTask<QueryParams, Void, Boolean> {
protected LoadChannelProgramsTask(Executor executor,
ContentResolver contentResolver,
Uri uri, String[] projection,
String selection,
String[] selectionArgs,
String orderBy) {
super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy);
}
@Override
protected Boolean onQuery(Cursor c) {
return true;
}
@Override
public Boolean doInBackground(QueryParams... queryParams) {
boolean ret = false;
long load_db_start_time = System.currentTimeMillis();
int startTime = queryParams[0].getStartTime();
int endTime = queryParams[0].getEndTime();
ArrayList<Integer> channelIdList = queryParams[0].getQueryChannels();
//Log.d("wujiang", "LoadChannelProgramsTask's doInBackground: thread name = " + Thread.currentThread().getName());
//Log.d("wujiang", "LoadChannelProgramsTask's doInBackground: startTime = " + startTime);
//Log.d("wujiang", "LoadChannelProgramsTask's doInBackground: endTime = " + endTime);
for (Integer channel_lcn : channelIdList) {
ArrayList<SkywayProgramEntity> programArrayList = queryOneChannelPrograms(channel_lcn, startTime, endTime);
Log.d("wujiang", "LoadChannelProgramsTask's doInBackground: channel lcn = " + channel_lcn);
Log.d("wujiang", "LoadChannelProgramsTask's doInBackground: programArrayList = " + programArrayList.size());
mChannelsProgramMap.remove(channel_lcn);
mChannelsProgramMap.put(channel_lcn.intValue(), programArrayList);
if (programArrayList.size() > 0) {
ret = true;
}
}
Log.d(TAG, "doInBackground: spend time = " + (System.currentTimeMillis() - load_db_start_time));
return ret;
}
@Override
protected void onPostExecute(Boolean retValue) {
//Log.d("wujiang", "LoadChannelProgramsTask's onPostExecute: thread name = " + Thread.currentThread().getName());
Log.d("wujiang", "LoadChannelProgramsTask's onPostExecute: retValue = " + retValue);
if (!retValue) {
Log.d("wujiang", "LoadChannelProgramsTask's onPostExecute: 没有查询到数据,不需要更新UI");
return;
}
for (QueryFinishListener listener : mListenerList) {
listener.queryFinishCallback();
}
}
}
/**
* 查询节目从startTime开始的epg信息
* @param channel_lcn
* @param startTime 单位秒
* @param endTime 单位秒
* @return
*/
private ArrayList<SkywayProgramEntity> queryOneChannelPrograms(int channel_lcn, int startTime, int endTime) {
ArrayList<SkywayProgramEntity> programsList = new ArrayList<>();
programsList.clear();
SkywayProgramEntity lastReadProgram = null;
Cursor cursor = null;
try {
cursor = mContext.getContentResolver().query(
SkyWayClientUtils.CHANNEL_EPG_URI,
null,
SkywayProgramEntity.LCN_FILED + "=? and " + SkywayProgramEntity.STARTTIME_INTEGER_FILED + ">=?",
new String[]{String.valueOf(channel_lcn), String.valueOf(startTime)},
SkywayProgramEntity.STARTTIME_INTEGER_FILED);
if (cursor != null && cursor.getCount() > 0) {
Log.d("wujiang", "queryOneChannelPrograms: channel_lcn = " + channel_lcn);
Log.d("wujiang", "queryOneChannelPrograms: cursor.getCount() = " + cursor.getCount());
cursor.moveToFirst();
while(!cursor.isAfterLast()) {
// to do your things
cursor.moveToNext();
}
}
} finally {
try {
if (null != cursor && !cursor.isClosed()) {
cursor.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return programsList;
}
public class QueryParams {
private int startTime;
private int endTime;
private ArrayList<Integer> queryChannels;
public QueryParams(int startTime, int endTime, ArrayList<Integer> queryChannels) {
this.startTime = startTime;
this.endTime = endTime;
this.queryChannels = queryChannels;
}
public int getStartTime() {
return startTime;
}
public int getEndTime() {
return endTime;
}
public ArrayList<Integer> getQueryChannels() {
return queryChannels;
}
}
这里查询数据库时指定了条件,startTime表示EPG开始时间,endTime表示EPG的结束时间,queryChannels表示要查询的节目。所以AsyncDbTask.LoadSkywayClientDBTask<QueryParams, Void, Boolean>的第一个参数就表示过滤的参数,第三个参数表示有没有查询到数据。和第一个例子不同,这里实现了doInBackground,那是因为我们需要接收QueryParams参数。执行完成后查询到的EPG数据就保存在了mChannelsProgramMap里。
怎么使用LoadChannelProgramsTask?代码如下:
/**
*
* @param startTime 秒单位
* @param endTime 秒单位
* @param queryLcnArrayList
*/
public void loadProgramOfChannelsFromSkywayClient(int startTime, int endTime, ArrayList<Integer> queryLcnArrayList) {
QueryParams queryParams = new QueryParams(startTime, endTime, queryLcnArrayList);
LoadChannelProgramsTask loadChannelProgramsTask = new LoadChannelProgramsTask(DB_EXECUTOR,
mContext.getContentResolver(),
SkyWayClientUtils.CHANNEL_EPG_URI, null, null, null, null);
loadChannelProgramsTask.executeOnDbThread(queryParams);
}
到此就介绍完了,感觉怎么样呢?需要您的评价,谢谢!
THE END