Android AccessibilityService 事件分发原理分析总结

目录
  • AccessibilityService 监听事件的调用逻辑
    • onAccessibilityEvent
    • onIntercept
  • AccessibilityService 事件的外部来源
    • AccessibilityServiceInfo
    • AccessibilityManager
    • AccessibilityManagerService
    • AccessibilityServiceConnection

前言:

在了解了无障碍服务基础使用之后,我们来探究一下 AccessibilityService 的事件接收方法回调的时机和它深层次的实现逻辑。

AccessibilityService 监听事件的调用逻辑

AccessibilityService 有很多用来接收外部调用事件变化的方法,这些方法封装在内部接口 Callbacks 中:

public interface Callbacks {
    void onAccessibilityEvent(AccessibilityEvent event);
    void onInterrupt();
    void onServiceConnected();
    void init(int connectionId, IBinder windowToken);
    boolean onGesture(AccessibilityGestureEvent gestureInfo);
    boolean onKeyEvent(KeyEvent event);
    void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY);
    void onSoftKeyboardShowModeChanged(int showMode);
    void onPerformGestureResult(int sequence, boolean completedSuccessfully);
    void onFingerprintCapturingGesturesChanged(boolean active);
    void onFingerprintGesture(int gesture);
    void onAccessibilityButtonClicked(int displayId);
    void onAccessibilityButtonAvailabilityChanged(boolean available);
    void onSystemActionsChanged();
}

以最常用的 onAccessibilityEvent 为例,介绍一下调用流程。

onAccessibilityEvent 在 AccessibilityService 中,AccessibilityService 在 onBind 生命周期中,返回了一个IAccessibilityServiceClientWrapper 对象,它是一个 Binder ,所以外部实际上通过 Binder 机制跨进程调用到无障碍服务的。

外部通过 Binder 调用到 Service 具体的实现方法的调用栈是:

- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#onAccessibilityEvent
- frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#sendMessage
- frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#Callback#executeMessage
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#executeMessage
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#Callbacks#onAccessibilityEvent
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#onAccessibilityEvent

首先是外部调用到 IAccessibilityServiceClientWrapper 的 onAccessibilityEvent 方法:

// IAccessibilityServiceClientWrapper
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
    Message message = mCaller.obtainMessageBO(
            DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
    mCaller.sendMessage(message);
}

在这个方法中通过 HandlerCaller 切换到主线程,然后发送了一个消息。

这里的 HandlerCaller 的源码是:

public 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;
    }
    ...
}

从它的源码中可以看出,这是一个向主线程发消息的 Handler 。 在主线程中执行它的内部类 Callback 的 executeMessage 方法。

IAccessibilityServiceClientWrapper 实现了 AccessibilityService.Callback接口,所以调用到了IAccessibilityServiceClientWrapper.executeMessage 方法中。IAccessibilityServiceClientWrapper 对象的创建是在 onBind 生命周期中。它接收一个 AccessibilityService.Callback 对象作为 IAccessibilityServiceClientWrapper 的构造参数:

@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);
        }
	...
});

IAccessibilityServiceClientWrapper 中的 executeMessage中,根据不同的 Handler 消息调用了 AccessibilityService.Callback 的对应方法:

public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback {
    private final HandlerCaller mCaller;
    public IAccessibilityServiceClientWrapper(Context context, Looper looper,
        Callbacks callback) {
        // ...
        mCaller = new HandlerCaller(context, looper, this, true);
    }
    @Override
    public void executeMessage(Message message) {
        switch (message.what) {
            case DO_ON_ACCESSIBILITY_EVENT: {
                // ...
                mCallback.onAccessibilityEvent(event);
                return;
            }
            case DO_ON_INTERRUPT: {
                // ...
                mCallback.onInterrupt();
                return;
            }
            default :
                Log.w(LOG_TAG, "Unknown message type " + message.what);
        }
    }
}

而刚才传入的 AccessibilityService.Callback 方法的实现中,调用了AccessibilityService 的 onAccessibilityEvent 方法:

        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            AccessibilityService.this.onAccessibilityEvent(event);
        }

