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

安卓(Android)中如何优雅的 创建/执行 异步任务/回调(AsyncTask 、Thread、Job、CallBack、Handler) ?线程池(Executor)

2016-06-25 09:56 1306 查看
        在安卓的开发过程中,我们时常需要写一些异步任务,比如网络请求,文件压缩,数据库访问等等,这些操作一般都需要放到子线程进行执行。但是,如果每一个异步操作都 new 一个thread去执行,会造成大量的资源消耗,而且不易管理,同时容易造成 内存泄露,而
Android 本身提供的AsyncTask同样难用,并且
不同版本的 Android
API 实现也是 不同的,所以为了更好的管理我们的异步任务,必须找到更佳的解决方案!

1、我们到底需要一个什么样的异步框架? 理想的情况是这样的,我有一个异步任务需求,然后我只需要

Add一下,然后框架去管理执行这个异步任务,完事之后把数据回调(callback)给我,我拿数据去更新UI,其他的事一律不管。并且这个框架要足够简单,灵活,内存占用低,没有任务时不消耗或者是很少消耗资源。

2网上有没有现成的框架可以用,为什么自己写? 有很多,我上github搜了一下,有好多还不错的框架,比如
 https://github.com/path/android-priority-jobqueue   和   https://github.com/yigit/android-priority-jobqueue 都不错,功能很强大,但是都相对比较大,不够简洁,所以我决定自己写。

3、写的时候具体思路是怎样的? 就如上面提到的那样,要足够易用,代码足够少,足够灵活,显然Thread不合适,AsyncTask
也不合适,那么只有用线程池(Executor )了。

4、是如何实现的?定下了初步方针,现在就需要实现了,废话不多说,直接上我的项目地址:

https://github.com/RockySteveJobs/TaskManager 

使用步骤是直接从github上复制过来的。


How to use?


1、first step:

copy the file "ExcuteTaskManager" and "ExcuteTask"  to your project


2、second step:

init the library in your application or activity

ExcuteTaskManager.getInstance().init();


3、third step:

write a file extends "ExcuteTask" (eg: JsonExcuteTask)


4、fourth step:

ExcuteTaskManager.getInstance().newExcuteTask(new JsonExcuteTask());

but if you want callback , please call the method getData()

ExcuteTaskManager.getInstance().getData(new JsonExcuteTask(),
new ExcuteTaskManager.GetExcuteTaskCallback()
{
public void onDataLoaded(ExcuteTask task)
{
//data has int the task, so you can update your ui;
}
});

核心代码(Core code)

package com.rocky.eagle.task;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.rocky.eagle.utils.LogUtils;

import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* Start
* <p/>
* User:Rocky(email:1247106107@qq.com)
* Created by Rocky on 2016/6/15  21:51
* PACKAGE_NAME com.rocky.eagle.activity
* PROJECT_NAME TaskManager
* TODO:
* Description:
*
* 使用的时候务必请先调用init()方法,否则不能使用
*
* 用最少的代码做尽可能多的事情
*
* Done
*/
public class ExcuteTaskManager implements Runnable {

/**
* 线程执行完事儿后默认的回调类型
*/
private static final int COMMON_EXCUTE_TASK_TYPE = 0;
/**
* 线程开关
*/
public volatile boolean isRunning = false;
/**
* 是否初始化完成的开关
*/
private boolean isHasInit = false;
/**
* 默认线程池的线程数量
*/
private static final int DEFAULT_THREAD_NUM = 5;
/**
* 初始化时的线程数量
*/
private int threadNum = DEFAULT_THREAD_NUM;
/**
* 定义一个单线程的线程池,专门用来执行耗时且不需要回调的操作
*/
private static ScheduledExecutorService singlePool = null/*Executors.newSingleThreadScheduledExecutor()*/;
/**
* 定义一个大小为5的线程池(这个我们比较适合多个图片下载时使用)
*/
private static ExecutorService threadPool = null/*Executors.newFixedThreadPool(threadNum)*/;
/**
* 任务执行队列
*/
private static ConcurrentLinkedQueue<ExcuteTask> allExcuteTask = null/*new ConcurrentLinkedQueue<ExcuteTask>()*/;
/**
* 回调接口列表
*/
private static ConcurrentHashMap<Integer, Object> uniqueListenerList = null/*new ConcurrentHashMap<String, Object>()*/;

public Handler getHandler() {
return handler;
}

public int getThreadNum() {
return threadNum;
}

public boolean isHasInit() {
return isHasInit;
}

public boolean isRunning() {
return isRunning;
}

/**
* @author Rocky
* @desc 得到普通的 ExcuteTask 对象,
* 对外界开放的回调接口
*/
public interface GetExcuteTaskCallback {
void onDataLoaded(ExcuteTask task);
}

/**
* 直接把数据发送到主线程
*/
private static Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
long start = System.currentTimeMillis();
if (msg != null && msg.obj != null
&& msg.obj instanceof ExcuteTask) {
switch (msg.what) {
case COMMON_EXCUTE_TASK_TYPE:
ExcuteTaskManager.getInstance().doCommonHandler((ExcuteTask) msg.obj);
break;
/** 如果想要添加其他类型的回调,可以在此加入代码*/
}
}
long end = System.currentTimeMillis();

LogUtils.i(" handleMessage 总共消耗时间为:" + (end - start));
}
};

