Android小技巧:利用动态代理自动切换线程(续)

本文是针对上文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();

具体实现

  1. ThreadType定义(不想用enum可以改用静态int常量)
java 复制代码
public enum ThreadType {
    IO, CPU, MAIN, HANDLER, SINGLE
}
  1. RunInThread注解
java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
public @interface RunInThread {

    ThreadType value();
}
  1. 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() + "必须是一个接口!");
        }
    }
}
相关推荐
许彰午3 小时前
14_Java泛型完全指南
java·windows·python
智慧物业老杨3 小时前
司法绿色通道下的物业纠纷数智化解决方案——基于“三优先“机制的全流程技术落地实践
java·django
2601_961194023 小时前
2026初级会计实务公式总结大全|计算题公式手册PDF
java·spring·eclipse·pdf·tomcat·hibernate
做个文艺程序员3 小时前
第1篇:K8s 核心概念精讲:Pod、Deployment、Service 与 Namespace——Java 开发者快速上手指南
java·云原生·容器·kubernetes·容器编排
小欣加油6 小时前
leetcode3751 范围内总波动值I
java·数据结构·c++·算法·leetcode
闪电悠米6 小时前
黑马点评-Redisson-01_why_redisson
java·服务器·网络·数据库·缓存·wpf
星轨zb6 小时前
LangChain4j 集成 Spring Boot:会话记忆 NPE 的根源与 ChatMemoryProvider 正确配置
java·spring boot·后端·langchain4j
JAVA9656 小时前
JAVA面试-并发篇 05-并发包AQS队列实现原理是什么
java·开发语言·面试
JAVA面经实录9176 小时前
RocketMQ全套学习知识手册
java·kafka·rabbitmq·rocketmq
phltxy6 小时前
Spring AI 从提示词到多模态
java·人工智能·spring