真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

通過源碼角度看看AccessibilityService

簡介

創(chuàng)新互聯(lián)建站是專業(yè)的膠州網(wǎng)站建設(shè)公司,膠州接單;提供成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行膠州網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!

AccessibilityService的設(shè)計(jì)初衷是為了輔助有身體缺陷的群體使用Android應(yīng)用,它的設(shè)計(jì)貫穿著Android的控件樹View, ViewGroup, ViewRootImpl體系。借助于system_server進(jìn)程的中轉(zhuǎn),能夠注冊Accessibility事件的客戶端可以具備通過system_server提供的Accessibility服務(wù)來實(shí)現(xiàn)監(jiān)聽、操作其它應(yīng)用視圖的功能。這個(gè)功能十分強(qiáng)大,可以模擬用戶的行為去操作其它APP,常常被用在自動(dòng)化測試、微信搶紅包、自動(dòng)回復(fù)等功能實(shí)現(xiàn)中。

寫這個(gè)的初衷有二:

  • 之前已經(jīng)完成了Android View控件樹的繪制、事件分發(fā)的源碼分析,知識(shí)儲(chǔ)備足夠
  • 最近接觸到了一些自動(dòng)化方面的項(xiàng)目,并且對使用無障礙服務(wù)實(shí)現(xiàn)的自動(dòng)微信搶紅包功能原理十分好奇

整體圖

類圖

通過源碼角度看看AccessibilityService

  • AccessibilityService: APP端直接繼承的類,本質(zhì)上是Service,通過onBind獲取匿名Binder對象實(shí)現(xiàn)通信
  • IAccessibilityServiceClientWrapper: 用于和system_server通信的匿名Binder服務(wù)
  • AccessibilityInteractionClient: 本質(zhì)上是個(gè)binder服務(wù),用于獲取Node信息
  • AccessibilityManagerService: 運(yùn)行在system_server的實(shí)名binder服務(wù),是整體的管理類
  • Service: AccessibilityManagerService的內(nèi)部類,用于響應(yīng)AccessibilityInteractionClient的binder通信請求
  • AccessibilityInteractionConnection: 運(yùn)行在被監(jiān)測的APP端,提供查找、點(diǎn)擊視圖等服務(wù)
  • AccessibilityManager: 運(yùn)行在各個(gè)APP端,用于發(fā)送視圖變化事件
  • AccessibilityInteractionController: 具體視圖查找、點(diǎn)擊服務(wù)的中間控制器
  • AccessibilityNodeProvider: 由客戶端實(shí)現(xiàn)的視圖節(jié)點(diǎn)內(nèi)容提供者,最終操作的實(shí)現(xiàn)者

整體設(shè)計(jì)圖

通過源碼角度看看AccessibilityService

實(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)行對應(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;
 }
 // 獲取到滿足字符要求的節(jié)點(diǎn)
 List 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)行查詢,直到尋找到可點(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)用
  • 通過AccessibilityService的getRootInActiveWindow與findAccessibilityNodeInfosByText方法,可以獲取到節(jié)點(diǎn)信息
  • 通過AccessibilityNodeInfo的performAction方法,最終會(huì)在被監(jiān)聽APP中執(zhí)行對應(yīng)操作

本篇文章將會(huì)圍繞著這三點(diǎn)主要功能進(jìn)行源碼分析

源碼分析

常見 AccessibilityEvent 事件種類

序號(hào)種類名稱觸發(fā)時(shí)機(jī)
1TYPE_VIEW_CLICKED可點(diǎn)擊的組件被點(diǎn)擊
2TYPE_VIEW_LONG_CLICKED可點(diǎn)擊的組件被長按
3TYPE_VIEW_SELECTED組件被選中
4TYPE_VIEW_FOCUSED組件獲取到了焦點(diǎn)
5TYPE_VIEW_TEXT_CHANGED組件中的文本發(fā)生變化
6TYPE_VIEW_SCROLLED組件被滑動(dòng)
7TYPE_WINDOW_STATE_CHANGEDdialog等被打開
8TYPE_NOTIFICATION_STATE_CHANGED通知彈出
9TYPE_WINDOW_CONTENT_CHANGED組件樹發(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)過層層向上傳遞,最終交由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)聽組件變化的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ā)到客戶端進(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是通過bind客戶端的AccessibilityService,在onServiceConnected連接成功后,獲取到binder proxy轉(zhuǎn)化來的,以這種方式實(shí)現(xiàn)了system_server與客戶端的通信
 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來實(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);
  // 通過回調(diào)調(diào)用以觸發(fā)事件
  mCallback.onAccessibilityEvent(event);
  ...
  }
 } return;
 }
}

