文章目錄
- 第一部分:無障礙服務基礎
- 1.1 無障礙服務概述
- 核心功能:
- 1.2 基本原理與架構
- 1.3 開發環境配置
- 所需工具:
- 關鍵依賴:
- 第二部分:創建基礎無障礙服務
- 2.1 服務聲明配置
- 2.2 服務配置文件
- 關鍵屬性說明:
- 2.3 實現服務類
- 第三部分:高級功能實現
- 3.1 節點查找與操作
- 常用查找方法:
- 節點操作示例:
- 3.2 手勢模擬
- 3.3 全局事件監聽
- 第四部分:實戰案例開發
- 4.1 自動填寫表單
- 4.2 消息自動回復
- 4.3 游戲自動化輔助
- 第五部分:調試與優化
- 5.1 調試技巧
- ADB調試命令:
- 日志記錄最佳實踐:
- 5.2 性能優化
- 優化建議:
- 優化示例:
- 第六部分:發布與安全
- 6.1 權限與隱私
- 必要權限聲明:
- 隱私注意事項:
- 6.2 發布流程
- 第七部分:高級主題
- 7.1 與其他技術的結合
- 與Tasker集成:
- 使用機器學習:
- 7.2 跨版本兼容性處理
- 版本差異處理表:
- 兼容性代碼示例:

