美文网首页
开源一套简易线程调度库

开源一套简易线程调度库

作者: Whyn | 来源:发表于2017-08-15 12:09 被阅读36次

    背景

    阅读开源库的一大好处就是不仅可以了解功能实现的逻辑,而且对于某些实现代码,经常给人醍醐灌顶的感受。ThreadDispatcher 的产生就是由于笔者在阅读了ButterknifeEventBusRetrofit 后,一时兴(chong)起(dong)写的(●'◡'●).

    用途

    • 简易代码调用实现 代码块/接口方法/普通方法 运行在特定的线程中;
    • 支持接口方法通过注解指定运行线程(实现类该方法都会运行在注解指定的线程中);
    • 支持注解指定函数运行线程;
    • 支持通过注解为函数指定 别名,后续可通过别名进行函数调用;
    • 支持对public方法,非public方法和静态方法的调用;
    • 支持异步线程结果获取;
    • 支持后台线程池和Android主线程调度器的替换;

    示例

    1. 使用前,先进行配置
     //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线程运行的结果。
    1. 最后,退出程序时,请记住关闭资源(主要就是线程池的关闭):
    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
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:开源一套简易线程调度库

          本文链接:https://www.haomeiwen.com/subject/tfmprxtx.html