getRootInActiveWindow 父節(jié)點(diǎn)獲取流程

在調(diào)用findAccessibilityNodeInfosByText之前,需要通過getRootInActiveWindow方法獲取到父節(jié)點(diǎn),才能通過調(diào)用父AccessibilityNodeInfo的方法進(jìn)行其子節(jié)點(diǎn)信息查詢

AccessibilityService.getRootInActiveWindow

frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java

public AccessibilityNodeInfo getRootInActiveWindow() {
 // 查找父節(jié)點(diǎn)的操作沒有在自己的類中實(shí)現(xiàn),而是交由了同一進(jìn)程的Client管理類進(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,請求中轉(zhuǎn)到其它APP進(jìn)程中查詢父節(jié)點(diǎn)信息,注意的是這里AccessibilityInteractionClient本身是個(gè)binder服務(wù)端,把this傳到system_server后,其它進(jìn)程可以通過這個(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 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)過system_server中轉(zhuǎn),而是直接可以APP對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) {
 // 這里也只是委托給控制類進(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 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);
  // 通過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 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 對目標(biāo)節(jié)點(diǎn)進(jìn)行操作

AccessibilityNodeInfo.findAccessibilityNodeInfosByText

找到父節(jié)點(diǎn)信息后,就可以通過父節(jié)點(diǎn)獲取對應(yīng)的子節(jié)點(diǎn)信息了

frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java

public List findAccessibilityNodeInfosByText(String text) {
 ...
 // 一樣的流程,通過AccessibilityInteractionClient去獲取信息
 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
 return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
  text);
}
``` 

以下的代碼流程同getRootInActiveWindow大概一致,就不詳細(xì)分析了

#### AccessibilityNodeInfo.performAction

獲取到對應(yīng)子節(jié)點(diǎn)后,通過performAction可以執(zhí)行對應(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) {
  // 在客戶端執(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ù)框架類似于hook在Android View組件樹中的一套實(shí)現(xiàn),它并不是獨(dú)立的一套機(jī)制,而是”寄生”在View的顯示、事件分發(fā)的流程中。

總結(jié)

功能實(shí)現(xiàn)依賴于ViewRootImpl, ViewGroup, View視圖層級(jí)管理的基本架構(gòu)。在視圖變化時(shí)發(fā)出事件、當(dāng)收到視圖操作請求時(shí)也能夠作出響應(yīng)。

system_server在實(shí)現(xiàn)該功能的過程中扮演著中間人的角色。當(dāng)被監(jiān)聽APP視圖變化時(shí),APP首先會(huì)發(fā)出事件到system_server,隨后再中轉(zhuǎn)到監(jiān)聽者APP端。當(dāng)監(jiān)聽者APP想要執(zhí)行視圖操作時(shí),也是首先在system_server中找到對應(yīng)的客戶端binder proxy,再調(diào)用相應(yīng)接口調(diào)用到被監(jiān)聽APP中。完成相關(guān)操作后,通過已經(jīng)獲取到的監(jiān)聽APP binder proxy句柄,直接binder call到對應(yīng)的監(jiān)聽客戶端。

無障礙權(quán)限十分重要,切記不可濫用,APP自身也需要有足夠的安全意識(shí),防止惡意應(yīng)用通過該服務(wù)獲取用戶隱私信息

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對創(chuàng)新互聯(lián)的支持。


文章標(biāo)題:通過源碼角度看看AccessibilityService
文章轉(zhuǎn)載:http://weahome.cn/article/jehiei.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部