博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android AccessibilityService机制源码解析
阅读量:6291 次
发布时间:2019-06-22

本文共 27769 字,大约阅读时间需要 92 分钟。

一、本文需要解决的问题

之前本人做了一个项目,需要用到AccessibilityService这个系统提供的拓展服务。这个服务本意是作为Android系统的一个辅助功能,去帮助残疾人更好地使用手机。但是由于它的一些特性,给很多项目的实现提供了一个新的思路,例如之前大名鼎鼎的微信抢红包插件,本质上就是使用了这个服务。我研究AccessibilityService的目的是解决以下几个我在使用过程中所思考的问题:

  1. AccessibilityService这个Service跟一般的Service有什么区别?
  2. AccessibilityService是如何做到监控并捕捉用户行为的?
  3. AccessibilityService是如何做到查找控件,执行点击等操作的?

二、初步分析

本文基于Android 7.1的源码对AccessibilityService进行分析。 为了更好地理解和分析代码,我写了一个demo,如果想学习具体的使用方法,可以参考Google官方文档。本文不做AccessibilityService的具体使用教程。

创建AccessibilityService
public class MyAccessibilityService extends AccessibilityService {    private static final String TAG = "MyAccessibilityService";    @Override    public void onCreate() {        super.onCreate();        Log.i(TAG, "onCreate");    }    @Override    public void onAccessibilityEvent(AccessibilityEvent event) {        int eventType = event.getEventType();        switch (eventType) {            case AccessibilityEvent.TYPE_VIEW_CLICKED:                // 捕获到点击事件                Log.i(TAG, "capture click event!");                AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();                if (nodeInfo != null) {                    // 查找text为Test!的控件                    List
button = nodeInfo.findAccessibilityNodeInfosByText("Test!"); nodeInfo.recycle(); for (AccessibilityNodeInfo item : button) { Log.i(TAG, "long-click button!"); // 执行长按操作 item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); } } break; default: break; } } @Override public void onInterrupt() { Log.i(TAG, "onInterrupt"); }}复制代码
AccessibilityService配置

res/xml/accessibility_service_config.xml

复制代码
在manifest中进行注册
复制代码
创建一个text为Test!的button控件,设置监听方法
public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private Button button;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button = findViewById(R.id.button);        button.setOnLongClickListener(new View.OnLongClickListener() {            @Override            public boolean onLongClick(View v) {                Log.i(TAG, "onLongClick");                return false;            }        });    }}复制代码
开启AccessibilityService

AccessibilityService服务具体开启位置在设置--无障碍中。

运行应用,点击text为Test!的按钮

会出现以下的日志:

具体解释: 点击按钮即产生TYPE_VIEW_CLICKED事件 --> 被AcceesibilityService捕获 --> 捕获后执行长按按钮操作 --> 执行长按回调方法。

为什么AcceesibilityService能捕获并执行其他操作呢,接下来我将对源码进行解析~

三、源码解析

AccessibilityService内部逻辑
AccessibilityService.java
public abstract class AccessibilityService extends Service {      // 省略代码      public abstract void onAccessibilityEvent(AccessibilityEvent event);            public abstract void onInterrupt();      @Override      public final IBinder onBind(Intent intent) {          return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {              @Override              public void onServiceConnected() {                  AccessibilityService.this.dispatchServiceConnected();              }              @Override              public void onInterrupt() {                  AccessibilityService.this.onInterrupt();              }              @Override              public void onAccessibilityEvent(AccessibilityEvent event) {                  AccessibilityService.this.onAccessibilityEvent(event);              }              @Override              public void init(int connectionId, IBinder windowToken) {                  mConnectionId = connectionId;                  mWindowToken = windowToken;                  // The client may have already obtained the window manager, so                  // update the default token on whatever manager we gave them.                  final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);                  wm.setDefaultToken(windowToken);              }              @Override              public boolean onGesture(int gestureId) {                  return AccessibilityService.this.onGesture(gestureId);              }              @Override              public boolean onKeyEvent(KeyEvent event) {                  return AccessibilityService.this.onKeyEvent(event);              }              @Override              public void onMagnificationChanged(@NonNull Region region,                      float scale, float centerX, float centerY) {                  AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);              }              @Override              public void onSoftKeyboardShowModeChanged(int showMode) {                  AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);              }              @Override              public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {                  AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);              }          });      }}复制代码

分析:

