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

开源一套简易线程调度库

作者: 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
            }
        }
    }
}

相关文章

  • 开源一套简易线程调度库

    背景 阅读开源库的一大好处就是不仅可以了解功能实现的逻辑,而且对于某些实现代码,经常给人醍醐灌顶的感受。Threa...

  • 多线程信号量dispatch_semaphore

    dispatch_semaphore是线程调度的一种方式主要方法 多线程和数据库

  • 漫画解读: Java 线程池的工作机制

    线程池 结构图 线程池是一套围绕着核心线程、非核心线程、等待队列的任务调度框架。 默认情况,线程池主要结构如下: ...

  • java虚拟机读书笔记之线程调度

    java线程调度 线程调度主要有两种方式,协同式线程调度和抢占式线程调度。1、协同式: 线程的执行时间由线程本身...

  • [Java]线程和锁

    0x00 线程调度 线程调度指的是系统为线程分配CPU使用权。分为两种: 协同式线程调度线程想用CPU多久就用多久...

  • 进程调度与管理3-用户进程与内核线程

    1-线程的三种模型 1.1-用户级线程(多对一模型) 库调度器从进程的多个线程中选择一个线程,然后该线程和该进程允...

  • CPU调度

    CPU调度 基本概念 CPU调度在讨论普通调度概念时使用进程调度,特别指定为线程概念时使用线程调度 CPU-I/O...

  • 2018-04-03 线程基础

    线程调度 是指系统分配CPU使用权限的方式,分为协同式线程调度和抢占式线程调度 进程、线程概念 进程是应用程序的一...

  • quartz源码3-调度处理

    一 类依赖结构 二 调度线程 初始化阶段等待其他流程初始化完成,进入调度处理 数据库访问失败则睡眠后重试 Obje...

  • 线程优先级和守护线程

    线程优先级: Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定调度哪个...

网友评论

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

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