Android应用设计之实现多线程框架
做了Android开发满2年了,感觉在开发中用的很多的就是多线程了;由于在现代的计算机中CPU核数越来越多,因此操作系统底层就向多线程方向发展。因此为了跟上时代,开篇就将平时用的比较多的多线程开发原理吧。
本文需要掌握一定的Android基础;比如Handler,Message,Looper等的关系;还需要掌握一些Java线程池的知识,方能更好的理解本文。
架构是本人根据实际开发经验悟出,因此有不完善的地方,希望大家指正。共同学习进步才是我的目的。
首先提出几个问题:
一:多线程,既然是多线程,就是多个运行的程度,那么是不是需要一个可以管理这些的策略。要不太多了,占用资源太多,系统就会变卡。
二:Activity启动只是在他自己的ActivityThread里面,因此启动其他的线程必须在主线程(ActivityThread)里面启动的。因此还有一个主线程和这个线程的交互问题。
三:手机只有一个,因此必定存在线程之间的竞争关系;比如同时读写一个文件;同时等待一个条件;这里面还牵扯到线程的释放问题;如何安全的结束一个线程。同时竞争CPU资源等。
四:最困难的是多线程之间互相等待的问题,比如这个线程等待一个条件;而另一个线程也等待一个条件;如果这个条件永远无法满足,那么就会出现线程饿死问题。
本文就是为了探索多线程在Android APP中的应用;我尽量为大家展示一种简单易懂的,不容易出现问题的设计多线程程序的思路来发挥多线程的优势,而避免多线程带来的不足。
既然是框架,那么解决的就是通用问题;因此本文主要围绕如何管理线程,如何为使用者提供线程,如何最大量的复用已有的线程池,如何在线程之间交互进行一个框架。
第一个出场的就是线程池对象:
java.util.concurrent.ExecutorService
这个对象就是线程池对象:
我们先将线程池的创建和使用接口分开设计,分别设计两个类;
线程池的创建管理类:
核心思想是利用Map集合来管理已经创建的线程池:
/**
*
* @author 徐晔
* @note 获取指定模式的线程池对象
*/
public final class XYTPoolCreater {
public final static int CachedThreadPool=0;
public final static int FixedThreadPoo=1;
public final static int ScheduledThreadPool=2;
public final static int SingleThreadScheduledExecutor=3;
private Map<String, ExecutorService> executorServicemap;
private Map<String, ScheduledExecutorService> scheduledExecutorServicemap;
public static XYTPoolCreater getInstance = new XYTPoolCreater();
private XYTPoolCreater() {
executorServicemap = new HashMap<String, ExecutorService>();
scheduledExecutorServicemap = new HashMap<String, ScheduledExecutorService>();
}
/***
* 创建新的线程池
* * @param typeid
* @param 0:返回一个带缓存的线程池,该池在必要的时候创建线程,在线程空闲60秒后终止线程
* @param 1:返回一个线程池,该池中的线程数由参数指定
* @param 2:返回一个执行器,它在一个单一的线程中依次执行各个任务
***************************
* @param corepoolsize
* :线程池中线程的数目
* @param key
* @param typeid
* @param corepoolsize
* @param factory
*/
public synchronized ExecutorService creatnewExecutorService(String key, int typeid,
int corepoolsize, ThreadFactory factory) {
ExecutorService service = null;
boolean flag = false;
if (key == null) {
flag = true;
return service;
}
for (String oldkey : executorServicemap.keySet()) {
if (key.equals(oldkey)) {
service = executorServicemap.get(oldkey);
flag = true;
break;
}
}
if (!flag) {
service = getExecutorService(typeid, corepoolsize, factory);
executorServicemap.put(key, service);
}
return service;
}
/**
* 获取已经存在的ExecutorService
*
* @param key
* @return
*/
public ExecutorService getExecutorService(String key) {
ExecutorService service = null;
if (key != null) {
service = executorServicemap.get(key);
}
return service;
}
/**
* 创建新的周期性任务池
*
* @param key
* @param typeid
* @param corepoolsize
* @param factory
*/
public synchronized ScheduledExecutorService creatnewScheduledExecutorService(
String key, int typeid, int corepoolsize, ThreadFactory factory) {
ScheduledExecutorService service = null;
boolean flag = false;
if (key == null) {
flag = true;
return service;
}
for (String oldkey : scheduledExecutorServicemap.keySet()) {
if (key.equals(oldkey)) {
service = scheduledExecutorServicemap.get(oldkey);
flag = true;
break;
}
}
if (!flag) {
service = getScheduledExecutorService(typeid, corepoolsize, factory);
scheduledExecutorServicemap.put(key, service);
}
return service;
}
/**
* 获取以及存在的ScheduledExecutorService
*
* @param key
* @return
*/
public ScheduledExecutorService getScheduledExecutorService(String key) {
ScheduledExecutorService service = null;
if (key != null) {
service = scheduledExecutorServicemap.get(key);
}
return service;
}
/**
* 创建线程池的工厂
*
* @param typeid
* @param XYTPool.CachedThreadPool:返回一个带缓存的线程池,该池在必要的时候创建线程,在线程空闲60秒后终止线程
* @param XYTPool.FixedThreadPoo:返回一个线程池,该池中的线程数由参数指定
***************************
* @param corepoolsize
* :线程池中线程的数目
*/
private ExecutorService getExecutorService(int typeid, int corepoolsize,
ThreadFactory factory) {
ExecutorService mService = null;
if (corepoolsize < 1) {
corepoolsize = 1;
}
if (typeid < 0 || typeid > 2) {
typeid = 0;
}
switch (typeid) {
case CachedThreadPool:
if (factory != null) {
mService = Executors.newCachedThreadPool(factory);
} else {
mService = Executors.newCachedThreadPool();
}
break;
case FixedThreadPoo:
if (factory != null) {
mService = Executors.newFixedThreadPool(corepoolsize, factory);
} else {
mService = Executors.newFixedThreadPool(corepoolsize);
}
break;
}
return mService;
}
/**
* 预定执行或者重复执行的线程池接口
*
* @param typeid
* @param XYTPool.ScheduledThreadPool:返回一个线程池,它使用给定的线程数来调度任务
* @param XYTPool.SingleThreadScheduledExecutor1:返回一个执行器,它在一个单线程中调度任务
*/
private ScheduledExecutorService getScheduledExecutorService(int typeid,
int corepoolsize, ThreadFactory factory) {
if (corepoolsize < 1) {
corepoolsize = 1;
}
ScheduledExecutorService mService = null;
if (typeid > 1 || typeid < 0) {
typeid = 0;
}
switch (typeid) {
case ScheduledThreadPool:
if (factory != null) {
mService = Executors.newScheduledThreadPool(corepoolsize,
factory);
} else {
mService = Executors.newScheduledThreadPool(corepoolsize);
}
break;
case SingleThreadScheduledExecutor:
if (factory != null) {
mService = Executors.newSingleThreadScheduledExecutor(factory);
} else {
mService = Executors.newSingleThreadScheduledExecutor();
}
break;
}
return mService;
}
}
接下来我们需要设计几个接口以满足线程池中添加任务的需求:
/**
* @author 徐晔
* 异步线程执行之前需要执行的方法
*/
public interface XYdoBefore {
/**在执行之前*/
public void before(int taskid);
}
/**
*
@author 徐晔
* @param <V>
* @note 执行异步任务得到的回调接口
*/
public interface XYgetresult<V> {
/**
* 执行完成,得到返回结果
* @param <V>
*/
public void onfinish(int id, V v);
}
/**
*
* @author 徐晔
* @note 执行没有返回参数的异步任务方法
*/
public interface XYInBack {
/**在异步线程中执行的方法体*/
public void doinbackground();
}
/**
*
* @author 徐晔
* @param <V>
* @note 执行有返回参数的异步任务方法
*/
public interface XYInBackwithResult<V> {
/**在异步线程中执行的方法体
* @param <V>*/
public V doinbackground();
}
以上四个是我简单
设计的执行异步任务的方法接口,里面有简单的标识id,有简单的值字段来代表参数。
下面是我具体的给线程池提交参数的类:
/**
* @author 徐晔
* @note 线程池配置类
*/
public final class XYTPool {
/**
* @param <V>
* 进行有返回参数异步任务
* @throws Exception
* @throws
*/
public static <V> void dobackWithResult(final ExecutorService pool,
final int id, final XYInBackwithResult<V> dInbackground,
final XYgetresult<V> getResult){
pool.submit(new Runnable() {
@Override
public void run() {
getResult.onfinish(id, dInbackground.doinbackground());
}
});
}
/**
* 执行没有返回参数的任务
*
* @param pool
* @param id
* @param dInbackground
* @throws Exception
*/
public static void dobackWithoutResult(final ExecutorService pool,
final XYInBack dInbackground){
pool.submit(new Runnable() {
@Override
public void run() {
dInbackground.doinbackground();
}
});
}
/**
* 执行定期任务
* @param <V>
*
* @param typeid
* @param 0:预定在指定时间之后执行任务(1次)
* @param 1:预定在初始的指定时间之后周期性的执行任务,周期是delay
* @param 2:预定在初始的指定时间之后周期性的执行任务,在一次调用完成和下一次调用开始之间有长度为delay的延迟
*
*/
public static <V> void schedule(final ScheduledExecutorService service,
int typeid, final XYInBackwithResult<V> task,
long initialdelay, long delay, TimeUnit unit) {
if (typeid < 0 || typeid > 2) {
typeid = 0;
}
switch (typeid) {
case 0:
service.schedule(new Runnable() {
@Override
public void run() {
task.doinbackground();
}
}, delay, unit);
break;
case 1:
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
task.doinbackground();
}
}, initialdelay, delay, unit);
break;
case 2:
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
task.doinbackground();
}
}, initialdelay, delay, unit);
break;
}
}
/**
* 关闭服务
*/
public static void shutdown(ExecutorService service) {
if (service != null && !service.isShutdown()) {
service.shutdown();
}
}
}
好了,有了这么几个,简单的线程池管理类,创建类和实现接口对象都有了;
稍微完善一下就可以直接用以上三个类来实现异步任务管理了;但是在Android中,我们还需要实现线程之间值的传递;如下
我设计一个通用的Task类:
/**
* @param
* @param handler:
* 推荐直接传递 new Handler(Looper);
* @param before
* :执行异步线程之前执行的方法
* @param getResult
* :得到异步任务执行结果得到的线程 可以完成大部分的任务,类似于异步请求网络,异步查询数据库,普通的异步任务都可以继承该类来进行实现
*/
public abstract class XYAsynBaseTask implements XYgetresult,
XYdoBefore,XYInBackwithResult {
private XYgetresult mResult;
private XYdoBefore before;
private Handler handler;
/**
* @param mLooper
* :Loop对象必须在调用前初始化好,
* 比如Looper.prepareMainLooper()(Activity主线程);或者Looper.prepare();
* 调用之后必须执行Looper.loop();方法
* @param before
* :执行异步线程之前执行的方法
* @param getResult
* :得到异步任务执行结果得到的线程
*/
public XYAsynBaseTask(Handler handler, XYdoBefore before,XYgetresult<V> mResult) {
this.handler=handler;
this.before = before;
this.mResult=mResult;
}
public void execute(int id) throws Exception {
XYAsynBaseTask.this.before.before(id);
XYTPool.dobackWithResult(XYTPoolCreater.getInstance
.creatnewExecutorService(XYAsynConsts.HTTPTHREAD_KEY, 1, 5,
null), id, this, this);
}
@Override
public void onfinish(final int id,final V v) {
handler.post(new Runnable() {
@Override
public void run() {
mResult.onfinish(id, v);
}
});
}
@Override
public abstract V doinbackground();
}
我们在这个基类里通过Android 自己的Handler机制实现了线程之间的交互;执行了线程运行之前需要执行的方法,线程运行得到结果后,将结果返回给Handler所在的线程;为什么传递参数用Handler,开始用Looper,然后new一个Handler对象,但是后面感觉这样貌似浪费了很多Handler对象,所以本人觉得还是传递Handler的好,在主线程new一次;将线程体中需要执行的方法延迟到子类实现;有一点控制反转的意思;
看到这里,一个多线程的框架就此完成。至于如何使用,我相信看懂的人没有不会使用的。
写到最后,这个只是一个简单的多线程框架,用了Runnable对象,无法跟踪线程的执行。如果想要保持线程的跟踪,可以使用Callable
对象和FutureTask对象来实现;但是要注意阻塞,本人不建议用FutureTask来得到结果,因为那个获取结果的方法是一个阻塞式方法;本文抛砖引玉,希望可以给大家提供一个思路。至于最上面提到的问题,就看大家各自的设计能力了。