这样整个调用链就清晰了:

  • 外部通过 Binder 机制调用到 AccessibilityService 的内部 Binder 代理实现IAccessibilityServiceClientWrapper对象
  • IAccessibilityServiceClientWrapper对象内部通过 Handler 机制切换到主线程执行 AccessibilityService.Callback 中对应的方法。
  • AccessibilityService.Callback 中的方法调用到了AccessibilityService 对应的生命周期方法。

接下来关注一下一些重要的事件接收方法。

onAccessibilityEvent

Handler 中的 DO_ON_ACCESSIBILITY_EVENT 事件会调用到 onAccessibilityEvent 。 在 executeMessage(Message message) 方法中的逻辑是:

case DO_ON_ACCESSIBILITY_EVENT: {
    AccessibilityEvent event = (AccessibilityEvent) message.obj;
    boolean serviceWantsEvent = message.arg1 != 0;
    if (event != null) {
        // Send the event to AccessibilityCache via AccessibilityInteractionClient
        AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
                event);
        if (serviceWantsEvent
                && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
            // Send the event to AccessibilityService
            mCallback.onAccessibilityEvent(event);
        }
        // Make sure the event is recycled.
        try {
            event.recycle();
        } catch (IllegalStateException ise) {
            /* ignore - best effort */
        }
    }
    return;
}
  • 取出 message.obj 转换为 AccessibilityEvent ,并根据 message.arg1检查 Service 是否想要处理这个事件。
  • 检查 AccessibilityEvent对象是否为 null,为空直接 return
  • 将 AccessibilityEvent对象通过 AccessibilityInteractionClient 加入到 AccessibilityCache 缓存中,然后根据 service 是否要处理事件和AccessibilityInteractionClient连接状态,决定是否要将事件发送给 AccessibilityService
  • 最后回收事件对象。

这里的AccessibilityInteractionClient连接状态检查时通过 mConnectionId 属性来判断的,在IAccessibilityServiceClientWrapper 的 init 时被赋值,init 也是通过 Handler 传递来的消息切换到主线程进行的:

case DO_INIT: {
    mConnectionId = message.arg1;
    SomeArgs args = (SomeArgs) message.obj;
    IAccessibilityServiceConnection connection =
            (IAccessibilityServiceConnection) args.arg1;
    IBinder windowToken = (IBinder) args.arg2;
    args.recycle();
    if (connection != null) {
        AccessibilityInteractionClient.getInstance(mContext).addConnection(
                mConnectionId, connection);
        mCallback.init(mConnectionId, windowToken);
        mCallback.onServiceConnected();
    } else {
        AccessibilityInteractionClient.getInstance(mContext).removeConnection(
                mConnectionId);
        mConnectionId = AccessibilityInteractionClient.NO_ID;
        AccessibilityInteractionClient.getInstance(mContext).clearCache();
        mCallback.init(AccessibilityInteractionClient.NO_ID, null);
    }
    return;
}

onIntercept

onAccessibilityEvent 事件一样都是通过 Handler 机制进行处理的:

case DO_ON_INTERRUPT: {
    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
        mCallback.onInterrupt();
    }
    return;
}

只检查了与AccessibilityInteractionClient 的连接状态。

AccessibilityService 事件的外部来源

经过上面的分析,我们知道外部通过 binder 机制触发了 AccessibilityService 的事件监听方法,那么它们来自哪里呢? 接下来从 AccessibilityServiceInfo 开始,分析系统事件是如何传递到无障碍服务的。

AccessibilityServiceInfo

AccessibilityServiceInfo 用来描述 AccessibilityService 。系统根据这个类中的信息,将 AccessibilityEvents 通知给一个 AccessibilityService。

AccessibilityServiceInfo 中定义了一些属性,用来控制无障碍服务的一些权限和能力。我们在 AndroidManifest.xml 中为无障碍服务指定的meta-data 标签中,指定的配置文件中的配置,和AccessibilityServiceInfo 中的属性一一对应。