第一部分:無障礙服務基礎
1.1 無障礙服務概述
安卓無障礙服務(Accessibility Service)是一種特殊類型的服務,旨在幫助殘障用戶或需要輔助功能的用戶更好地使用設備。但它的功能遠不止于此,開發者可以利用它實現自動化操作、界面監控和交互等功能。
核心功能:
- 界面內容訪問:獲取屏幕上的UI元素信息
- 自動化操作:模擬點擊、滑動等用戶操作
- 事件監控:監聽窗口變化、通知、焦點改變等系統事件
- 增強交互:為特定應用提供定制化輔助功能
1.2 基本原理與架構
1.3 開發環境配置
所需工具:
- Android Studio最新版
- 安卓設備或模擬器(API 16+)
- ADB調試工具
關鍵依賴:
dependencies {implementation 'androidx.core:core-ktx:1.7.0'implementation 'androidx.appcompat:appcompat:1.4.1'
}
第二部分:創建基礎無障礙服務
2.1 服務聲明配置
在AndroidManifest.xml
中添加服務聲明:
<serviceandroid:name=".MyAccessibilityService"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"android:exported="true"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/service_config" />
</service>
2.2 服務配置文件
創建res/xml/service_config.xml
:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:description="@string/accessibility_service_description"android:accessibilityEventTypes="typeAllMask"android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows"android:canRetrieveWindowContent="true"android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"android:canRequestFilterKeyEvents="true"android:canPerformGestures="true"android:notificationTimeout="100"android:packageNames="com.example.targetapp" />
關鍵屬性說明:
accessibilityEventTypes
:監聽的事件類型packageNames
:指定監控的應用包名(可選)canPerformGestures
:允許執行手勢操作(API 24+)
2.3 實現服務類
創建基礎服務類MyAccessibilityService.kt
:
class MyAccessibilityService : AccessibilityService() {override fun onServiceConnected() {Log.d("A11yService", "無障礙服務已連接")// 可以在此處進行服務配置更新val info = AccessibilityServiceInfo().apply {eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_WINDOW_STATE_CHANGEDfeedbackType = AccessibilityServiceInfo.FEEDBACK_GENERICnotificationTimeout = 100flags = AccessibilityServiceInfo.DEFAULT orAccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS}this.serviceInfo = info}override fun onAccessibilityEvent(event: AccessibilityEvent?) {event ?: returnwhen (event.eventType) {AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> {handleWindowChange(event)}AccessibilityEvent.TYPE_VIEW_CLICKED -> {handleViewClick(event)}}}override fun onInterrupt() {Log.w("A11yService", "無障礙服務被中斷")}private fun handleWindowChange(event: AccessibilityEvent) {val rootNode = rootInActiveWindow ?: returnLog.d("A11yService", "窗口變化: ${event.packageName}")// 遍歷視圖樹traverseNode(rootNode)}private fun traverseNode(node: AccessibilityNodeInfo, depth: Int = 0) {if (node.childCount == 0) {Log.d("A11yTree", "${" ".repeat(depth)}${node.viewIdResourceName}")return}for (i in 0 until node.childCount) {node.getChild(i)?.let { child ->traverseNode(child, depth + 1)child.recycle()}}}
}
第三部分:高級功能實現
3.1 節點查找與操作
常用查找方法:
fun findNodes(root: AccessibilityNodeInfo) {// 通過文本查找val byText = root.findAccessibilityNodeInfosByText("搜索")// 通過View ID查找(全限定ID)val byId = root.findAccessibilityNodeInfosByViewId("com.example.app:id/btnSubmit")// 通過類名查找val editTexts = mutableListOf<AccessibilityNodeInfo>()val queue: Queue<AccessibilityNodeInfo> = LinkedList()queue.add(root)while (queue.isNotEmpty()) {val current = queue.poll()if (current.className == "android.widget.EditText") {editTexts.add(current)}for (i in 0 until current.childCount) {current.getChild(i)?.let { queue.add(it) }}}
}
節點操作示例:
fun performActions(node: AccessibilityNodeInfo) {// 點擊操作if (node.isClickable) {node.performAction(AccessibilityNodeInfo.ACTION_CLICK)}// 文本輸入val arguments = Bundle().apply {putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "Hello")}node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)// 焦點控制node.performAction(AccessibilityNodeInfo.ACTION_FOCUS)// 滾動操作node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
}
3.2 手勢模擬
Android支持通過無障礙服務模擬復雜手勢:
fun performGesture(service: AccessibilityService) {val path = Path().apply {moveTo(100f, 100f) // 起點lineTo(500f, 100f) // 移動到右側lineTo(500f, 500f) // 向下移動lineTo(100f, 500f) // 向左移動close() // 閉合路徑}val gestureBuilder = GestureDescription.Builder().addStroke(GestureDescription.StrokeDescription(path, 0L, // 開始時間1000L, // 持續時間(毫秒)false // 是否持續))service.dispatchGesture(gestureBuilder.build(), object : AccessibilityService.GestureResultCallback() {override fun onCompleted(gestureDescription: GestureDescription?) {Log.d("Gesture", "手勢完成")}override onCancelled(gestureDescription: GestureDescription?) {Log.w("Gesture", "手勢取消")}}, null)
}
3.3 全局事件監聽
監聽系統級事件:
override fun onAccessibilityEvent(event: AccessibilityEvent) {when (event.eventType) {AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED -> {val notificationText = event.text.joinToString()Log.d("Notification", "新通知: $notificationText")}AccessibilityEvent.TYPE_ANNOUNCEMENT -> {Log.d("Announcement", "系統公告: ${event.text}")}AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START -> {Log.d("Touch", "觸摸探索開始")}}
}
第四部分:實戰案例開發
4.1 自動填寫表單
class FormFillerService : AccessibilityService() {private val formData = mapOf("username" to "testuser","password" to "secure123","email" to "test@example.com")override fun onAccessibilityEvent(event: AccessibilityEvent) {if (event.eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) returnval rootNode = rootInActiveWindow ?: returnformData.forEach { (fieldName, value) ->val nodes = rootNode.findAccessibilityNodeInfosByViewId("com.example.app:id/$fieldName")nodes.firstOrNull()?.let { field ->if (field.className == "android.widget.EditText") {val args = Bundle().apply {putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, value)}field.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)}}}// 自動提交表單rootNode.findAccessibilityNodeInfosByViewId("com.example.app:id/submit").firstOrNull()?.performAction(AccessibilityNodeInfo.ACTION_CLICK)}
}
4.2 消息自動回復
class AutoReplyService : AccessibilityService() {private val replyMessages = listOf("我正在開會,稍后回復您","好的,收到","謝謝通知")override fun onAccessibilityEvent(event: AccessibilityEvent) {if (event.packageName != "com.whatsapp") returnwhen (event.eventType) {AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED -> {// 處理通知事件val messages = event.text.filter { it.contains("發來消息") }if (messages.isNotEmpty()) {replyToLatestMessage()}}AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED -> {// 處理界面文本變化if (isChatOpen()) {autoReplyInChat()}}}}private fun replyToLatestMessage() {// 實現打開聊天界面并回復的邏輯}private fun isChatOpen(): Boolean {// 檢測當前是否在聊天界面}private fun autoReplyInChat() {val root = rootInActiveWindow ?: returnval messageNodes = root.findAccessibilityNodeInfosByViewId("com.whatsapp:id/message_text")// 獲取最后一條消息val lastMessage = messageNodes.lastOrNull()?.text ?: return// 隨機選擇回復內容val randomReply = replyMessages.random()// 找到輸入框并發送root.findAccessibilityNodeInfosByViewId("com.whatsapp:id/entry").firstOrNull()?.let { input ->val args = Bundle().apply {putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, randomReply)}input.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)// 發送消息root.findAccessibilityNodeInfosByViewId("com.whatsapp:id/send").firstOrNull()?.performAction(AccessibilityNodeInfo.ACTION_CLICK)}}
}
4.3 游戲自動化輔助
class GameHelperService : AccessibilityService() {private var isRunning = falseprivate val handler = Handler(Looper.getMainLooper())private val clickRunnable = object : Runnable {override fun run() {performAutoClick()if (isRunning) {handler.postDelayed(this, 1000) // 每秒點擊一次}}}override fun onServiceConnected() {val info = AccessibilityServiceInfo().apply {eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGEDfeedbackType = AccessibilityServiceInfo.FEEDBACK_GENERICflags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS}serviceInfo = info}override fun onAccessibilityEvent(event: AccessibilityEvent) {if (event.packageName != "com.game.package") returnwhen (event.eventType) {AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> {checkGameState()}}}private fun checkGameState() {val root = rootInActiveWindow ?: returnval battleNode = root.findAccessibilityNodeInfosByViewId("com.game.package:id/battle_indicator")if (battleNode.isNotEmpty()) {startAutoClicking()} else {stopAutoClicking()}}private fun startAutoClicking() {if (!isRunning) {isRunning = truehandler.post(clickRunnable)}}private fun stopAutoClicking() {isRunning = falsehandler.removeCallbacks(clickRunnable)}private fun performAutoClick() {val root = rootInActiveWindow ?: returnval attackBtn = root.findAccessibilityNodeInfosByViewId("com.game.package:id/attack_button").firstOrNull()attackBtn?.performAction(AccessibilityNodeInfo.ACTION_CLICK)// 隨機位置點擊,避免被檢測為機器人if (Math.random() < 0.3) {val randomX = (100..500).random()val randomY = (200..800).random()dispatchGesture(createClickGesture(randomX, randomY), null, null)}}private fun createClickGesture(x: Int, y: Int): GestureDescription {val clickPath = Path().apply {moveTo(x.toFloat(), y.toFloat())}return GestureDescription.Builder().addStroke(GestureDescription.StrokeDescription(clickPath, 0, 50)).build()}
}
第五部分:調試與優化
5.1 調試技巧
ADB調試命令:
# 查看已啟用的無障礙服務
adb shell settings get secure enabled_accessibility_services# 啟用服務
adb shell settings put secure enabled_accessibility_services com.example.pkg/.MyAccessibilityService# 查看無障礙事件日志
adb shell logcat -s AccessibilityEvent
日志記錄最佳實踐:
fun logNodeInfo(node: AccessibilityNodeInfo) {val sb = StringBuilder().apply {append("View ID: ${node.viewIdResourceName}\n")append("Text: ${node.text}\n")append("Class: ${node.className}\n")append("Bounds: ${node.boundsInScreen}\n")append("Actions: ${node.actionList.joinToString()}\n")append("ChildCount: ${node.childCount}\n")}Log.d("NodeInfo", sb.toString())
}
5.2 性能優化
優化建議:
- 減少遍歷深度:只查找必要的節點層級
- 及時回收節點:調用
recycle()
釋放資源 - 事件過濾:只監聽必要的事件類型
- 延遲處理:對頻繁事件使用防抖
- 后臺處理:將耗時操作移到工作線程
優化示例:
class OptimizedService : AccessibilityService() {private val eventQueue = LinkedBlockingQueue<AccessibilityEvent>()private val workerThread = HandlerThread("EventProcessor").apply { start() }private val workerHandler = Handler(workerThread.looper)private val eventProcessor = object : Runnable {override fun run() {while (true) {val event = eventQueue.take()processEvent(event)}}}override fun onCreate() {super.onCreate()workerHandler.post(eventProcessor)}override fun onAccessibilityEvent(event: AccessibilityEvent) {// 快速將事件加入隊列,避免阻塞主線程eventQueue.put(event)}private fun processEvent(event: AccessibilityEvent) {// 實際處理邏輯}override fun onDestroy() {workerThread.quitSafely()super.onDestroy()}
}
第六部分:發布與安全
6.1 權限與隱私
必要權限聲明:
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
隱私注意事項:
- 明確告知用戶:在隱私政策中說明數據收集范圍
- 最小權限原則:只請求必要的權限
- 敏感數據處理:避免收集密碼等敏感信息
- 數據加密:對存儲的日志和數據進行加密
6.2 發布流程
-
測試階段:
- 在不同安卓版本上測試
- 在各種品牌設備上測試(特別是國產ROM)
- 測試電池消耗情況
-
應用商店要求:
- 明確說明是無障礙輔助工具
- 提供詳細的使用說明視頻
- 如果是自動化工具,需遵守各商店政策
-
持續更新:
- 定期適配新安卓版本
- 針對流行應用的特殊適配
- 根據用戶反饋優化功能
第七部分:高級主題
7.1 與其他技術的結合
與Tasker集成:
// 接收Tasker的廣播意圖
private val taskerReceiver = object : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {if (intent.action == "net.dinglisch.android.tasker.ACTION_TRIGGER") {val task = intent.getStringExtra("task")when (task) {"start_automation" -> startAutomation()"stop_automation" -> stopAutomation()}}}
}override fun onCreate() {super.onCreate()registerReceiver(taskerReceiver, IntentFilter("net.dinglisch.android.tasker.ACTION_TRIGGER"))
}
使用機器學習:
// 使用ML Kit識別屏幕內容
fun detectTextFromScreen(bitmap: Bitmap): String {val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)val image = InputImage.fromBitmap(bitmap, 0)return try {val result = recognizer.process(image).await()result.text} catch (e: Exception) {Log.e("ML", "識別失敗", e)""}
}// 截圖并處理
fun captureAndAnalyze() {val projection = MediaProjectionManager.createScreenCaptureIntent()// 需要先獲取用戶授權...val imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 2)imageReader.setOnImageAvailableListener({ reader ->val image = reader.acquireLatestImage()// 轉換為Bitmap并傳遞給識別器val text = detectTextFromScreen(convertImageToBitmap(image))Log.d("ScreenText", "識別結果: $text")image.close()}, handler)
}
7.2 跨版本兼容性處理
版本差異處理表:
功能 | API 16-22 | API 23-28 | API 29+ |
---|---|---|---|
節點信息獲取 | 基本支持 | 增強支持 | 受限 |
手勢模擬 | 不支持 | 部分支持 | 完全支持 |
隱私限制 | 無 | 部分 | 嚴格 |
后臺服務 | 允許 | 限制 | 嚴格限制 |
兼容性代碼示例:
fun performActionCompat(node: AccessibilityNodeInfo, action: Int, args: Bundle? = null): Boolean {return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {node.performAction(action, args)} else {node.performAction(action)}
}fun getNodeTextCompat(node: AccessibilityNodeInfo): String {return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {node.text?.toString() ?: ""} else {node.text ?: ""}
}