开源一套简易线程调度库

背景

阅读开源库的一大好处就是不仅可以了解功能实现的逻辑,而且对于某些实现代码,经常给人醍醐灌顶的感受。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
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • 做水果微商两年,很多人问我:你怎么不开个水果店? 每天也都有新粉加我:你的实体店在哪?我从来没在网上买过水果,不会...
    赵小昭_莱阳慈梨膏阅读 677评论 0 0
  • 好几十人的一个小团体中,她的年龄最大,其他人多是80后,只有她一个70后,她自己也不避讳,非常豪爽地宣布:我比你们...
    半岛公子阅读 427评论 0 13
  • 何时 在什么时间做什么事情 吉田さんは日曜日に休みますか? 陳さんは明日の朝、勉強します。 私ま毎朝、7時に起きま...
    onizuka_jp阅读 129评论 0 0