开源一套简易线程调度库
2017-08-15 本文已影响36人
Whyn
背景
阅读开源库的一大好处就是不仅可以了解功能实现的逻辑,而且对于某些实现代码,经常给人醍醐灌顶的感受。ThreadDispatcher 的产生就是由于笔者在阅读了Butterknife,EventBus 和 Retrofit 后,一时兴(chong)起(dong)写的(●'◡'●).
用途
- 简易代码调用实现 代码块/接口方法/普通方法 运行在特定的线程中;
- 支持接口方法通过注解指定运行线程(实现类该方法都会运行在注解指定的线程中);
- 支持注解指定函数运行线程;
- 支持通过注解为函数指定 别名,后续可通过别名进行函数调用;
- 支持对public方法,非public方法和静态方法的调用;
- 支持异步线程结果获取;
- 支持后台线程池和Android主线程调度器的替换;
示例
- 使用前,先进行配置
//configure defautl switcher
Switcher.builder()
.setUiExecutor(new UiDispatcher()) //android main thread dispatcher
.setIndex(new DispatcherIndex()) //generated by annotation processor
.installDefaultThreadDispatcher();
- 简易代码调用实现 代码块/接口方法/普通方法 运行在特定的线程中;
Switcher.getDefault().main(new Runnable() {
@Override
public void run() {
//this will run on main thread
String msg = "main in main thread--> thread.name = " + Thread.currentThread().getName();
log(msg);
toast(msg);
}
}).post(new Runnable() {
@Override
public void run() {
//this will run on post thread
String msg = "post in main thread --> thread.name = " + Thread.currentThread().getName();
log(msg);
toast(msg);
}
}).background(new Runnable() {
@Override
public void run() {
//this will run on background thread
String msg = "background in main thread --> thread.name = " + Thread.currentThread().getName();
log(msg);
toast(msg);
}
}).async(new Runnable() {
@Override
public void run() {
//this will run on async thread
String msg = "async in main thread --> thread.name = " + Thread.currentThread().getName();
log(msg);
toast(msg);
}
});
运行结果:
normal_usage_result注:这里要说明一下:
main
: 表示代码块运行在Android主线程;post
: 表示代码块运行在调用者线程上;background
: 运行在后台线程池中(串行);async
: 运行在后台线程池中(并行);可以看到,这里的概念其实就是跟 EventBus 的线程调度概念完全一致,甚至里面的线程调度实现方式也是参考 EventBus 实现的。
ps:EventBus的源码解析可以参考: EventBus3.0 源码解析
- 支持接口方法通过注解指定运行线程(实现类该方法都会运行在注解指定的线程中);
public interface ITestInterface {
@Switch(threadMode = MAIN)
String doMain(List<String> test);
}
public class InterfaceMethodUsageActivity extends AppCompatActivity implements ITestInterface {
···
···
···
@OnClick(R.id.main_In_main)
public void onMainInMain() {
log("onMainInxx :run on thread.name = " + Thread.currentThread().getName());
ITestInterface proxy = Switcher.getDefault().create(this);
String result = proxy.doMain(null);
if (result == null) {
log("return type is not Future," +
"so you are unable to obtain the return value");
} else {
log("if you delete the annotation,then you can get the result:" + result);
}
}
@OnClick(R.id.main_In_background)
public void mainInBackground() {
new Thread() {
@Override
public void run() {
super.run();
onMainInMain();
}
}.start();
}
}
运行结果:
interface_usage_result可以看到,由于接口方法
doMain
指定的运行线程为threadMode = MAIN
主线程,所以无论是在主线程还是在子线程调用,都是运行在主线程中的。注:大家看到这个部分的调用方式有没有觉得很熟悉 ^-^。没错,这部分的实现其实跟 Retrofit 通过接口进行HTTP请求描述和通过动态代理实现接口注解解析和进行HTTP请求的实现方式是一样的。只是我们这里实现的是线程调度而已。
ps:如果还有不熟悉 Retrofit 源码实现方式的,可以参考下这篇文章:Retrofit 2.0源码解析.
- 支持通过注解为函数指定 别名,后续可通过别名进行函数调用;
- 支持对public方法,非public方法和静态方法的调用;
public class MethodAliasUsageActivity extends AppCompatActivity {
Switcher switcher;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.alias_usage);
ButterKnife.bind(this);
//you can also obtain switcher object from Builder;
//or you can event get switcher by new Switcher(),but without Android main thread dispatcher and alias method calling ability.
switcher = new Switcher.Builder()
.setIndex(new DispatcherIndex())
.setUiExecutor(new UiDispatcher())
.build();
}
//run public method
@OnClick(R.id.btnPublic)
public void doPublic() {
switcher.run("runPublic", this, 1);
}
@Switch(alias = "runPublic", threadMode = ThreadMode.BACKGROUND)
public void publicMethod(int i) {
log(String.format("publicMethod:threadMode = %s, Thread.name = %s",
ThreadMode.BACKGROUND, Thread.currentThread().getName()));
log("publicMethod:params = " + i);
}
//run private method
@OnClick(R.id.btnPrivateMethod)
public void doPrivate() {
switcher.run("runPrivate", this);
}
@Switch(alias = "runPrivate", threadMode = ThreadMode.MAIN)
private void privateMethod() {
log(String.format("privateMethod:threadMode = %s, Thread.name = %s",
ThreadMode.MAIN, Thread.currentThread().getName()));
}
}
//run static public method
@OnClick(R.id.btnRunStaticPublicMethod)
public void doStaticPublic() {
int[][] intint = new int[][]{
new int[]{1, 2, 3, 4, 5},
new int[]{100, 300, 2343, 341324},
};
switcher.run("staticPublic", MethodAliasUsageActivity.class, new String[][]{}, intint);
}
@Switch(alias = "staticPublic")
public static void staticPublicMethod(String[][] strstr, int[][] intint) {
Log.i("Whyn111", String.format("publicMethod:threadMode = %s, Thread.name = %s",
ThreadMode.BACKGROUND, Thread.currentThread().getName()));
log(strstr);
for (int i = 0; i < intint.length; ++i) {
for (int j = 0; j < intint[i].length; ++j) {
Log.i("Whyn111", String.format("arrarr[%d][%d] = %d", i, j, intint[i][j]));
}
}
}
@OnClick(R.id.btnRunStaticPrivateMethod)
public void doStaticPrivate() {
List<String> list = new LinkedList<>();
list.add("asdfad");
list.add("aaaaaaaaaaaaaaaaaaaa");
Map<String, List<String>> maps = new LinkedHashMap<>();
maps.put("withValue", list);
maps.put("withoutValue", new ArrayList<String>());
switcher.run("staticPrivate", MethodAliasUsageActivity.class, list, maps);
}
@Switch(alias = "staticPrivate")
private static <T, V> void staticPrivateMethod(List<T> list, Map<String, V> maps) {
Log.i("Whyn111", "list:" + list.toArray());
int i = 0;
for (Map.Entry<String, V> entry : maps.entrySet()) {
Log.i("Whyn111", String.format("maps[%d] = [%s,%s]", i++, entry.getKey(), entry.getValue()));
}
}
运行结果:
alias_usage_result
注:这里要说明一下,ThreadDispatcher 对于公有方法的调用,100%无反射,而对于非公有方法的调用,使用的是反射调用,所以,推荐大家使用public
方法进行线程调度。
- 支持异步线程结果获取;
@OnClick(R.id.btnGetAsyncResult)
public void onGetAsyncResult() {
try {
Future<String> result = Switcher.getDefault().async(new Callable<String>() {
@Override
public String call() throws Exception {
return whatIsYourName(1);
}
});
Log.i("Whyn111", "result from async thread: " + result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private String whatIsYourName(int id) {
Log.i("Whyn111", "whatIsYourName:Thread.name = " + Thread.currentThread().getName());
return "My name is No. " + id;
}
运行结果:
get_result_from_differ_thread从运行结果可以看到,我们成功的在主线程获取到
async
线程运行的结果。
- 最后,退出程序时,请记住关闭资源(主要就是线程池的关闭):
Switcher.getDefault().shutdown();
更多Demo,请查看:sample
下载
Gradle
compile 'com.whyn:threaddispatcher:1.1.1'
如果想在Android上使用主线程调度器,还需加上:
compile 'com.whyn:threaddispatcher4android:1.0.0'
如果想使用函数别名功能,还需加上:
annotationProcessor 'com.whyn:threaddispatcherprocessor:1.1.1'
#app build.gradle
android {
defaultConfig{
···
···
javaCompileOptions {
annotationProcessorOptions {
arguments = [dispatcherIndex: 'com.yn.DispatcherIndex'] //generated file package name
}
}
}
}