如果你不 Care 这是怎么实现的,只想要个解决方案,那么请戳这里下载 AutoInstall 的 apk来安装并开启服务即可。
如果你顺便还想拿源码来自己定制一下,可从这里找到AndroidStudio 工程源码,仅一个 Service 而已。
如果你想知道一下什么是 AccessibilityService,可自行搜索学习或看官方介绍 http://developer.android.com/reference/android/accessibilityservice/AccessibilityService.html
开启方法:
普通手机: 设置 -> 无障碍/辅助功能 -> 服务 -> AutoInstall -> 开启 -> 确定
某些手机:设置 -> 其它高级设置 -> 辅助功能 -> 服务 -> AutoInstall -> 开启 -> 确定
注意:
开启自动安装不仅适用于 adb install,也适用于主动点击 apk 来启动安装。所以有安全风险,建议仅在测试机器上安装
不需要 Activity,仅需要一个继承 AccessibilityService 的服务,在服务里兼听 onAccessibilityEvent,当出现安装界面的时候,自动去点击。在安装完成后,到辅助功能里开启即可。
package {你的包名};
import android.accessibilityservice.AccessibilityService;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.List;
public class AutoInstallService extends AccessibilityService {
private static final String TAG = "AutoInstallService";
private static String PACKAGE_INSTALLER = "com.android.packageinstaller";
public AutoInstallService() {
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
/*
* 回调方法,当事件发生时会从这里进入,在这里判断需要捕获的内容,
* 可通过下面这句log将所有事件详情打印出来,分析决定怎么过滤。
*/
//log(event.toString());
if (event.getSource() == null) {
log("<null> event source");
return;
}
int eventType = event.getEventType();
/*
* 在弹出安装界面时会发生 TYPE_WINDOW_STATE_CHANGED 事件,其属主
* 是系统安装器com.android.packageinstaller
*/
if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& event.getPackageName().equals(PACKAGE_INSTALLER)) {
boolean r = performInstallation(event);
log("Action Perform: " + r);
}
}
@Override
public void onInterrupt() {
log("AutoInstallServiceInterrupted");
}
private void log(String s) {
Log.d(TAG, s);
}
private boolean performInstallation(AccessibilityEvent event) {
List<AccessibilityNodeInfo> nodeInfoList;
/*
* 有的手机会弹2次,有的只弹一次,在替换安装时会出现确定按钮,
* 为了大而全,下面定义了比较多的内容,可按需增减。
*/
String[] labels = new String[]{"确定", "安装", "下一步", "完成"};
for (String label : labels) {
nodeInfoList = event.getSource().findAccessibilityNodeInfosByText(label);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
boolean performed = performClick(nodeInfoList);
if (performed) return true;
}
}
return false;
}
private boolean performClick(List<AccessibilityNodeInfo> nodeInfoList) {
for (AccessibilityNodeInfo node : nodeInfoList) {
/*
* 这里还可以根据node的类名来过滤,大多数是button类,这里也是为了大而全,
* 判断只要是可点击的是可用的就点。
*/
if (node.isClickable() && node.isEnabled()) {
return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
return false;
}
}
AndroidManifest 里面要声明权限,除了上面从代码里面可以过滤,通过 meta-data 的 xml 里也可直接配置过滤
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mrqyoung.autoinstall" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black" >
<service
android:name=".AutoInstallService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" />
</service>
</application>
</manifest>
在 AndroidManifest 里面引用的 meta-data 文件,样例。
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
android:accessibilityEventTypes="typeWindowStateChanged"
android:packageNames="com.android.packageinstaller"
android:description="@string/description"
android:accessibilityFeedbackType="feedbackVisual"
android:notificationTimeout="100"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
xmlns:android="http://schemas.android.com/apk/res/android" />
<!-- 第3行等同于过滤 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -->
<!-- 第4行只监听 com.android.packageinstaller -->
进行了 3 种方式的安装测试:
直接在手机上点击 apk 文件弹出安装,它能自动安装完成。写代码时没有那些特殊机器只好用这种方式来调试。
通过 adb 安装,新安装和覆盖安装,在特殊手机上弹出安装后能自动安装完成。
[无图]
在运行 appium 时,appium 会安装 2 个 apk,新安装和覆盖安装,弹出安装后能自动完成。
利用 AccessibilityService 可以自动识别界面上的内容,并进行操作,也能达到自动化操作的目的。绿色守护的自动停止应用,豌豆荚的自动安装,一些抢红包的工具是用它来实现的。在最开始遇到这个问题的时候,我给了一个简单直接暴力的解决方法,在批处理中:
...
start adb shell "sleep 3 && input tap 1200 300"
adb install -r xxx.apk
...
后来为了兼容性强,做成了一个自动安装的工具,也许能勉强解决在自动化过程中出现安装提示的问题吧,期待大家共同验证和完善。