private static ExcuteTaskManager instance = null;

private ExcuteTaskManager() {
LogUtils.i("private ExcuteTaskManager() { 当前的线程Id为:" + Thread.currentThread().getId());
}

public static ExcuteTaskManager getInstance() {
if (instance == null) {
synchronized (ExcuteTaskManager.class) {
if (instance == null) {
instance = new ExcuteTaskManager();
}
}
}
return instance;
}

/**
* 初始化操作,这个主要是初始化需要执行异步
* 回调任务的线程池,默认开启5个线程
*/
public void init() {
init(threadNum);
}

/**
* 初始化操作,这个主要是初始化需要执行异步
* 回调任务的线程池,可以传入线程的个数
*/
public synchronized void init(int initNum) {
if (!isHasInit) {
/**
* 初始化之后就相当于开始了线程次的运行
* 只不过如果没有任务处于等待状态
*/
isRunning = true;
if (initNum > 0) {
threadNum = initNum;
}
threadPool = Executors.newFixedThreadPool(threadNum);
singlePool = Executors.newSingleThreadScheduledExecutor();
allExcuteTask = new ConcurrentLinkedQueue<>();
uniqueListenerList = new ConcurrentHashMap<>();

/**
* 初始化需要用到的线程
*/
for (int i = 0; i < threadNum; i++) {
threadPool.execute(this);
}
isHasInit = true;
} else {
LogUtils.d("ExcuteTaskManager 已经初始化完成,不需要重复初始化");
}
}

/**
* 当应用被销毁时,执行清理操作
*/
public void doDestory() {
/**
* 关闭线程开关
*/
isRunning = false;
isHasInit = false;
if (allExcuteTask != null) {
allExcuteTask.clear();
allExcuteTask = null;
}
if (uniqueListenerList != null) {
uniqueListenerList.clear();
uniqueListenerList = null;
}
if (threadPool != null) {
threadPool.shutdown();
threadPool = null;
}
if (singlePool != null) {
singlePool.shutdown();
singlePool = null;
}
}

/**
* 向任务队列中添加任务对象,添加成功后,
* 任务会自动执行,执行完事儿后,不进行任何回调操作
*
* @param task 可执行的任务对象
*/
public void newExcuteTask(ExcuteTask task) {
if (task != null) {
allExcuteTask.offer(task);
LogUtils.i("ExcuteTaskManager 添加任务成功之后" + "allExcuteTask.size()=" + allExcuteTask.size());
long timeOne = System.currentTimeMillis();
synchronized (allExcuteTask) {
allExcuteTask.notifyAll();
LogUtils.i("ExcuteTaskManager =====>处于唤醒状态");
}
long timeTwo = System.currentTimeMillis();
LogUtils.i("ExcuteTaskManager唤醒线程所消耗的时间为:" + (timeTwo - timeOne));
} else {
LogUtils.w("ExcuteTaskManager====您添加的ExcuteTask为空,请重新添加");
}
}

/**
* 这个方法主要是获取普通的回调数据,
* 获取成功后会把加入的 ExcuteTask 对象回调到用户界面
*
* @param task     加入的任务Task
* @param callback 任务的回调接口GetDataCallback
*/
public void getData(ExcuteTask task, GetExcuteTaskCallback callback) {
/**
*  把CallBack 接口加入列表中,用完之后移除
*/
try {
if (task != null && callback != null) {
LogUtils.i("callback的hashcode为:" + callback.hashCode() + "task的hashcode为:" + task.hashCode());
if (task.getUniqueID() == 0) {
task.setUniqueID(task.hashCode());
}
uniqueListenerList.put(task.getUniqueID(), callback);

/**
* 开始加入任务,执行任务
*/
newExcuteTask(task);
} else {
LogUtils.w("Task 或者是 GetDataCallback 为空了,请检查你添加的参数!");
}
} catch (Exception e) {
// TODO: handle exception
/**
* 其实,这个地方的数据应该写到一个文件中
*/
LogUtils.e("ExcuteTaskManager========getData=====" + e.toString() + " thread id 为:" + Thread.currentThread().getId());
e.printStackTrace();
}
}

/**
* 从任务队列中移除任务对象,使其不再执行(如果任务已经执行,则此方法无效)
*
* @param task 添加的任务对象
*/
public void removeExcuteTask(ExcuteTask task) {
if (task != null) {
uniqueListenerList.remove(task.getUniqueID());
allExcuteTask.remove(task);
} else {
LogUtils.w("ExcuteTaskManager====您所要移除的任务为null,移除失败");
}
}

/**
* 清除所有的任务
*/
public void removeAllExcuteTask() {
allExcuteTask.clear();
uniqueListenerList.clear();
}

/**=================================任务执行、回调、分发start============================================*/