AccessibilityServiceInfo 的引用:

查看 AccessibilityServiceInfo 的引用栈,发现有很多地方都用到了这个类,关于无障碍的重点看 AccessibilityManagerService 和 AccessibilityManager 这一套逻辑。 从名称上看,无障碍功能提供了类似 AMS 一样的系统服务,并通过一个 Manager 类来进行调用。

AccessibilityManager

AccessibilityManager 是一个系统服务管理器,用来分发 AccessibilityEvent 事件。当用户界面中发生一些值得注意的事件时,例如焦点变化和 Activity 启动等,会生成这些事件。

AccessibilityManager 内部有一个看起来与发送消息有关的方法:

public void sendAccessibilityEvent(AccessibilityEvent event) {
        final IAccessibilityManager service;
        final int userId;
        final AccessibilityEvent dispatchedEvent;
        synchronized (mLock) {
            service = getServiceLocked();
            if (service == null) return;
            event.setEventTime(SystemClock.uptimeMillis());
            if (event.getAction() == 0) {
                event.setAction(mPerformingAction);
            }
            if (mAccessibilityPolicy != null) {
                dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, mIsEnabled, mRelevantEventTypes);
                if (dispatchedEvent == null) return;
            } else {
                dispatchedEvent = event;
            }
            if (!isEnabled()) {
                Looper myLooper = Looper.myLooper();
                if (myLooper == Looper.getMainLooper()) {
                    throw new IllegalStateException("Accessibility off. Did you forget to check that?");
                } else {
                    // 当不是在主线程(mainLooper)运行时,调用检查无障碍开启状态可能会异常。因此直接抛出异常
                    Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
                    return;
                }
            }
            userId = mUserId;
        }
        try {
            final long identityToken = Binder.clearCallingIdentity();
            try {
                service.sendAccessibilityEvent(dispatchedEvent, userId);
            } finally {
                Binder.restoreCallingIdentity(identityToken);
            }
            if (DEBUG) {
                Log.i(LOG_TAG, dispatchedEvent + " sent");
            }
        } catch (RemoteException re) {
            Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
        } finally {
            if (event != dispatchedEvent) {
                event.recycle();
            }
            dispatchedEvent.recycle();
        }
    }

这个方法是用来发送一个 AccessibilityEvent 事件的,简化里面的逻辑:

- 加锁
- getServiceLocked() 获取 service 对象,service 获取不到直接 return
- event 设置一个时间,然后设置 action
- 检查 AccessibilityPolicy 对象是否为 null
  - 不为空,dispatchedEvent 根据 AccessibilityPolicy 的 onAccessibilityEvent(event) 赋值,赋值后仍为空直接 return
  - 为空, dispatchedEvent = event
- 检查系统是否开启无障碍功能
- 解锁
- try
  - try
    - service.sendAccessibilityEvent(dispatchedEvent, userId); 通过 AccessibilityManagerService 的 sendAccessibilityEvent 发送事件
  - finally
    - Binder.restoreCallingIdentity(identityToken);
- finally
  - 回收 event 和 dispatchedEvent 对象

这里 getServiceLocked() 内部调用了 tryConnectToServiceLocked 方法:

    private void tryConnectToServiceLocked(IAccessibilityManager service) {
        if (service == null) {
            IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
            if (iBinder == null) {
                return;
            }
            service = IAccessibilityManager.Stub.asInterface(iBinder);
        }
		  // ...
    }

而 AccessibilityManagerService 实现了 IAccessibilityManager.Stub ,所以这里的 service 时 AccessibilityManagerService。 这里调用了 service.sendAccessibilityEvent(dispatchedEvent, userId);

通过 Binder 机制,调用到的是 AccessibilityManagerService 里的 sendAccessibilityEvent 方法。

