一桂敛、AccessibilityService
根據(jù)官方的介紹,是指開(kāi)發(fā)者通過(guò)增加類似contentDescription的屬性溅潜,從而在不修改代碼的情況下术唬,讓殘障人士能夠獲得使用體驗(yàn)的優(yōu)化,大家可以打開(kāi)AccessibilityService來(lái)試一下滚澜,點(diǎn)擊區(qū)域粗仓,可以有語(yǔ)音或者觸摸的提示,幫助殘障人士更好的使用App
現(xiàn)在被廣泛應(yīng)用在自動(dòng)化设捐,比如自動(dòng)搶紅包潦牛,抖音自動(dòng)關(guān)注點(diǎn)贊等
官方文檔:
https://developer.android.com/guide/topics/ui/accessibility/service
二、AccessibilityService 開(kāi)發(fā)流程
1.確定執(zhí)行腳本的APK安裝包
2.通過(guò)UIAutomator 獲取包名及UI控件ID挡育,或者下載一個(gè)開(kāi)發(fā)者助手apk,也可以進(jìn)行控件ID獲取朴爬,代碼君已經(jīng)幫你下載好了即寒,需要自取
http://share.dmjzy.cn/f/17143538-501591536-a374b2(訪問(wèn)密碼:8401)
3.編寫腳本代碼
4.調(diào)試、兼容性處理
三召噩、核心代碼
1.AccessibilityService主要是實(shí)現(xiàn)onAccessibilityEvent
public class AccessibilitySampleService extends AccessibilityService{
/**當(dāng)無(wú)障礙服務(wù)連接之后回調(diào)*/
@Override
public void onServiceConnected() {
super.onServiceConnected()
}
/**當(dāng)觸發(fā)了需要監(jiān)聽(tīng)的無(wú)障礙事件后回調(diào)*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event){
// 獲取包名
String pkgName = event.getPackageName().toString();
int eventType = event.getEventType();
AccessibilityOperator.getInstance().updateEvent(this, event);
//過(guò)濾出目標(biāo)包母赵,如果要檢測(cè)所有包,可以去掉此判斷
if (pkgName.equals(pageName)) {
AccessibilityLog.printLog("eventType: " + eventType + " pkgName: " + pkgName);
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
//執(zhí)行具體的腳本
toOperator();
break;
case AccessibilityEvent.TYPE_VIEW_CLICKED:
break;
case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
break;
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
break;
}
}
}
/**無(wú)障礙服務(wù)斷開(kāi)后回調(diào)*/
@Override
public void onInterrupt(){
// TODO Auto-generated method stub
}
}
方法說(shuō)明
1. onServiceConnected
當(dāng)聲明的無(wú)障礙服務(wù)連接之后, 系統(tǒng)會(huì)回調(diào)此方法. 在這個(gè)方法里, 可以做一些初始化工作. 比如保存服務(wù)的實(shí)例 標(biāo)識(shí)服務(wù)連接的狀態(tài)等.
也可以通過(guò)
android.accessibilityservice.AccessibilityService#getServiceInfo
動(dòng)態(tài)更改xml配置文件中聲明的無(wú)障礙配置信息.
2. onAccessibilityEvent
當(dāng)監(jiān)聽(tīng)的事件觸發(fā)時(shí), 系統(tǒng)會(huì)回調(diào)此方法, 比如view被點(diǎn)擊了 window內(nèi)容改變了等.
可以用
android.view.accessibility.AccessibilityRecord#getSource
獲取對(duì)象AccessibilityNodeInfo, 這個(gè)對(duì)象就是無(wú)障礙操作的核心對(duì)象, 通尘叩危可以理解為android開(kāi)發(fā)中的view控件.
可以通過(guò)AccessibilityNodeInfo對(duì)象, 進(jìn)行控件的點(diǎn)擊操作 輸入文本操作 滾動(dòng)操作 獲取文本操作等
3. onInterrupt
當(dāng)中途關(guān)閉了無(wú)障礙服務(wù)時(shí)回調(diào), 通常這個(gè)時(shí)候無(wú)障礙服務(wù)不可用, 調(diào)用api都會(huì)失敗.
4. AccessibilityService 其他方法說(shuō)明
方法名 | 方法說(shuō)明 |
---|---|
disableSelf() | 禁用當(dāng)前服務(wù),也就是在服務(wù)可以通過(guò)該方法停止運(yùn)行 |
findFoucs(int falg) | 查找擁有特定焦點(diǎn)類型的控件 |
getRootInActiveWindow() | 如果配置能夠獲取窗口內(nèi)容,則會(huì)返回當(dāng)前活動(dòng)窗口的根結(jié)點(diǎn) |
performGlobalAction(int action) | 執(zhí)行全局操作,比如返回,回到主頁(yè),打開(kāi)最近等操作凹嘲,此方法可以模擬用戶點(diǎn)擊返回鍵和home鍵,操作見(jiàn)下面的官方文檔 |
setServiceInfo(AccessibilityServiceInfo info) | 設(shè)置當(dāng)前服務(wù)的配置信息 |
getSystemService(String name) | 獲取系統(tǒng)服務(wù) |
onKeyEvent(KeyEvent event) | 如果允許服務(wù)監(jiān)聽(tīng)按鍵操作,該方法是按鍵事件的回調(diào),需要注意,這個(gè)過(guò)程發(fā)生了系統(tǒng)處理按鍵事件之前 |
更多AccessibilityService參數(shù)說(shuō)明見(jiàn)官方文檔:
AccessibilityEvent
字段名 | 字段說(shuō)明 |
---|---|
TYPE_NOTIFICATION_STATE_CHANGED | 通知欄狀態(tài)變化 |
TYPE_VIEW_CLICKED | 視圖被點(diǎn)擊 |
TYPE_WINDOW_CONTENT_CHANGED | 窗口內(nèi)容變化 |
TYPE_WINDOW_STATE_CHANGED | 窗口狀態(tài)變化,即切換activity |
2. AndroidManifest.xml注冊(cè)服務(wù)
<!-- 注冊(cè)輔助功能服務(wù)-->
<service
android:name=".AccessibilitySampleService"
android:exported="true"
android:label="碼君助手"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":BackgroundService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<!-- 通過(guò)xml文件完成輔助功能相關(guān)配置构韵,也可以在onServiceConnected中動(dòng)態(tài)配置-->
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config" />
</service>
3. 在資源文件夾新增xml文件夾周蹭,新建accessibility_config文件,代碼如下
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:canPerformGestures="true"
android:description="@string/accessibility_desc"
android:notificationTimeout="10" />
<!-- canPerformGestures //申請(qǐng)手勢(shì)權(quán)限-->
<!--accessibility_desc:碼君助手疲恢,讓你的手機(jī)更智能一點(diǎn) -->
accessibility_config說(shuō)明
官方文檔說(shuō)明:
https://developer.android.google.cn/reference/android/R.styleable#AccessibilityService
這里列舉一些比較常用的
字段名 | 字段說(shuō)明 |
---|---|
accessibilityEventTypes | 表示該服務(wù)對(duì)界面中的哪些變化感興趣,即哪些事件通知,比如窗口打開(kāi),滑動(dòng),焦點(diǎn)變化,長(zhǎng)按等.具體的值可以在AccessibilityEvent類中查到,如typeAllMask表示接受所有的事件通知 |
accessibilityFeedbackType | 表示反饋方式,比如是語(yǔ)音播放,還是震動(dòng)凶朗。feedbackGeneric代表所有 |
canRetrieveWindowContent | 表示該服務(wù)能否訪問(wèn)活動(dòng)窗口中的內(nèi)容.也就是如果你希望在服務(wù)中獲取窗體內(nèi)容的化,則需要設(shè)置其值為true |
notificationTimeout | 接受事件的時(shí)間間隔,通常將其設(shè)置為100即可 |
packageNames | 表示對(duì)該服務(wù)是用來(lái)監(jiān)聽(tīng)哪個(gè)包的產(chǎn)生的事件。如果不寫代表監(jiān)聽(tīng)所有的應(yīng)用显拳。中間可以用";"來(lái)分割棚愤。 |
canPerformGestures | 表示可以執(zhí)行手勢(shì)屬性 |
canTakeScreenshot | 是否能夠截屏 |
4、編寫執(zhí)行腳本
try {
Thread.sleep(2000);
AccessibilityOperator.getInstance().clickById("com.xxxx.packagename:id/btn_later");// 關(guān)閉彈框
Thread.sleep(1000);
AccessibilityOperator.getInstance().clickById("com.xxxx.packagename:id/tab_work");//切換到工作tab
AccessibilityLog.printLog("切換到工作tab: ");
} catch (InterruptedException e) {
e.printStackTrace();
}
AccessibilityNodeInfo
方法名 | 方法說(shuō)明 |
---|---|
findAccessibilityNodeInfosByText() | 通過(guò)字符串查找節(jié)點(diǎn)元素 |
findAccessibilityNodeInfosByViewId() | 通過(guò)視圖id查找節(jié)點(diǎn)元素 |
performAction() | 在節(jié)點(diǎn)上執(zhí)行一個(gè)動(dòng)作,比如點(diǎn)擊杂数、向上滑動(dòng)等宛畦,更多操作見(jiàn)下面的官方文檔 |
getParent() | 獲取父節(jié)點(diǎn) |
getChild() | 獲取子節(jié)點(diǎn) |
isEnabled() | 判斷節(jié)點(diǎn)是否激活 |
isClickable() | 判斷節(jié)點(diǎn)是否可以點(diǎn)擊 |
isScrollable() | 判斷節(jié)點(diǎn)是否可以滾動(dòng) |
isSelected() | 判斷節(jié)點(diǎn)是否選中 |
isPassword() | 判斷節(jié)點(diǎn)是否是密碼輸入框 |
isFocusable() | 判斷節(jié)點(diǎn)是否可以獲取焦點(diǎn) |
getText() | 獲取節(jié)點(diǎn)的文本信息 |
getContentDescription() | 節(jié)點(diǎn)的內(nèi)容描述 |
getViewIdResourceName() | 獲取節(jié)點(diǎn)控件的id ,獲取到的值大概是這樣的:com.ss.android.ugc.aweme:id/afy |
getBoundsInScreen() | 獲取節(jié)點(diǎn)在屏幕中的位置 |
getClassName() | 獲取節(jié)點(diǎn)的類型/類名揍移,值:android.widget.LinearLayout |
getChild() | 獲取子節(jié)點(diǎn)的AccessibilityNodeInfo信息 |
更多請(qǐng)查看官方文檔:
https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo