开源一套简易线程调度库

2017-08-15  本文已影响36人  Whyn

背景

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

用途

示例

  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 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
            }
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读