AccessibilityManager.sendAccessibilityEvent 的调用位置有很多,其中比较显眼的是在 ViewRootImpl 中的,因为 ViewRootImpl 是 View 添加到 Window 的重要实现类。sendAccessibilityEvent 在 ViewRootImpl 的内部类中存在调用:

    @Override
    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
        // ...
        final int eventType = event.getEventType();
        final View source = getSourceForAccessibilityEvent(event);
        switch (eventType) {
            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
                if (source != null) {
                    AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
                    if (provider != null) {
                        final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId());
                        final AccessibilityNodeInfo node;
                        node = provider.createAccessibilityNodeInfo(virtualNodeId);
                        setAccessibilityFocus(source, node);
                    }
                }
            } break;
            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
                if (source != null && source.getAccessibilityNodeProvider() != null) {
                    setAccessibilityFocus(null, null);
                }
            } break;
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
                handleWindowContentChangedEvent(event);
            } break;
        }
        mAccessibilityManager.sendAccessibilityEvent(event);
        return true;
    }

这是一个 override 方法,它的定义在接口 ViewParent 中。 这个方法的调用栈很多:

可以跟到 View 中存在的同名方法 :

public void sendAccessibilityEvent(int eventType) {
    if (mAccessibilityDelegate != null) {
        mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
    } else {
        sendAccessibilityEventInternal(eventType);
    }
}

它在 View 中的调用:

可以看出,常见的 View 的事件,包括:点击、长按、焦点变化等,都会调用sendAccessibilityEvent方法。

在 ViewRootImpl中,AccessibilityManager也存在很多处调用逻辑:

可以看出在 Android 在 View 体系中,提供了很多对无障碍能力的支持。所有的 View 的事件都会被系统的无障碍服务捕获到。

回到调用逻辑,AccessibilityManager内部调用到的是AccessibilityManagerService里的sendAccessibilityEvent方法。下面介绍 AccessibilityManagerService里面的流程。

AccessibilityManagerService

sendAccessibilityEvent伪代码逻辑:

synchronized {
	1. 解析配置文件中的属性,并对其进行配置
	2. 设置配置的包名范围
}
if (dispatchEvent) {
	3. 确保接收此事件的 client 能够获取 window 当前的状态,因为 Window Manager 可能会出于性能原因延迟计算,通过配置 shouldComputeWindows = true/false
	if (shouldComputeWindows) {
		4. 获取 WindowManagerInternal wm
		5. wm.computeWindowsForAccessibility(displayId);
	}
	synchoronized {
		notifyAccessibilityServicesDelayedLocked(event, false)
		notifyAccessibilityServicesDelayedLocked(event, true)
		mUiAutomationManager.sendAccessibilityEventLocked(event);
	}
}
...

最后的关键三行代码中,调用了两个方法:

notifyAccessibilityServicesDelayedLocked :

    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) {
        try {
            AccessibilityUserState state = getCurrentUserStateLocked();
            for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
                AccessibilityServiceConnection service = state.mBoundServices.get(i);
                if (service.mIsDefault == isDefault) {
                    service.notifyAccessibilityEvent(event);
                }
            }
        } catch (IndexOutOfBoundsException oobe) {}
    }

AccessibilityManagerService从这个方法中,调用 AccessibilityServiceConnection的同名方法notifyAccessibilityEvent 。

这个意思是,先通知service.mIsDefault = false的无障碍服务连接发送事件,然后再通知 等于 true 的无障碍服务连接发送事件。

mUiAutomationManager.sendAccessibilityEventLocked(event)

mUiAutomationManager的类型是UiAutomationManager,它的sendAccessibilityEventLocked方法实现是:

void sendAccessibilityEventLocked(AccessibilityEvent event) {
    if (mUiAutomationService != null) {
        mUiAutomationService.notifyAccessibilityEvent(event);
    }
}

mUiAutomationService的类型是 UiAutomationService,它是UiAutomationManager的内部类,继承自AbstractAccessibilityServiceConnection,内部操作都是切换到主线程进行的,notifyAccessibilityEvent方法的实现在父类中,后续会和上面的AccessibilityServiceConnection一起说明。

AccessibilityServiceConnection

