demo地址

本篇分析对接收事件的处理

需要思考的问题

  1. android的事件会回调在public void onAccessibilityEvent(AccessibilityEvent event)这个方法里,这是在主线程,直接在这里处理合适吗?
  2. 如果你的应用需要实现多个功能,怎么在这个方法里面保证他们的执行隔离呢?
  3. 当微信不知道什么原因,或者是你代码不健壮导致的页面停住,不再触发新的event事件,你怎么处理?
  4. 怎么循环一个列表(批量加好友,批量发消息)去迭代每个item去完成一组功能?
  5. 怎么做到多个功能有重复逻辑的时候的代码复用
  6. 怎么设计一个功能执行的过程中的暂停、继续,结束之后的弹框交互

ok,本篇开始分析1、2、3的问题。

线程问题

首先当一个event触发的时候,你需要去判断这个event的getEventType(),然后判断节点的类名,或者是寻找某一个控件的id的节点。那么通常,你需要将你所有的功能的方法都放到void onAccessibilityEvent(AccessibilityEvent event)里面,去挨个判断执行,因为当然你不知道这个event适用于那段逻辑代码。

试想,如果你的给所有好友(判断标签、备注、性别)发送文字和图片的一个功能的逻辑代码片段有几十处,一个event进来需要挨个去走这几十短代码,并且,可能当前的event都不是这几十短代码判断之后符合的(因为有各种的click,contentChange,scroll事件)。如此看来,再加上更多的功能片段,可想而知,会耗时阻塞主线程,极有可能出现ANR异常。

这么说来当然有必要将这些event事件发送到一个子线程去处理啦。

在这里,我采用IntentService,将Service里面触发的event都发送到IntentService里面去处理.

public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable{}

当然了,AccessibilityEvent是Parcelble的,可以通过Bundle传送。

public class MainService extends AccessibilityService {    @Override    public void onAccessibilityEvent(AccessibilityEvent event) {        HandleAccessibilityEventService.startWithEvent(getApplicationContext(), event);    }}
public class HandleAccessibilityEventService extends IntentService {    private static final String EXTRA_EVENT = "extra_event";    public HandleAccessibilityEventService() {        super("HandleAccessibilityEventService");    }    public static void startWithEvent(Context context, AccessibilityEvent event) {        if (TYPE_NOTIFICATION_STATE_CHANGED == event.getEventType()) {            H.getInstance().excuteServiceMethods(TaskId.get().mCurrentId, event);            return;        }        Intent intent = new Intent(context, HandleAccessibilityEventService.class);        intent.putExtra(EXTRA_EVENT, event);        context.startService(intent);    }    @Override    protected void onHandleIntent(Intent intent) {        if (intent != null) {            AccessibilityEvent event = intent.getParcelableExtra(EXTRA_EVENT);            if (event != null) {                // TODO: 2019/3/18 处理event             }        }    }}

这样,即便是event事件同一时刻会触发很多,没关系,让IntentService一个一个处理,不着急。

多个任务的问题

这个可以通过一个全局变量,设置一个id,相同功能的代码包含在一个id里面。

那么在判断event的时候,先判断当前的功能id,如果是的话再去执行这一片代码。

@Override    protected void onHandleIntent(Intent intent) {        if (intent != null) {            AccessibilityEvent event = intent.getParcelableExtra(EXTRA_EVENT);            if (event != null) {switch(mCurrentId){case Id.Add_FIRENDS://todo//todobreak;...... }            }        }    }

微信页面停留,不触发新的event,导致功能不能继续下去。

当你的应用中注册了AccessibilityService时,系统会在需要发送AccessibilityEvent的地方去遍历发送到这些service里面.

那么我们可不可以手动反射出创建AccessibilityEvent对象,来发送到我们的service呢。答案当然是肯定的。

我们可以在处理事件的时候,首先去判断事件类型是不是TYPE_WINDOW_STATE_CHANGED,因为所有的activity切换都会触发这个事件,但是Dialog的alert也会发出,为了区分,只能通过类名来判断。恰巧的是,微信大多activity的类名都是以UI结尾的,那么,我斗胆用这种方式来记录下当前微信停留的页面的类名。

public class MainService extends AccessibilityService {        @Override    public void onAccessibilityEvent(AccessibilityEvent event) {        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {            String s = event.getClassName().toString();            if (s.endsWith("UI")) {                WechatCurrentActivity.getInstance().updateCurrent(s);            }        }        HandleAccessibilityEventService.startWithEvent(getApplicationContext(), event);    }}

这样以来就好办啦,AccessibilityEvent这个类最核心的属性就是eventType和ClassName,那么我们直接反射它的构造去设置这两个属性,然后传递到我们的IntentService里面去处理就好啦。

这里我们需要自己设定这个微信页面无响应的超时时间啦,比如5s,10s。

在这里我重新启动一个守护服务,去守护微信无响应的情况。当每一个功能开启时,它也就启动。

public class DeamonService extends IntentService {    private static final String EXTRA_TASK_ID = "extra_task_id";    private AccessibilityEvent event;    public DeamonService() {        super("DeamonService");    }    public static void startWithTaskId(Context context, int taskId) {        Intent intent = new Intent(context, DeamonService.class);        intent.putExtra(EXTRA_TASK_ID, taskId);        context.startService(intent);    }    @Override    protected void onHandleIntent(Intent intent) {        //更新当前操作时间戳        H.getInstance().updateOpreateTime();        if (intent != null) {            int taskId = intent.getIntExtra(EXTRA_TASK_ID ,-2);            while (TaskId.assertTaskId(taskId)) {                if (Delay.isResponseTimeOut()) {                    //超时                    //手动触发动作                    todo(taskId);                }                UiKit.sleep(5000);            }        }    }    private void todo(int taskId) {        if (event != null) {            event.recycle();        }        event = ObtainWindowStateChangeEvent.obtainEvent(WechatCurrentActivity.getInstance().getCurrentActivity());        if (event != null) {            L.e("手动触发Event :" + event);            H.getInstance().excuteServiceMethods(taskId,event);        }    }}

反射获取Event对象:

public class ObtainWindowStateChangeEvent {    public static AccessibilityEvent obtainLuancherEvent() {        return obtainEvent(WechatUI.UI_LUANCHER);    }    public static AccessibilityEvent obtainEvent(String className) {        try {            Constructor constructor = AccessibilityEvent.class.getDeclaredConstructor();            constructor.setAccessible(true);            AccessibilityEvent accessibilityEvent = constructor.newInstance();            accessibilityEvent.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);            accessibilityEvent.setClassName(className);            return accessibilityEvent;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}

到此,我们前三个问题都分析完了。

下篇分析之后的几个问题。

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. Android开发学习笔记之 Service 的使用
  3. Android(安卓)实现对话框圆角功能
  4. Android(安卓)应用桌面角标显示各厂商规则说明
  5. 下载并编译Chrome浏览器For Android
  6. Android资源文件在配置文件中的使用
  7. Android代码优化
  8. [百度空间] [原]跨平台编程注意事项(三): window 到 android 的
  9. 利用Eclipse和NDK建立一个简单service

随机推荐

  1. android 记事本
  2. Android Stdio实现简单计算器
  3. Android实现录制视频
  4. Android 使用内容解析者往短信数据库里插
  5. android工程没有gen路径
  6. 移植Busybox到Android平台
  7. android 上传文件到服务器
  8. android关机充电的奥妙所在(留着以后用)
  9. 【Android】 dialog 设置maxHeight 最大
  10. Android Handler不同界面发送数据