本文是针对上文Android小技巧:利用动态代理自动切换线程的一个补充,补充一种简单的实现方式。
上文中我们提到利用动态代理将对某个对象的方法调用自动切换到对应线程中去,只是探讨了可行性和局限,但如果每个功能都手动创建代理就显得有些繁琐。本文提供一个简单的工具来简化这个创建流程。
使用效果演示
方式一:注解控制
java
@RunInThread(ThreadType.IO)
public interface TestActor {
void test();
}
class TestActorImpl implements TestActor {
@Override
public void test() {
Log.d("SPECTRE", "TestActorImpl.test => " + Thread.currentThread().getName());
}
@Override
public int hashCode() {
Log.d("SPECTRE", "TestActorImpl.hashCode => " + Thread.currentThread().getName());
return super.hashCode();
}
}
//下面是获取代理对象并调用的代码
TestActor actor = ThreadProxyUtils.createThreadProxy(TestActor.class, new TestActorImpl());
actor.test();
actor.hashCode();
方式二:参数控制
java
// 这里没有注解
public interface TestActor {
void test();
}
class TestActorImpl implements TestActor {
@Override
public void test() {
Log.d("SPECTRE", "TestActorImpl.test => " + Thread.currentThread().getName());
}
@Override
public int hashCode() {
Log.d("SPECTRE", "TestActorImpl.hashCode => " + Thread.currentThread().getName());
return super.hashCode();
}
}
//下面是获取代理对象并调用的代码
TestActor actor = ThreadProxyUtils.createThreadProxy(ThreadType.IO, TestActor.class, new TestActorImpl()); // 多了ThreadType参数
actor.test();
actor.hashCode();
具体实现
ThreadType
定义(不想用enum可以改用静态int常量)
java
public enum ThreadType {
IO, CPU, MAIN, HANDLER, SINGLE
}
RunInThread
注解
java
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
public @interface RunInThread {
ThreadType value();
}
ThreadProxyUtils
实现(这里用了com.blankj.utilcode的ThreadUtils
线程池,如果自己项目中已经有线程池管理,可以改用自己项目中的线程池)
java
public class ThreadProxyUtils {
private static final AtomicInteger HANDLER_THREAD_INDEX = new AtomicInteger(1);
private ThreadProxyUtils() {
}
public static <T> T createThreadProxy(Class<T> clazz, T impl) {
ThreadType threadType = Optional.ofNullable(clazz.getAnnotation(RunInThread.class)).map(RunInThread::value)
.orElseThrow(() -> new IllegalStateException("类的注解中找不到ThreadType!"));
return createThreadProxy(threadType, clazz, impl);
}
public static <T> T createThreadProxy(ThreadType threadType, Class<T> clazz, T impl) {
if (clazz.isInterface()) {
final Consumer<Runnable> taskConsumer;
switch (threadType) {
case IO:
taskConsumer = action -> ThreadUtils.getIoPool().submit(action);
break;
case CPU:
taskConsumer = action -> ThreadUtils.getCpuPool().submit(action);
break;
case SINGLE:
taskConsumer = action -> ThreadUtils.getSinglePool().submit(action);
break;
case MAIN:
Handler mainHandler = new Handler(Looper.getMainLooper());
taskConsumer = mainHandler::post;
break;
case HANDLER:
HandlerThread handlerThread = new HandlerThread("HandlerThread-" + HANDLER_THREAD_INDEX.getAndIncrement());
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
taskConsumer = handler::post;
break;
default:
throw new RuntimeException("非法的ThreadType: " + threadType);
}
Set<Method> methodSet = Arrays.stream(clazz.getMethods()).collect(Collectors.toSet());
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, (proxy, method, args) -> {
if (methodSet.contains(method)) {
taskConsumer.accept(() -> {
try {
method.invoke(impl, args);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
});
return null;
} else {
return method.invoke(impl, args);
}
});
} else {
throw new IllegalArgumentException(clazz.getCanonicalName() + "必须是一个接口!");
}
}
}