此类用来表示一个无障碍服务。 它存储着服务管理所需的所有每个服务数据,提供用于启动/停止服务的 API,并负责在服务管理的数据结构中添加/删除服务。 该类还公开了配置接口,该接口在绑定后立即传递给它所代表的服务。 它还用作服务的连接。

AccessibilityServiceConnectionUiAutomationService 一样,继承自AbstractAccessibilityServiceConnection

它们的notifyAccessibilityEvent方法,在AbstractAccessibilityServiceConnection中:

    public void notifyAccessibilityEvent(AccessibilityEvent event) {
        synchronized (mLock) {
            ...
            // copy 一个副本,因为在调度期间,如果接收的服务没有访问窗口内容的权限,则可能会修改并删除事件。
            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
            Message message;
            if ((mNotificationTimeout > 0)
                    && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
                // 最多允许一个待处理事件
                final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
                mPendingEvents.put(eventType, newEvent);
                if (oldEvent != null) {
                    mEventDispatchHandler.removeMessages(eventType);
                    oldEvent.recycle();
                }
                message = mEventDispatchHandler.obtainMessage(eventType);
            } else {
                // 发送所有消息,绕过 mPendingEvents
                message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
            }
            message.arg1 = serviceWantsEvent ? 1 : 0;

            mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
        }
    }

这个方法中,通过一个 Handler 来处理和发送消息。

        mEventDispatchHandler = new Handler(mainHandler.getLooper()) {
            @Override
            public void handleMessage(Message message) {
                final int eventType =  message.what;
                AccessibilityEvent event = (AccessibilityEvent) message.obj;
                boolean serviceWantsEvent = message.arg1 != 0;
                notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);
            }
        };

内部调用notifyAccessibilityEventInternal

private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event, boolean serviceWantsEvent) {
    IAccessibilityServiceClient listener;
    synchronized (mLock) {
        listener = mServiceInterface;
        // 如果在消息分发无障碍事件时 service die 或 关闭,listener 可能为空
        if (listener == null) return;
        // 我们有两种通知事件的方式,节流和非节流。 如果我们不进行节流,那么消息会随事件一起出现,我们会毫不费力地处理这些事件。
        if (event == null) {
            // 我们正在限制事件,所以只要它为空,我们就会在 mPendingEvents 中发送这种类型的事件。 由于竞争条件,它只能为空:
            //   1) 一个 binder 线程调用 notifyAccessibilityServiceDelayedLocked,它发布一条用于调度事件的消息并将该事件存储在 mPendingEvents 中。
            //   2) 消息由服务线程上的处理程序从队列中拉出,此方法即将获取锁。
            //   3) 另一个 binder 线程在 notifyAccessibilityEvent 中获取锁
            //   4) notifyAccessibilityEvent 回收该方法即将处理的事件,替换为新的,并发布第二条消息
            //   5) 此方法抓取新事件,对其进行处理,然后将其从 mPendingEvents 中删除
            //   6) (4) 中发送的第二条消息到达,但事件已在 (5) 中删除。
            event = mPendingEvents.get(eventType);
            if (event == null) {
                return;
            }
            mPendingEvents.remove(eventType);
        }
        if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
            event.setConnectionId(mId);
        } else {
            event.setSource((View) null);
        }
        event.setSealed(true);
    }

    try {
        listener.onAccessibilityEvent(event, serviceWantsEvent);

    } catch (RemoteException re) {} finally {
        event.recycle();
    }
}

备注中说明了消息处理和分发逻辑,但我们这里只需要关注最后的:

listener.onAccessibilityEvent(event, serviceWantsEvent);

这里的 listener 是一个IAccessibilityServiceClient,是个 AIDL 文件。这个 AIDL 在我们的 AccessibilityService 中有实现类!

调用到这个IAccessibilityServiceClient , 就能调用到AccessibilityService中的代码了。

至此无障碍服务的调用,从系统的 View 和其他事件位置,经过AccessibilityManager.notifyAccessibilityEvent用 Binder 机制调用AccessibilityManagerService.notifyAccessibilityEvent ;然后AccessibilityManagerService内部通过调用AccessibilityServiceConnection.notifyAccessibilityEvent来调用我们可以实现的AccessibilityService中接收事件。