  1. AccessibilityService是一个抽象类,继承于Service,提供两个抽象方法 onAccessibilityEvent() 和 onInterrupt();
  2. 虽然是抽象类,但是实现了最重要的 onBind() 方法,在其中创建了一个IAccessibilityServiceClientWrapper对象,实现Callbacks接口中的抽象方法。
IAccessibilityServiceClientWrapper
// 以分析onAccessibilityEvent为例,省略部分代码public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub            implements HandlerCaller.Callback {    private final HandlerCaller mCaller;    private final Callbacks mCallback;    private int mConnectionId;        public IAccessibilityServiceClientWrapper(Context context, Looper looper,                Callbacks callback) {        mCallback = callback;        mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);    }        public void init(IAccessibilityServiceConnection connection, int connectionId,                IBinder windowToken) {        Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,                    connection, windowToken);        mCaller.sendMessage(message);    }        // 省略部分代码     public void onAccessibilityEvent(AccessibilityEvent event) {        Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);        mCaller.sendMessage(message);    }    @Override    public void executeMessage(Message message) {        switch (message.what) {            case DO_ON_ACCESSIBILITY_EVENT: {                AccessibilityEvent event = (AccessibilityEvent) message.obj;                if (event != null) {                    AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);                    mCallback.onAccessibilityEvent(event);                    // Make sure the event is recycled.                    try {                        event.recycle();                    } catch (IllegalStateException ise) {                        /* ignore - best effort */                    }                }            } return;            // ...                 }     }}复制代码

分析:

  1. IAccessibilityServiceClientWrapper继承于IAccessibilityServiceClient类,它是一个aidl接口,同时注意到它是继承于IAccessibilityServiceClient.Stub类,可以大概猜测到,AccessibilityService为一个远程Service,使用到跨进程通信技术,后面我还会继续分析这个;
  2. IAccessibilityServiceClientWrapper的类构造方法中,有两个比较重要的参数,一个是looper,另一个是Callbacks callback。Looper不用说,而Callbacks接口定义了很多方法,代码如下:
public interface Callbacks {    public void onAccessibilityEvent(AccessibilityEvent event);    public void onInterrupt();    public void onServiceConnected();    public void init(int connectionId, IBinder windowToken);    public boolean onGesture(int gestureId);    public boolean onKeyEvent(KeyEvent event);    public void onMagnificationChanged(@NonNull Region region,                float scale, float centerX, float centerY);    public void onSoftKeyboardShowModeChanged(int showMode);    public void onPerformGestureResult(int sequence, boolean completedSuccessfully);}复制代码
  1. IAccessibilityServiceClientWrapper同时也实现了HandlerCaller.Callback接口,HandlerCaller类通过命名也可以知道,它内部含有一个Handler实例,所以可以把它当做一个Handler,而处理信息的方法就是HandlerCaller.Callback#executeMessage(msg)方法
  2. 代码有点绕,故简单总结一下流程: AccessibilityEvent产生   -> Binder驱动    -> IAccessibilityServiceClientWrapper#onAccessibilityEvent(AccessibilityEvent)     -> HandlerCaller#sendMessage(message); // message中包括AccessibilityEvent      -> IAccessibilityServiceClientWrapper#executeMessage();       -> Callbacks#onAccessibilityEvent(event);        -> AccessibilityService.this.onAccessibilityEvent(event);

到这里解决了我们的第一个问题:AccessibilityService同样继承于Service类,它属于远程服务类,是Android系统提供的一种服务,可以绑定此服务,用于捕捉界面的一些特定事件。

AccessibilityService外部逻辑

前面分析了接收到AccessibilityEvent之后的代码逻辑,那么,这些AccessibilityEvent是怎样产生的呢,而且,在回调执行之后是怎么做到点击等操作的(如demo所示)?我们接下来继续分析相关的源码~

我们从demo作为例子开始入手,首先我们知道,一个点击事件的产生,实际代码逻辑是在View#onTouchEvent() -> View#performClick()中:

public boolean performClick() {    final boolean result;    final ListenerInfo li = mListenerInfo;    if (li != null && li.mOnClickListener != null) {        playSoundEffect(SoundEffectConstants.CLICK);        li.mOnClickListener.onClick(this);        result = true;    } else {        result = false;    }    // !!!    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);    return result;}复制代码

这里找到一个重点方法sendAccessibilityEvent(),继续跟进去,最后走到View#sendAccessibilityEventUncheckedInternal()方法:

public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {    if (!isShown()) {        return;    }    onInitializeAccessibilityEvent(event);    // Only a subset of accessibility events populates text content.    if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {        dispatchPopulateAccessibilityEvent(event);    }    // In the beginning we called #isShown(), so we know that getParent() is not null.    getParent().requestSendAccessibilityEvent(this, event);}复制代码

这里的getParent()会返回一个实现ViewParent接口的对象。 我们可以简单理解为,它会让View的父类执行requestSendAccessibilityEvent()方法,而View的父类一般为ViewGroup,我们查看ViewGroup#requestSendAccessibilityEvent()方法

@Overridepublic boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {    ViewParent parent = mParent;    if (parent == null) {        return false;    }    final boolean propagate = onRequestSendAccessibilityEvent(child, event);    if (!propagate) {        return false;    }    return parent.requestSendAccessibilityEvent(this, event);}复制代码

这里涉及到一个变量mParent,我们要找到这个mParent变量是在哪里被赋值的。 首先我们在View类中找到一个相关的方法View#assignParent():

void assignParent(ViewParent parent) {    if (mParent == null) {        mParent = parent;    } else if (parent == null) {        mParent = null;    } else {        throw new RuntimeException("view " + this + " being added, but" + " it already has a parent");    }}复制代码

但是View类中并没有调用此方法,猜测是View的父类进行调用。 通过对源码进行搜索,发现最后是在ViewRootImpl#setView()中进行调用,赋值的是this即ViewRootImpl本身。 直接跳到ViewRootImpl#requestSendAccessibilityEvent()方法:

@Overridepublic boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {    if (mView == null || mStopped || mPausedForTransition) {        return false;    }    // Intercept accessibility focus events fired by virtual nodes to keep    // track of accessibility focus position in such nodes.    final int eventType = event.getEventType();    switch (eventType) {        case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:            {                final long sourceNodeId = event.getSourceNodeId();                final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(sourceNodeId);                View source = mView.findViewByAccessibilityId(accessibilityViewId);                if (source != null) {                    AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();                    if (provider != null) {                        final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(sourceNodeId);                        final AccessibilityNodeInfo node;                        if (virtualNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {                            node = provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID);                        } else {                            node = provider.createAccessibilityNodeInfo(virtualNodeId);                        }                        setAccessibilityFocus(source, node);                    }                }            }            break;            // 省略部分代码    }    // !!!    mAccessibilityManager.sendAccessibilityEvent(event);    return true;}复制代码

重点:AccessibilityManager#sendAccessibilityEvent(event)

public void sendAccessibilityEvent(AccessibilityEvent event) {    final IAccessibilityManager service;    final int userId;    synchronized(mLock) {        service = getServiceLocked();        if (service == null) {            return;        }        if (!mIsEnabled) {            Looper myLooper = Looper.myLooper();            if (myLooper == Looper.getMainLooper()) {                throw new IllegalStateException("Accessibility off. Did you forget to check that?");            } else {                // If we're not running on the thread with the main looper, it's possible for                // the state of accessibility to change between checking isEnabled and                // calling this method. So just log the error rather than throwing the                // exception.                Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");                return;            }        }        userId = mUserId;    }    boolean doRecycle = false;    try {        event.setEventTime(SystemClock.uptimeMillis());        // it is possible that this manager is in the same process as the service but        // client using it is called through Binder from another process. Example: MMS        // app adds a SMS notification and the NotificationManagerService calls this method        long identityToken = Binder.clearCallingIdentity();        // !!!        doRecycle = service.sendAccessibilityEvent(event, userId);        Binder.restoreCallingIdentity(identityToken);        if (DEBUG) {            Log.i(LOG_TAG, event + " sent");        }    } catch (RemoteException re) {        Log.e(LOG_TAG, "Error during sending " + event + " ", re);    } finally {        if (doRecycle) {            event.recycle();        }    }}private IAccessibilityManager getServiceLocked() {    if (mService == null) {        tryConnectToServiceLocked(null);    }    return mService;}private void tryConnectToServiceLocked(IAccessibilityManager service) {    if (service == null) {        IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);        if (iBinder == null) {            return;        }        service = IAccessibilityManager.Stub.asInterface(iBinder);    }    try {        final int stateFlags = service.addClient(mClient, mUserId);        setStateLocked(stateFlags);        mService = service;    } catch (RemoteException re) {        Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);    }}复制代码

这里有使用到Android Binder机制,重点为IAccessibilityManager#sendAccessibilityEvent()方法,这里调用的是代理方法,实际代码逻辑在AccessibilityManagerService#sendAccessibilityEvent():

@Overridepublic boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {    synchronized(mLock) {        // We treat calls from a profile as if made by its parent as profiles        // share the accessibility state of the parent. The call below        // performs the current profile parent resolution..        final int resolvedUserId = mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId);        // This method does nothing for a background user.        if (resolvedUserId != mCurrentUserId) {            return true; // yes, recycle the event        }        if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {            mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(), event.getSourceNodeId(), event.getEventType(), event.getAction());            mSecurityPolicy.updateEventSourceLocked(event);            // !!!            notifyAccessibilityServicesDelayedLocked(event, false);            notifyAccessibilityServicesDelayedLocked(event, true);        }        if (mHasInputFilter && mInputFilter != null) {            mMainHandler.obtainMessage(MainHandler.MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER, AccessibilityEvent.obtain(event)).sendToTarget();        }        event.recycle();    }    return (OWN_PROCESS_ID != Binder.getCallingPid());}private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) {    try {        UserState state = getCurrentUserStateLocked();        for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {            Service service = state.mBoundServices.get(i);            if (service.mIsDefault == isDefault) {                if (canDispatchEventToServiceLocked(service, event)) {                    service.notifyAccessibilityEvent(event);                }            }        }    } catch (IndexOutOfBoundsException oobe) {        // An out of bounds exception can happen if services are going away        // as the for loop is running. If that happens, just bail because        // there are no more services to notify.    }}复制代码
  1. 在方法中,最后会调用notifyAccessibilityServicesDelayedLocked()方法,然后将event进行回收;
  2. 在notifyAccessibilityServicesDelayedLocked()方法中,会获得所有Bound即绑定的Service,执行notifyAccessibilityEvent()方法,通过跟踪代码逻辑,最后会调用绑定Service的onAccessibilityEvent()方法。绑定的Service是指我们自己实现的继承于AccessibilityService的Service类,当你在设置-无障碍中开启服务之后即将服务绑定到AccessibilityManagerService中。

这样我们解决了第二个问题: AccessibilityService是如何做到监控捕捉用户行为的:(以点击事件为例) AccessibilityEvent产生: View#performClick()   -> View#sendAccessibilityEventUncheckedInternal()    -> ViewGroup#requestSendAccessibilityEvent()     -> ViewRootImpl#requestSendAccessibilityEvent()      -> AccessibilityManager#sendAccessibilityEvent(event)       -> AccessibilityManagerService#sendAccessibilityEvent()        -> AccessibilityManagerService#notifyAccessibilityServicesDelayedLocked()         -> Service#notifyAccessibilityEvent(event)

AccessibilityEvent处理: AccessibilityEvent   -> Binder驱动    -> IAccessibilityServiceClientWrapper#onAccessibilityEvent(AccessibilityEvent)     -> HandlerCaller#sendMessage(message); // message中包括AccessibilityEvent      -> IAccessibilityServiceClientWrapper#executeMessage();       -> Callbacks#onAccessibilityEvent(event);        -> AccessibilityService.this.onAccessibilityEvent(event);

AccessibilityService交互之查找控件

在demo中,我们在MyAccessibilityService中调用了getRootInActiveWindow()方法获取被监控的View的所有结点,这些结点都封装成一个AccessibilityNodeInfo对象中。同时也调用AccessibilityNodeInfo#findAccessibilityNodeInfosByText()方法查找相应的控件。 这些方法的本质是调用了AccessibilityInteractionClient类的对应方法。 以AccessibilityInteractionClient#findAccessibilityNodeInfosByText()为例:

public List
findAccessibilityNodeInfosByText(int connectionId, int accessibilityWindowId, long accessibilityNodeId, String text) { try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); final boolean success = connection.findAccessibilityNodeInfosByText(accessibilityWindowId, accessibilityNodeId, text, interactionId, this, Thread.currentThread().getId()); Binder.restoreCallingIdentity(identityToken); if (success) { List
infos = getFindAccessibilityNodeInfosResultAndClear(interactionId); if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); return infos; } } } else { if (DEBUG) { Log.w(LOG_TAG, "No connection for connection id: " + connectionId); } } } catch (RemoteException re) { Log.w(LOG_TAG, "Error while calling remote" + " findAccessibilityNodeInfosByViewText", re); } return Collections.emptyList();}复制代码

代码逻辑比较简单,就是直接调用IAccessibilityServiceConnection#findAccessibilityNodeInfosByText()方法。 IAccessibilityServiceConnection是一个aidl接口,从注释看,它是AccessibilitySerivce和AccessibilityManagerService之间沟通的桥梁。 猜想代码真正的实现在AccessibilityManagerService中。 AccessibilityManagerService.Service#findAccessibilityNodeInfosByText():

@Overridepublic boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException {    final int resolvedWindowId;    IAccessibilityInteractionConnection connection = null;    Region partialInteractiveRegion = Region.obtain();    synchronized(mLock) {        if (!isCalledForCurrentUserLocked()) {            return false;        }        resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);        final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);        if (!permissionGranted) {            return false;        } else {            connection = getConnectionLocked(resolvedWindowId);            if (connection == null) {                return false;            }        }        if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(resolvedWindowId, partialInteractiveRegion)) {            partialInteractiveRegion.recycle();            partialInteractiveRegion = null;        }    }    final int interrogatingPid = Binder.getCallingPid();    final long identityToken = Binder.clearCallingIdentity();    MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);    try {        connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, spec);        return true;    } catch (RemoteException re) {        if (DEBUG) {            Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");        }    } finally {        Binder.restoreCallingIdentity(identityToken);        // Recycle if passed to another process.        if (partialInteractiveRegion != null && Binder.isProxy(connection)) {            partialInteractiveRegion.recycle();        }    }    return false;}复制代码
  1. 此方法在AccessibilityManagerService的内部类Service中实现,这个Service继承于IAccessibilityServiceConnection.Stub,验证了我上面的猜想是正确的;
  2. 代码重点是调用connection.findAccessibilityNodeInfosByText(),这里的connection实例与上面不同,它隶属于IAccessibilityInteractionConnection类。这个类同样是一个aidl接口,从注释上看,它又是AccessibilityManagerService与指定窗口的ViewRoot之间沟通的桥梁。 再次猜想,真正的代码逻辑在ViewRootImpl中。 查看ViewRootImpl.AccessibilityInteractionConnection#findAccessibilityNodeInfosByText():
@Overridepublic void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {    ViewRootImpl viewRootImpl = mViewRootImpl.get();    if (viewRootImpl != null && viewRootImpl.mView != null) {        viewRootImpl.getAccessibilityInteractionController().findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, interactiveRegion, interactionId, callback, flags, interrogatingPid, interrogatingTid, spec);    } else {        // We cannot make the call and notify the caller so it does not wait.        try {            callback.setFindAccessibilityNodeInfosResult(null, interactionId);        } catch (RemoteException re) {            /* best effort - ignore */        }    }}复制代码
  1. 同样的,此方法在ViewRootImpl的内部类AccessibilityInteractionConnection中实现,这个内部类继承于IAccessibilityServiceConnection.Stub,验证了我的猜想;
  2. 查找控件等操作,ViewRootImpl并不是直接处理,而是交给AccessibilityInteractionController类去查找,查找到的结果会保存到一个callback中,这个callback为IAccessibilityInteractionConnectionCallback类型,它也是一个aidl接口,而AccessibilityInteractionClient类继承了IAccessibilityInteractionConnectionCallback.Stub,即最后查询后的结果会回调到AccessibilityInteractionClient类中,如上面AccessibilityInteractionClient#findAccessibilityNodeInfosByText()方法,最后会调用getFindAccessibilityNodeInfosResultAndClear()方法获取结果。具体如何寻找指定控件则不再分析代码。
AccessibilityService交互之执行控件操作

类似的,与上面的流程基本相同,只是回调的时候,返回的是执行操作的返回值(True or False)。

到这里,我们解决了最后一个问题: AccessibilityService是如何做到查找控件,执行点击等操作的? 总结: 寻找指定控件/执行操作   -> 交给AccessibilityInteractionClient类处理     -> Binder       -> AccessibilityManagerService类进行查找/执行操作         -> Binder           -> 指定窗口的ViewRoot(ViewRootImpl)进行查找/执行操作         <- Binder     <- 结果回调到AccessibilityInteractionClient类

四、有用代码记录

  1. HandlerCaller类:结合Handler类和自定义的接口类(Caller.java),利用Handler的消息循环机制来分发消息,将最终的处理函数交给Caller#executeMessage():
// HandlerCaller.javapublic class HandlerCaller {    final Looper mMainLooper;    final Handler mH;    final Callback mCallback;    class MyHandler extends Handler {        MyHandler(Looper looper, boolean async) {            super(looper, null, async);        }        @Override        public void handleMessage(Message msg) {            mCallback.executeMessage(msg);        }    }    public interface Callback {        public void executeMessage(Message msg);    }    public HandlerCaller(Context context, Looper looper, Callback callback,            boolean asyncHandler) {        mMainLooper = looper != null ? looper : context.getMainLooper();        mH = new MyHandler(mMainLooper, asyncHandler);        mCallback = callback;    }    public Handler getHandler() {        return mH;    }    public void executeOrSendMessage(Message msg) {        // If we are calling this from the main thread, then we can call        // right through.  Otherwise, we need to send the message to the        // main thread.        if (Looper.myLooper() == mMainLooper) {            mCallback.executeMessage(msg);            msg.recycle();            return;        }                mH.sendMessage(msg);    }    public void sendMessageDelayed(Message msg, long delayMillis) {        mH.sendMessageDelayed(msg, delayMillis);    }    public boolean hasMessages(int what) {        return mH.hasMessages(what);    }        public void removeMessages(int what) {        mH.removeMessages(what);    }        public void removeMessages(int what, Object obj) {        mH.removeMessages(what, obj);    }        public void sendMessage(Message msg) {        mH.sendMessage(msg);    }    public SomeArgs sendMessageAndWait(Message msg) {        if (Looper.myLooper() == mH.getLooper()) {            throw new IllegalStateException("Can't wait on same thread as looper");        }        SomeArgs args = (SomeArgs)msg.obj;        args.mWaitState = SomeArgs.WAIT_WAITING;        mH.sendMessage(msg);        synchronized (args) {            while (args.mWaitState == SomeArgs.WAIT_WAITING) {                try {                    args.wait();                } catch (InterruptedException e) {                    return null;                }            }        }        args.mWaitState = SomeArgs.WAIT_NONE;        return args;    }    public Message obtainMessage(int what) {        return mH.obtainMessage(what);    }         // 省略部分代码}复制代码
  1. HandlerCaller#sendMessageAndWait():
public SomeArgs sendMessageAndWait(Message msg) {    if (Looper.myLooper() == mH.getLooper()) {        throw new IllegalStateException("Can't wait on same thread as looper");    }    SomeArgs args = (SomeArgs) msg.obj;    args.mWaitState = SomeArgs.WAIT_WAITING;    mH.sendMessage(msg);    synchronized(args) {        while (args.mWaitState == SomeArgs.WAIT_WAITING) {            try {                args.wait();            } catch (InterruptedException e) {                return null;            }        }    }    args.mWaitState = SomeArgs.WAIT_NONE;    return args;}复制代码

这篇文章会同步到我的,如有问题,请大家踊跃提出,谢谢大家!

转载地址:http://yxcta.baihongyu.com/

你可能感兴趣的文章
Oracle数据库的语句级读一致性
查看>>
Cannot run Eclipse; JVM terminated. Exit code=13
查看>>
sort与sorted
查看>>
linux下安装和卸载vmware产品
查看>>
Linux系统(一)文件系统、压缩、打包操作总结
查看>>
微信小程序把玩(四十)animation API
查看>>
Android Application中的Context和Activity中的Context的异同
查看>>
MyBatis接口的简单实现原理
查看>>
从0移植uboot (二) _uboot启动流程分析
查看>>
C++异常实现与longjmp, setjmp,栈指针EBP, Active Record
查看>>
Python高级特性(切片,迭代,列表生成式,生成器,迭代器)
查看>>
CISCO知识扫盲
查看>>
[原创]浅谈对华为34岁以上员工“退休”
查看>>
一个hadoop hdfs put 文件失败的小情况
查看>>
C语言 · 计算时间
查看>>
JavaEE开发之Spring中的依赖注入与AOP编程
查看>>
spi flash偶尔出现写入错误的情况
查看>>
Native SBS for Android
查看>>
vue过渡和animate.css结合使用
查看>>
C#编程(七十四)----------释放非托管资源
查看>>