android 借助AccessibilityService实现模拟点击功能-实现重复功能代码复用的架构(五)

下集预告:
android 借助AccessibilityService实现模拟点击功能-代码复用架构的注解和细节(六)

本篇介绍辅助功能重复逻辑的代码复用

为什么复用?

假想,不,你可以实际操作一下:在AccessibilityService-onAccessibilityEvent(event)方法里面,不停的在switch(event.getEventType())下case 类型、类名,然后在每一个case下面,又是好多功能id的判断。

通常好多功能的公共部分有很多,比如一个弹框弹出,然后需要点击最右边的menu按钮,再比如滑动通讯录列表去点击每一个好友,返回一个页面。类似的场景有好多,有没有考虑把通用的场景涉及的代码抽取出来然后去复用呢。

of course,你把代码片段alt+command+M来抽取成static方法,在需要调用的地方去调用,当然可以。但是这样写下来,你的代码有没有很乱,或者是很难找某一步的位置。

为了达到既美观,又容易分析的效果,我是这样做的。

模仿retrofit2采用一个动态代理,统一执行代码片段

  • 首先创建统一接口
public interface Action {
    void run(AccessibilityEvent event);
}

所有的一段代码片段我称作一个Action,如果满足条件(eventType,className等),就去run();

比如点击微信主界面右上角加号的这么一个操作:

@EVENT_TYPE
@EVENT_CLASS(UI_LUANCHER)
public class ActionClickPlusAdd implements Action{
    @Override
    public void run(AccessibilityEvent event) {
       //todo find the node by this view's id ,and click it 
    }
}

这里的注解是为了简化代码,之后统一介绍。

  • 然后一个个的Action写在一个Interface里面(对,就是retrofit的实现方式):
public interface ActionService {
    /**
     * 点击首页【加号】
     *
     * @param event
     */
    @TASK_IDS({TASK_MASS_SEND_GROUP, TASK_GET_ALL_GROUP_NAMES})
    @RUN(ActionClickPlusAdd.class)
    void i(AccessibilityEvent event);
}
  • 再然后就是重点了,创建代理对象,实现判断逻辑的统一处理:

public class H {

    private ActionService service;
    private SparseArray<List<Method>> mTaskMethods = new SparseArray<>();
    private HashMap<Method, ActionAssertEntity> mActions = new HashMap<>();

    private static final H ourInstance = new H();
    private long  mUpdateTime ;

    public static H getInstance() {
        return ourInstance;
    }

    private H() {

    }

    public ActionService createService() {
        if (service == null) {
            service = (ActionService) Proxy.newProxyInstance(ActionService.class.getClassLoader(), new Class<?>[]{ActionService.class},
                    new InvocationHandler() {

                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                                // 逻辑省略...
                                
                            	ActionAssertEntity actionAssertEntity = mActions.get(method);
                                actionAssertEntity.getAction().run(event);
                            return null;
                        }
                    });
        }
        return service;
    }
}
  • 然后就是在IntentService中的调用了:
public class HandleAccessibilityEventService extends IntentService {
    //重复代码前面贴过 略...
    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            AccessibilityEvent event = intent.getParcelableExtra(EXTRA_EVENT);
            if (event != null) {
                H.getInstance().excuteServiceMethods(TaskId.get().mCurrentId, event);
            }
        }
    }
}

总结下来,写一个功能中的一个操作,比如点一个特定的按钮的操作就变成下面两步了:

  1. 写一个类实现Action接口,在run(event)里面去实现逻辑,当然了上面需要加上特性eventType和ClassName的注解;
  2. 把这个类名粘在ActionService里面,相当于注册了。和retrofit类似。

当然了,有人会说,通过反射注解,执行方法的方式耗时耗内存,其实不然,当某一个功能任务的方法第一次需要执行的时候,这里的处理是首先把当前功能任务的id所拥有的全部method全部提取出来存在mTaskMethods里面,这个map是一个任务id和methodList的映射,当需要执行一个event所对应的方法时,直接从mTaskMethods中取出来当前任务的所有method,然后遍历去执行。其次是反射的过程中已经把每个action注解对应的属性存放在ActionAssertEntity中,这样,下次就没必要重复反射获取注解属性了。无非是多用了两个Map而已。

这样看来,是不是很简洁,我们就可以把注意力集中在实现功能的逻辑上了,而不必担心因为代码的杂乱引起的额外的注意力开销。

下篇具体讲解用到的注解,和在代理对象中的解析和判断逻辑。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值