到此这篇关于Android AccessibilityService 事件分发原理分析总结的文章就介绍到这了,更多相关Android AccessibilityService 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android AccessibilityService实现微信抢红包插件

    在你的手机更多设置或者高级设置中,我们会发现有个无障碍的功能,很多人不知道这个功能具体是干嘛的,其实这个功能是为了增强用户界面以帮助残障人士,或者可能暂时无法与设备充分交互的人们 它的具体实现是通过AccessibilityService服务运行在后台中,通过AccessibilityEvent接收指定事件的回调.这样的事件表示用户在界面中的一些状态转换,例如:焦点改变了,一个按钮被点击,等等.这样的服务可以选择请求活动窗口的内容的能力.简单的说AccessibilityService就是一个后

  • Android实现微信自动向附近的人打招呼(AccessibilityService)

    学习功能强大的AccessibilityService!!! 以下是本人根据自动抢红包的实现思路敲的用于微信自动向附近的人打招呼的核心代码 public class AutoService extends AccessibilityService implements View.OnClickListener { private static final String TAG = "test"; /** * 微信的包名 */ static final String WECHAT_PAC

  • Android基于AccessibilityService制作的钉钉自动签到程序代码

    前两天公司开始宣布要使用阿里钉钉来签到啦!!!~~这就意味着,我必须老老实实每天按时签到上班下班了,这真是一个悲伤的消息,可是!!!!那么机智(lan)的我,怎么可能就这么屈服!!!阿里钉钉签到,说到底不就是手机软件签到吗?我就是干移动开发的,做一个小应用每天自动签到不就行了:) 说干就干,首先分析一下,阿里钉钉的签到流程: 打开阿里钉钉->广告页停留2S左右->进入主页->点击"工作"tab->点击"签到"模块->进入签到页面(可能会

  • Android辅助功能AccessibilityService与抢红包辅助

    推荐阅读:Android中微信抢红包插件原理解析及开发思路 抢红包的原理都差不多,一般是用Android的辅助功能(AccessibilityService类)先监听通知栏事件或窗口变化事件来查找红包关键字然后去模拟点击或打开红包. 下面附上源码,程序已实现自动抢红包,锁屏黑屏状态自动解锁亮屏,Android4.X测试通过.函数具体功能请看详细注释. 注:在聊天界面收到红包不会自动打开,因为通知栏没有消息提示从而监听不了,此时只需手动点一下即可.其他未知情况请自行用LogCat调试,源码已经有相

  • Android AccessibilityService 事件分发原理分析总结

    目录 AccessibilityService 监听事件的调用逻辑 onAccessibilityEvent onIntercept AccessibilityService 事件的外部来源 AccessibilityServiceInfo AccessibilityManager AccessibilityManagerService AccessibilityServiceConnection 前言: 在了解了无障碍服务基础使用之后,我们来探究一下 AccessibilityService

  • 关于Android触摸事件分发的原理详析

    目录 一:前言 二:说在前面的知识 三:整体流程 1:activity 2:window就是PhoneWindow 3:view group 4:view 四:一些关键点 五:从源码看触摸事件分发 总结 一:前言 最近在学Android的触摸事件分发,我觉得网上说的太杂太乱,而且有很多博客都有明显的错误.什么自顶向下分发,自下向顶分发,什么拦截又一直消费什么什么之类,非常难懂.为了自己将来回顾可以更好的理解这块知识,也为了后来之人可以更好的学习,我写下这篇博客. 二:说在前面的知识 点击,滑动,

  • Android ViewGroup事件分发和处理源码分析

    目录 正文 处理ACTION_DOWN事件 检测是否截断事件 不截断ACTION_DOWN事件 寻找处理事件的子View 事件分发给子View ViewGroup自己处理ACTION_DOWN事件 处理ACTION_DOWN总结 处理ACTION_MOVE事件 检测是否截断ACTION_MOVE事件 不截断ACTION_MOVE 事件分发给mFirstTouchTarget.child 截断ACTION_MOVE 处理 ACTION_UP 和 ACTION_CANCEL 事件 正确地使用requ

  • Android Touch事件分发过程详解

    本文以实例形式讲述了Android Touch事件分发过程,对于深入理解与掌握Android程序设计有很大的帮助作用.具体分析如下: 首先,从一个简单示例入手: 先看一个示例如下图所示: 布局文件 : <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id=&

  • Android View 事件分发机制详解

    Android开发,触控无处不在.对于一些 不咋看源码的同学来说,多少对这块都会有一些疑惑.View事件的分发机制,不仅在做业务需求中会碰到这些问题,在一些面试笔试题中也常有人问,可谓是老生常谈了.我以前也看过很多人写的这方面的文章,不是说的太啰嗦就是太模糊,还有一些在细节上写的也有争议,故再次重新整理一下这块内容,十分钟让你搞明白View事件的分发机制. 说白了这些触控的事件分发机制就是弄清楚三个方法,dispatchTouchEvent(),OnInterceptTouchEvent(),o

  • Android View事件分发机制详解

    准备了一阵子,一直想写一篇事件分发的文章总结一下,这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等,这些子View的外层还有ViewGroup,如RelativeLayout,LinearLayout.作为一个开发者,我们会思考,当点击一个按钮,Android系统是怎样确定我点的就是按钮而不是TextView的?然后还正确的响应了按钮的点击事件.内部经过了一系列什么过程呢? 先铺垫一些知识能更加清晰的理解事件分发机制: 1. 通过setC

  • Android View事件分发和消费源码简单理解

    Android View事件分发和消费源码简单理解 前言: 开发过程中觉得View事件这块是特别烧脑的,看了好久,才自认为看明白.中间上网查了下singwhatiwanna粉丝的读书笔记,有种茅塞顿开的感觉. 很重要的学习方法:化繁为简,只抓重点. 源码一坨,不要指望每一行代码都看懂.首先是没必要,其次大量非关键代码会让你模糊真正重要的部分. 以下也只是学姐的学习成果,各位同学要想理解深刻,还需要自己亲自去看源码. 2.源码分析 由于源码实在太长,而且也不容易看懂,学姐这里就不贴出来了,因为没必

  • Android之事件分发机制与冲突详解

    在日常的开发过程中,我们往往会在同一个界面中出现内外两层或者多层View同时滑动的现象,这个时候往往**会出现滑动冲突.面对滑动冲突,很多开发人员不知道从哪里入手,**即便稍微有点思路,也要费点时间才能解决.其实滑动冲突的解决是有一定的套路的. 下面主要针对Android开发中几种常见的滑动冲突及对应的解决方案来进行讲解 常见的滑动冲突场景! 目前常见的滑动冲突主要可以分为以下三种来概括: 外部滑动方向和内部View的滑动方向不一致,内部的View和外部的View的滑动方向是**互相垂直的,**

  • Android Touch事件分发深入了解

    本文带着大家深入学习触摸事件的分发,具体内容如下 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOWN.ACTION_MOVE.ACTION_UP.当用户手指接触屏幕时,便产生一个动作为ACTION_DOWN的触摸事件,此时若用户的手指立即离开屏幕,会产生一个动作为ACTION_UP的触摸事件:若用户手指接触屏幕后继续滑动,当滑动距离超过了系统中预定义的距离常数,则产生一个动作为ACTION_MOVE的触摸事件,系统中预定义的用来判断用户手指在屏幕上的滑动是

  • 谈谈对Android View事件分发机制的理解

    最近因为项目中用到类似一个LinearLayout中水平布局中,有一个TextView和Button,然后对该LinearLayout布局设置点击事件,点击TextView能够触发该点击事件,然而奇怪的是点击Button却不能触发.然后google到了解决办法(重写Button,然后重写其中的ontouchEvent方法,且返回值为false),但是不知道原因,这两天看了几位大神的博客,然后自己总结下. public class MyButton extends Button { private

随机推荐