/**
* 所有的异步任务都在此执行
*/
@Override
public void run() {
// TODO Auto-generated method stub
while (isRunning) {

LogUtils.i("ExcuteTaskManager====准备开始执行任务 总任务个数为  allExcuteTask.size()=" + allExcuteTask.size());

/**
* 从allExcuteTask取任务
*/
ExcuteTask lastExcuteTask = allExcuteTask.poll();

LogUtils.i("ExcuteTaskManager====从allExcuteTask取出了一个任务  allExcuteTask.size()=" + allExcuteTask.size());
if (lastExcuteTask != null) {
try {
LogUtils.i("ExcuteTaskManager任务ID" + lastExcuteTask.getUniqueID());
/**
* 真正开始执行任务,
* 所有的耗时任务都是在子线程中执行
*/
doExcuteTask(lastExcuteTask);
} catch (Exception e) {
// TODO: handle exception
LogUtils.e("ExcuteTaskManager=====>执行任务发生了异常,信息为:" + e.getMessage());
e.printStackTrace();
}
LogUtils.i("任务仍在执行,ExcuteTaskManager线程处于运行状态,当前的线程的ID为:" + Thread.currentThread().getId());
} else {
LogUtils.i("任务执行完毕,ExcuteTaskManager线程处于等待状态,当前的线程的ID为:" + Thread.currentThread().getId());
try {
synchronized (allExcuteTask) {
allExcuteTask.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
LogUtils.e("ExcuteTaskManager=====>  线程等待时发生了错误,信息为:" + e.getMessage());
e.printStackTrace();
}
}
}
}

/**
* 根据不同的ExcuteTask,执行相应的任务
*
* 这个是真正开始执行异步任务的地方,
* 即调用需要在子线程执行的代码==>task.doTask()
*
* @param task ExcuteTask对象
*/
private void doExcuteTask(ExcuteTask task) {

ExcuteTask result = task.doTask();

/**
*
* 开始执行的Task和最后得到的Task是同一个的时候,才会进行回调,
* 否则不进行回调(保证在回调得到数据的时候知道是哪一个Task,以便进行强转)
*
*
* 没有UniqueID相当于不需要回调
*
*/
if (result != null && task == result && result.getUniqueID() != 0) {
/**
*  发送当前消息,更新UI(把数据回调到界面),
*  下面不用做任何的发送消息,
*  只在这一个地方发送就行,否者会发生错误!
*/

Message msg = Message.obtain();
msg.what = COMMON_EXCUTE_TASK_TYPE;
msg.obj = result;
handler.sendMessage(msg);
} else {
uniqueListenerList.remove(task.getUniqueID());
}
}

/**
* 真正的回调操作,所有的任务在这里
* 把数据回调到主界面
*
* @param task ExcuteTask对象
*/
private void doCommonHandler(ExcuteTask task) {
long start = System.currentTimeMillis();
LogUtils.i("已经进入了private void doCommonHandler(Message msg) {");
if (task != null && uniqueListenerList.containsKey(task.getUniqueID())) {
try {
/**
* 回调整个Task数据
* 然后可以回调方法中去直接更新UI
*/
((GetExcuteTaskCallback) uniqueListenerList.get(task.getUniqueID())).onDataLoaded(task);
/**
* 回调完成移除CallBack对象
*/
uniqueListenerList.remove(task.getUniqueID());
LogUtils.i("TaskManager========doCommonHandler=====回调成功====task 为:" + task.toString());
} catch (Exception e) {
// TODO: handle exception
LogUtils.e("TaskManager========doCommonHandler=====if (uniqueListenerList.containsKey(task.getUniqueID())) {" + e.toString());
e.printStackTrace();
}
} else {
LogUtils.i("TaskManager========doCommonHandler=====已经移除了回调监听");
}
long end = System.currentTimeMillis();
LogUtils.i("执行回调doCommonHandler 耗时:" + (end - start));
}
/**=================================任务执行、回调、分发end============================================*/

/**=================================单线程池,可以顺序,延迟执行一些任务start============================================*/

/**
* 顺序执行耗时的操作
*
* @param runnable 对象
*/
public void excute(Runnable runnable) {
singlePool.execute(runnable);
}

/**
* 顺序执行耗时的操作
*
* @param runnable 对象
* @param delay    延迟执行的时间,单位毫秒
*/
public void excute(Runnable runnable, long delay) {
singlePool.schedule(runnable, delay, TimeUnit.MILLISECONDS);
}

/**
* 顺序执行耗时的操作
*
* @param runnable 对象
* @param delay    延迟执行的时间
* @param timeUnit 时间单位
*/
public void excute(Runnable runnable, long delay, TimeUnit timeUnit) {
singlePool.schedule(runnable, delay, timeUnit);
}

public void scheduleAtFixedRate(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
singlePool.scheduleAtFixedRate(runnable, delay, period, timeUnit);
}

public void scheduleAtFixedRate(Runnable runnable, long delay, long period) {
singlePool.scheduleAtFixedRate(runnable, delay, period, TimeUnit.MILLISECONDS);
}

public void scheduleAtFixedRate(Runnable runnable, long period) {
singlePool.scheduleAtFixedRate(runnable, 0, period, TimeUnit.MILLISECONDS);
}
/**=================================单线程池,可以顺序,延迟执行一些任务end============================================*/
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息