通過(guò)源碼角度看看AccessibilityService
簡(jiǎn)介
AccessibilityService的設(shè)計(jì)初衷是為了輔助有身體缺陷的群體使用Android應(yīng)用,它的設(shè)計(jì)貫穿著Android的控件樹(shù)View, ViewGroup, ViewRootImpl體系。借助于system_server進(jìn)程的中轉(zhuǎn),能夠注冊(cè)Accessibility事件的客戶(hù)端可以具備通過(guò)system_server提供的Accessibility服務(wù)來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)、操作其它應(yīng)用視圖的功能。這個(gè)功能十分強(qiáng)大,可以模擬用戶(hù)的行為去操作其它APP,常常被用在自動(dòng)化測(cè)試、微信搶紅包、自動(dòng)回復(fù)等功能實(shí)現(xiàn)中。
寫(xiě)這個(gè)的初衷有二:
- 之前已經(jīng)完成了Android View控件樹(shù)的繪制、事件分發(fā)的源碼分析,知識(shí)儲(chǔ)備足夠
- 最近接觸到了一些自動(dòng)化方面的項(xiàng)目,并且對(duì)使用無(wú)障礙服務(wù)實(shí)現(xiàn)的自動(dòng)微信搶紅包功能原理十分好奇
整體圖
類(lèi)圖
- AccessibilityService: APP端直接繼承的類(lèi),本質(zhì)上是Service,通過(guò)onBind獲取匿名Binder對(duì)象實(shí)現(xiàn)通信
- IAccessibilityServiceClientWrapper: 用于和system_server通信的匿名Binder服務(wù)
- AccessibilityInteractionClient: 本質(zhì)上是個(gè)binder服務(wù),用于獲取Node信息
- AccessibilityManagerService: 運(yùn)行在system_server的實(shí)名binder服務(wù),是整體的管理類(lèi)
- Service: AccessibilityManagerService的內(nèi)部類(lèi),用于響應(yīng)AccessibilityInteractionClient的binder通信請(qǐng)求
- AccessibilityInteractionConnection: 運(yùn)行在被監(jiān)測(cè)的APP端,提供查找、點(diǎn)擊視圖等服務(wù)
- AccessibilityManager: 運(yùn)行在各個(gè)APP端,用于發(fā)送視圖變化事件
- AccessibilityInteractionController: 具體視圖查找、點(diǎn)擊服務(wù)的中間控制器
- AccessibilityNodeProvider: 由客戶(hù)端實(shí)現(xiàn)的視圖節(jié)點(diǎn)內(nèi)容提供者,最終操作的實(shí)現(xiàn)者
整體設(shè)計(jì)圖
實(shí)例代碼
public class AutoDismissService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event == null) { return; } // 自動(dòng)將android系統(tǒng)彈出的其它c(diǎn)rash dialog取消 dismissAppErrorDialogIfExists(event); } private void dismissAppErrorDialogIfExists(AccessibilityEvent event) { // WINDOW視圖變化才進(jìn)行對(duì)應(yīng)操作 if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && event.getPackageName().equals("android")) { // 查找?guī)в?OK"字符的可點(diǎn)擊Node AccessibilityNodeInfo nodeInfo = findViewByText("OK", true); if (nodeInfo != null) { // 查找到后執(zhí)行點(diǎn)擊操作 performViewClick(nodeInfo); } } public AccessibilityNodeInfo findViewByText(String text, boolean clickable) { // 獲取當(dāng)前窗口父節(jié)點(diǎn) AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow(); if (accessibilityNodeInfo == null) { return null; } // 獲取到滿(mǎn)足字符要求的節(jié)點(diǎn) List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text); if (nodeInfoList != null && !nodeInfoList.isEmpty()) { for (AccessibilityNodeInfo nodeInfo : nodeInfoList) { if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) { return nodeInfo; } } } return null; } public void performViewClick(AccessibilityNodeInfo nodeInfo) { if (nodeInfo == null) { return; } // 由下至上進(jìn)行查詢(xún),直到尋找到可點(diǎn)擊的節(jié)點(diǎn) while (nodeInfo != null) { if (nodeInfo.isClickable()) { nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); break; } nodeInfo = nodeInfo.getParent(); } } }
以上是一個(gè)典型的實(shí)現(xiàn)Accessibility功能的JAVA代碼,主要涉及三點(diǎn)功能:
- 當(dāng)系統(tǒng)中有應(yīng)用視圖變化后,onAccessibilityEvent 方法會(huì)自動(dòng)被system_server調(diào)用
- 通過(guò)AccessibilityService的getRootInActiveWindow與findAccessibilityNodeInfosByText方法,可以獲取到節(jié)點(diǎn)信息
- 通過(guò)AccessibilityNodeInfo的performAction方法,最終會(huì)在被監(jiān)聽(tīng)APP中執(zhí)行對(duì)應(yīng)操作
本篇文章將會(huì)圍繞著這三點(diǎn)主要功能進(jìn)行源碼分析
源碼分析
常見(jiàn) AccessibilityEvent 事件種類(lèi)
序號(hào) | 種類(lèi)名稱(chēng) | 觸發(fā)時(shí)機(jī) |
---|---|---|
1 | TYPE_VIEW_CLICKED | 可點(diǎn)擊的組件被點(diǎn)擊 |
2 | TYPE_VIEW_LONG_CLICKED | 可點(diǎn)擊的組件被長(zhǎng)按 |
3 | TYPE_VIEW_SELECTED | 組件被選中 |
4 | TYPE_VIEW_FOCUSED | 組件獲取到了焦點(diǎn) |
5 | TYPE_VIEW_TEXT_CHANGED | 組件中的文本發(fā)生變化 |
6 | TYPE_VIEW_SCROLLED | 組件被滑動(dòng) |
7 | TYPE_WINDOW_STATE_CHANGED | dialog等被打開(kāi) |
8 | TYPE_NOTIFICATION_STATE_CHANGED | 通知彈出 |
9 | TYPE_WINDOW_CONTENT_CHANGED | 組件樹(shù)發(fā)生了變化 |
onAccessibilityEvent 觸發(fā)流程
這里以TextView.setText觸發(fā)事件變化流程為例進(jìn)行分析
TextView.setText
應(yīng)用組件狀態(tài)發(fā)生變化
frameworks/base/core/java/android/widget/TextView.java
private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { ... notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT); ... } public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) { if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) { return; } if (mSendViewStateChangedAccessibilityEvent == null) { // 本質(zhì)上是一個(gè)Runnable,意味著這里的流程會(huì)進(jìn)入異步處理 mSendViewStateChangedAccessibilityEvent = new SendViewStateChangedAccessibilityEvent(); } mSendViewStateChangedAccessibilityEvent.runOrPost(changeType); } private class SendViewStateChangedAccessibilityEvent implements Runnable { ... @Override public void run() { mPosted = false; mPostedWithDelay = false; mLastEventTimeMillis = SystemClock.uptimeMillis(); if (AccessibilityManager.getInstance(mContext).isEnabled()) { final AccessibilityEvent event = AccessibilityEvent.obtain(); event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); event.setContentChangeTypes(mChangeTypes); // 最終TYPE_WINDOW_CONTENT_CHANGED事件在這里異步發(fā)送 sendAccessibilityEventUnchecked(event); } mChangeTypes = 0; } ... } public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { if (mAccessibilityDelegate != null) { mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event); } else { sendAccessibilityEventUncheckedInternal(event); } } public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { host.sendAccessibilityEventUncheckedInternal(event); } public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { if (!isShown()) { return; } ... // 此處交由TextView所在父View進(jìn)行處理,為責(zé)任鏈模式,事件經(jīng)過(guò)層層向上傳遞,最終交由ViewRootImpl進(jìn)行處理 ViewParent parent = getParent(); if (parent != null) { getParent().requestSendAccessibilityEvent(this, event); } }
ViewRootImpl.requestSendAccessibilityEvent
ViewRootImpl將事件派發(fā)到system_server
frameworks/base/core/java/android/view/ViewRootImpl.java
@Override public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { ... // 本地調(diào)用到AccessibilityManager進(jìn)行事件發(fā)送 mAccessibilityManager.sendAccessibilityEvent(event); return true; }
frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java
public void sendAccessibilityEvent(AccessibilityEvent event) { final IAccessibilityManager service; final int userId; synchronized (mLock) { // 獲取system_server的Accessibility實(shí)名服務(wù) service = getServiceLocked(); ... } try { ... long identityToken = Binder.clearCallingIdentity(); // binder call 到服務(wù)端,進(jìn)行事件分發(fā)中轉(zhuǎn) doRecycle = service.sendAccessibilityEvent(event, userId); Binder.restoreCallingIdentity(identityToken); ... } catch (RemoteException re) { Log.e(LOG_TAG, "Error during sending " + event + " ", re); } finally { ... } }
AccessibilityManagerService.sendAccessibilityEvent
system_server將事件分發(fā)到各個(gè)監(jiān)聽(tīng)組件變化的Service
frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
// binder call 到服務(wù)端,觸發(fā)事件派發(fā) @Override public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) { synchronized (mLock) { ... if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) { ... notifyAccessibilityServicesDelayedLocked(event, false); notifyAccessibilityServicesDelayedLocked(event, true); } ... } 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)) { // 調(diào)用內(nèi)部服務(wù),以觸發(fā)事件派發(fā) service.notifyAccessibilityEvent(event); } } } } catch (IndexOutOfBoundsException oobe) { ... } } class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection, DeathRecipient { public void notifyAccessibilityEvent(AccessibilityEvent event) { synchronized (mLock) { ... if ((mNotificationTimeout > 0) && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) { ... // 按照慣例,異步分發(fā)到客戶(hù)端進(jìn)行派發(fā) message = mEventDispatchHandler.obtainMessage(eventType); } else { message = mEventDispatchHandler.obtainMessage(eventType, newEvent); } mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout); } } } public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) { @Override public void handleMessage(Message message) { final int eventType = message.what; AccessibilityEvent event = (AccessibilityEvent) message.obj; notifyAccessibilityEventInternal(eventType, event); } }; private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event) { IAccessibilityServiceClient listener; ... // mServiceInterface是通過(guò)bind客戶(hù)端的AccessibilityService,在onServiceConnected連接成功后,獲取到binder proxy轉(zhuǎn)化來(lái)的,以這種方式實(shí)現(xiàn)了system_server與客戶(hù)端的通信 listener = mServiceInterface; ... try { listener.onAccessibilityEvent(event); if (DEBUG) { Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); } } catch (RemoteException re) { Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re); } finally { event.recycle(); } }
AccessibilityService.onAccessibilityEvent
APP接收到組件變化的事件,并可以選擇做出相應(yīng)的處理
frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
// 抽象方法,模板模式,被系統(tǒng)主動(dòng)調(diào)用 public abstract void onAccessibilityEvent(AccessibilityEvent event); // 該service是被system_server主動(dòng)綁定的,獲取到IAccessibilityServiceClientWrapper的proxy來(lái)實(shí)現(xiàn)系統(tǒng)的主動(dòng)調(diào)用 @Override public final IBinder onBind(Intent intent) { return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { ... @Override public void onAccessibilityEvent(AccessibilityEvent event) { AccessibilityService.this.onAccessibilityEvent(event); } ... } } // 收到binder調(diào)用后,使用handler異步進(jìn)行事件的處理 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); // 通過(guò)回調(diào)調(diào)用以觸發(fā)事件 mCallback.onAccessibilityEvent(event); ... } } return; } }
getRootInActiveWindow 父節(jié)點(diǎn)獲取流程
在調(diào)用findAccessibilityNodeInfosByText之前,需要通過(guò)getRootInActiveWindow方法獲取到父節(jié)點(diǎn),才能通過(guò)調(diào)用父AccessibilityNodeInfo的方法進(jìn)行其子節(jié)點(diǎn)信息查詢(xún)
AccessibilityService.getRootInActiveWindow
frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
public AccessibilityNodeInfo getRootInActiveWindow() { // 查找父節(jié)點(diǎn)的操作沒(méi)有在自己的類(lèi)中實(shí)現(xiàn),而是交由了同一進(jìn)程的Client管理類(lèi)進(jìn)行處理 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); }
frameworks/base/core/java/android/view/accessibility/AccessibilityInteractionClient.java
public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) { return findAccessibilityNodeInfoByAccessibilityId(connectionId, AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); } public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache, int prefetchFlags) { ... // 嘗試binder call到system_server,請(qǐng)求中轉(zhuǎn)到其它APP進(jìn)程中查詢(xún)父節(jié)點(diǎn)信息,注意的是這里AccessibilityInteractionClient本身是個(gè)binder服務(wù)端,把this傳到system_server后,其它進(jìn)程可以通過(guò)這個(gè)引用拿到binder proxy,以實(shí)現(xiàn)通信 final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId( accessibilityWindowId, accessibilityNodeId, interactionId, this, prefetchFlags, Thread.currentThread().getId()); Binder.restoreCallingIdentity(identityToken); // If the scale is zero the call has failed. if (success) { // 調(diào)用成功后,這里會(huì)嘗試同步獲取結(jié)果 List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); if (infos != null && !infos.isEmpty()) { return infos.get(0); } } ... }
Service.findAccessibilityNodeInfoByAccessibilityId
注意一下,這里的Service不是Android中的四大組件的Service,取名叫AccessiblitManagerServiceInternal其實(shí)更合適
frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@Override public boolean findAccessibilityNodeInfoByAccessibilityId( int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid) throws RemoteException { ... // 獲取到其他APP的節(jié)點(diǎn)獲取服務(wù) IAccessibilityInteractionConnection connection = null; ... resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); ... if (!permissionGranted) { return false; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { return false; } } ... // 這里的callback為之前應(yīng)用的服務(wù)proxy句柄,將它傳入是為了之后的信息通信不再需要經(jīng)過(guò)system_server中轉(zhuǎn),而是直接可以APP對(duì)APP的進(jìn)行通信 connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, partialInteractiveRegion, interactionId, callback, mFetchFlags | flags, interrogatingPid, interrogatingTid, spec); ... }
AccessibilityInteractionConnection.findAccessibilityNodeInfoByAccessibilityId
這里調(diào)用到了APP端,其實(shí)同onAccessibilityEvent調(diào)用流程一樣,是APP->SYSTEM->APP的調(diào)用順序
frameworks/base/core/java/android/view/ViewRootImpl.java
@Override public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { // 這里也只是委托給控制類(lèi)進(jìn)行細(xì)節(jié)操作的處理 viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, interactiveRegion, interactionId, callback, flags, interrogatingPid, interrogatingTid, spec); } else { ... } }
frameworks/base/core/java/android/view/AccessibilityInteractionController.java
private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { ... // 初始化將會(huì)返回的節(jié)點(diǎn) List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; infos.clear(); try { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { root = mViewRootImpl.mView; } else { root = findViewByAccessibilityId(accessibilityViewId); } ... } finally { try { ... adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); // 通過(guò)callback binder proxy句柄,將節(jié)點(diǎn)信息binder回應(yīng)用 callback.setFindAccessibilityNodeInfosResult(infos, interactionId); infos.clear(); } catch (RemoteException re) { /* ignore - the other side will time out */ } ... } }
AccessibilityInteractionClient.setFindAccessibilityNodeInfosResult
frameworks/base/core/java/android/view/accessibility/AccessibilityInteractionClient.java
public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos, int interactionId) { synchronized (mInstanceLock) { if (interactionId > mInteractionId) { if (infos != null) { ... // 設(shè)置應(yīng)用的返回節(jié)點(diǎn)信息 if (!isIpcCall) { mFindAccessibilityNodeInfosResult = new ArrayList<>(infos); } else { mFindAccessibilityNodeInfosResult = infos; } } else { mFindAccessibilityNodeInfosResult = Collections.emptyList(); } mInteractionId = interactionId; } // 釋放鎖,停止等待,節(jié)點(diǎn)信息已經(jīng)取回 mInstanceLock.notifyAll(); } }
findAccessibilityNodeInfosByText與performAction 對(duì)目標(biāo)節(jié)點(diǎn)進(jìn)行操作
AccessibilityNodeInfo.findAccessibilityNodeInfosByText
找到父節(jié)點(diǎn)信息后,就可以通過(guò)父節(jié)點(diǎn)獲取對(duì)應(yīng)的子節(jié)點(diǎn)信息了
frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { ... // 一樣的流程,通過(guò)AccessibilityInteractionClient去獲取信息 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, text); } ```
以下的代碼流程同getRootInActiveWindow大概一致,就不詳細(xì)分析了
#### AccessibilityNodeInfo.performAction
獲取到對(duì)應(yīng)子節(jié)點(diǎn)后,通過(guò)performAction可以執(zhí)行對(duì)應(yīng)的操作了,如常用的點(diǎn)擊
最終回調(diào)用到AccessibilityInteractionController,獲取到AccessibilityProvier后就可以執(zhí)行performAction的最終操作了
frameworks/base/core/java/android/view/AccessibilityInteractionController.java
```java private void performAccessibilityActionUiThread(Message message) { View target = null; if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { target = findViewByAccessibilityId(accessibilityViewId); } else { target = mViewRootImpl.mView; } if (target != null && isShown(target)) { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { // 在客戶(hù)端執(zhí)行performAction操作 succeeded = provider.performAction(virtualDescendantId, action, arguments); } else { succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, action, arguments); } } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { succeeded = target.performAccessibilityAction(action, arguments); } } }
frameworks/base/core/java/android/view/View.java
public boolean performAccessibilityActionInternal(int action, Bundle arguments) { ... switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { // 最終調(diào)用到我們熟悉的View.performClick方法 performClick(); return true; } } break; ... }
分析到這里可以看到,Accessibility服務(wù)框架類(lèi)似于hook在Android View組件樹(shù)中的一套實(shí)現(xiàn),它并不是獨(dú)立的一套機(jī)制,而是”寄生”在View的顯示、事件分發(fā)的流程中。
總結(jié)
功能實(shí)現(xiàn)依賴(lài)于ViewRootImpl, ViewGroup, View視圖層級(jí)管理的基本架構(gòu)。在視圖變化時(shí)發(fā)出事件、當(dāng)收到視圖操作請(qǐng)求時(shí)也能夠作出響應(yīng)。
system_server在實(shí)現(xiàn)該功能的過(guò)程中扮演著中間人的角色。當(dāng)被監(jiān)聽(tīng)APP視圖變化時(shí),APP首先會(huì)發(fā)出事件到system_server,隨后再中轉(zhuǎn)到監(jiān)聽(tīng)者APP端。當(dāng)監(jiān)聽(tīng)者APP想要執(zhí)行視圖操作時(shí),也是首先在system_server中找到對(duì)應(yīng)的客戶(hù)端binder proxy,再調(diào)用相應(yīng)接口調(diào)用到被監(jiān)聽(tīng)APP中。完成相關(guān)操作后,通過(guò)已經(jīng)獲取到的監(jiān)聽(tīng)APP binder proxy句柄,直接binder call到對(duì)應(yīng)的監(jiān)聽(tīng)客戶(hù)端。
無(wú)障礙權(quán)限十分重要,切記不可濫用,APP自身也需要有足夠的安全意識(shí),防止惡意應(yīng)用通過(guò)該服務(wù)獲取用戶(hù)隱私信息
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android實(shí)現(xiàn)簡(jiǎn)單時(shí)鐘View的方法
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單的時(shí)鐘View,關(guān)鍵點(diǎn)在Canvas的平移與旋轉(zhuǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Android中Textview超鏈接實(shí)現(xiàn)方式
TextView中的超鏈接可以通過(guò)幾種方式實(shí)現(xiàn):1.Html.fromHtml,2.Spannable,3.Linkify.addLinks。下面分別進(jìn)行測(cè)試,包括修改字體樣式,下劃線樣式,點(diǎn)擊事件等,需要的朋友可以參考下2016-02-02Android RecyclerView添加搜索過(guò)濾器的示例代碼
本篇文章主要介紹了Android RecyclerView添加搜索過(guò)濾器的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01解決Android Studio突然不顯示logcat日志的問(wèn)題
這篇文章主要介紹了解決Android Studio突然不顯示logcat日志的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04Android項(xiàng)目實(shí)戰(zhàn)之ListView懸浮頭部展現(xiàn)效果實(shí)現(xiàn)
這篇文章主要給大家介紹了Android項(xiàng)目實(shí)戰(zhàn)之ListView懸浮頭部展現(xiàn)效果實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01詳解Android業(yè)務(wù)組件化之URL Schema使用
這篇文章主要為大家詳細(xì)介紹了Android業(yè)務(wù)組件化之URL Schema使用,感興趣的小伙伴們可以參考一下2016-09-09