尝试使用 AccessibilityService 解决应用安装提示

0. 干货区

如果你不Care这是怎么实现的,只想要个解决方案,那么请戳这里下载AutoInstall的apk来安装并开启服务即可。
如果你顺便还想拿源码来自己定制一下,可从这里找到AndroidStudio工程源码,仅一个Service而已。
如果你想知道一下什么是AccessibilityService,可自行搜索学习或看官方介绍http://developer.android.com/reference/android/accessibilityservice/AccessibilityService.html

开启方法:
普通手机: 设置 -> 无障碍/辅助功能 -> 服务 -> AutoInstall -> 开启 -> 确定
某些手机:设置 -> 其它高级设置 -> 辅助功能 -> 服务 -> AutoInstall -> 开启 -> 确定

注意:
开启自动安装不仅适用于adb install,也适用于主动点击apk来启动安装。所以有安全风险,建议仅在测试机器上安装

1. 代码区

不需要Activity,仅需要一个继承AccessibilityService的服务,在服务里兼听onAccessibilityEvent,当出现安装界面的时候,自动去点击。在安装完成后,到辅助功能里开启即可。

AutoInstallService.java
<code><span class="kn">package</span> <span class="o">{</span><span class="err">你的包名</span><span class="o">};</span>

<span class="kn">import</span> <span class="nn">android.accessibilityservice.AccessibilityService</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">android.util.Log</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">android.view.accessibility.AccessibilityEvent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">android.view.accessibility.AccessibilityNodeInfo</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">AutoInstallService</span> <span class="kd">extends</span> <span class="n">AccessibilityService</span> <span class="o">{</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">TAG</span> <span class="o">=</span> <span class="s">"AutoInstallService"</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="n">String</span> <span class="n">PACKAGE_INSTALLER</span> <span class="o">=</span> <span class="s">"com.android.packageinstaller"</span><span class="o">;</span>

    <span class="kd">public</span> <span class="n">AutoInstallService</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="n">onAccessibilityEvent</span><span class="o">(</span><span class="n">AccessibilityEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
        <span class="cm">/*
         * 回调方法,当事件发生时会从这里进入,在这里判断需要捕获的内容,
         * 可通过下面这句log将所有事件详情打印出来,分析决定怎么过滤。
         */</span>
        <span class="c1">//log(event.toString());</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getSource</span><span class="o">()</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">log</span><span class="o">(</span><span class="s">"<null> event source"</span><span class="o">);</span>
            <span class="k">return</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="kt">int</span> <span class="n">eventType</span> <span class="o">=</span> <span class="n">event</span><span class="o">.</span><span class="na">getEventType</span><span class="o">();</span>
        <span class="cm">/*
         * 在弹出安装界面时会发生 TYPE_WINDOW_STATE_CHANGED 事件,其属主
         * 是系统安装器com.android.packageinstaller
         */</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">eventType</span> <span class="o">==</span> <span class="n">AccessibilityEvent</span><span class="o">.</span><span class="na">TYPE_WINDOW_STATE_CHANGED</span>
                <span class="o">&&</span> <span class="n">event</span><span class="o">.</span><span class="na">getPackageName</span><span class="o">().</span><span class="na">equals</span><span class="o">(</span><span class="n">PACKAGE_INSTALLER</span><span class="o">))</span> <span class="o">{</span>
            <span class="kt">boolean</span> <span class="n">r</span> <span class="o">=</span> <span class="n">performInstallation</span><span class="o">(</span><span class="n">event</span><span class="o">);</span>
            <span class="n">log</span><span class="o">(</span><span class="s">"Action Perform: "</span> <span class="o">+</span> <span class="n">r</span><span class="o">);</span>
        <span class="o">}</span>

    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="n">onInterrupt</span><span class="o">()</span> <span class="o">{</span>
        <span class="n">log</span><span class="o">(</span><span class="s">"AutoInstallServiceInterrupted"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kt">void</span> <span class="n">log</span><span class="o">(</span><span class="n">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="n">s</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">performInstallation</span><span class="o">(</span><span class="n">AccessibilityEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">List</span><span class="o"><</span><span class="n">AccessibilityNodeInfo</span><span class="o">></span> <span class="n">nodeInfoList</span><span class="o">;</span>
        <span class="cm">/*
         * 有的手机会弹2次,有的只弹一次,在替换安装时会出现确定按钮,
         * 为了大而全,下面定义了比较多的内容,可按需增减。
         */</span>
        <span class="n">String</span><span class="o">[]</span> <span class="n">labels</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[]{</span><span class="s">"确定"</span><span class="o">,</span> <span class="s">"安装"</span><span class="o">,</span> <span class="s">"下一步"</span><span class="o">,</span> <span class="s">"完成"</span><span class="o">};</span>
        <span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="n">label</span> <span class="o">:</span> <span class="n">labels</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">nodeInfoList</span> <span class="o">=</span> <span class="n">event</span><span class="o">.</span><span class="na">getSource</span><span class="o">().</span><span class="na">findAccessibilityNodeInfosByText</span><span class="o">(</span><span class="n">label</span><span class="o">);</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">nodeInfoList</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&&</span> <span class="o">!</span><span class="n">nodeInfoList</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
                <span class="kt">boolean</span> <span class="n">performed</span> <span class="o">=</span> <span class="n">performClick</span><span class="o">(</span><span class="n">nodeInfoList</span><span class="o">);</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">performed</span><span class="o">)</span> <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">performClick</span><span class="o">(</span><span class="n">List</span><span class="o"><</span><span class="n">AccessibilityNodeInfo</span><span class="o">></span> <span class="n">nodeInfoList</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="n">AccessibilityNodeInfo</span> <span class="n">node</span> <span class="o">:</span> <span class="n">nodeInfoList</span><span class="o">)</span> <span class="o">{</span>
            <span class="cm">/*
             * 这里还可以根据node的类名来过滤,大多数是button类,这里也是为了大而全,
             * 判断只要是可点击的是可用的就点。
             */</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">node</span><span class="o">.</span><span class="na">isClickable</span><span class="o">()</span> <span class="o">&&</span> <span class="n">node</span><span class="o">.</span><span class="na">isEnabled</span><span class="o">())</span> <span class="o">{</span>
                <span class="k">return</span> <span class="n">node</span><span class="o">.</span><span class="na">performAction</span><span class="o">(</span><span class="n">AccessibilityNodeInfo</span><span class="o">.</span><span class="na">ACTION_CLICK</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
    <span class="o">}</span>

<span class="o">}</span></code>

AndroidManifest里面要声明权限,除了上面从代码里面可以过滤,通过meta-data的xml里也可直接配置过滤

AndroidManifest.xml
<code><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><manifest</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
    <span class="na">package=</span><span class="s">"com.mrqyoung.autoinstall"</span> <span class="nt">></span>

    <span class="nt"><application</span>
        <span class="na">android:allowBackup=</span><span class="s">"true"</span>
        <span class="na">android:icon=</span><span class="s">"@mipmap/ic_launcher"</span>
        <span class="na">android:label=</span><span class="s">"@string/app_name"</span>
        <span class="na">android:theme=</span><span class="s">"@android:style/Theme.Black"</span> <span class="nt">></span>
        <span class="nt"><service</span>
            <span class="na">android:name=</span><span class="s">".AutoInstallService"</span>
            <span class="na">android:permission=</span><span class="s">"android.permission.BIND_ACCESSIBILITY_SERVICE"</span><span class="nt">></span>
            <span class="nt"><intent-filter></span>
                <span class="nt"><action</span> <span class="na">android:name=</span><span class="s">"android.accessibilityservice.AccessibilityService"</span> <span class="nt">/></span>
            <span class="nt"></intent-filter></span>
            <span class="nt"><meta-data</span> <span class="na">android:name=</span><span class="s">"android.accessibilityservice"</span> <span class="na">android:resource=</span><span class="s">"@xml/accessibilityservice"</span> <span class="nt">/></span>
        <span class="nt"></service></span>
    <span class="nt"></application></span>

<span class="nt"></manifest></span></code>

在AndroidManifest里面引用的meta-data文件,样例

@xml/accessibilityservice.xml
<code><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><accessibility-service</span>
    <span class="na">android:accessibilityEventTypes=</span><span class="s">"typeWindowStateChanged"</span>    
    <span class="na">android:packageNames=</span><span class="s">"com.android.packageinstaller"</span>              
    <span class="na">android:description=</span><span class="s">"@string/description"</span>
    <span class="na">android:accessibilityFeedbackType=</span><span class="s">"feedbackVisual"</span>
    <span class="na">android:notificationTimeout=</span><span class="s">"100"</span>
    <span class="na">android:accessibilityFlags=</span><span class="s">"flagDefault"</span>
    <span class="na">android:canRetrieveWindowContent=</span><span class="s">"true"</span>
    <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span> <span class="nt">/></span>

<span class="c"><!--  第3行等同于过滤 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED --></span>
<span class="c"><!--  第4行只监听 com.android.packageinstaller  --></span></code>

2. 展示区

进行了3种方式的安装测试:

  • 直接在手机上点击apk文件弹出安装,它能自动安装完成。写代码时没有那些特殊机器只好用这种方式来调试。

    ins0.gif
  • 通过adb安装,新安装和覆盖安装,在特殊手机上弹出安装后能自动安装完成。
    [无图]

  • 在运行appium时,appium会安装2个apk,新安装和覆盖安装,弹出安装后能自动完成。

    apum.gif

3. 其它区

利用AccessibilityService可以自动识别界面上的内容,并进行操作,也能达到自动化操作的目的。绿色守护的自动停止应用,豌豆荚的自动安装,一些抢红包的工具是用它来实现的。在最开始遇到这个问题的时候,我给了一个简单直接暴力的解决方法,在批处理中:

<code>...
start adb shell "sleep 3 && input tap 1200 300"
adb install -r xxx.apk
...</code>

后来为了兼容性强,做成了一个自动安装的工具,也许能勉强解决在自动化过程中出现安装提示的问题吧,期待大家共同验证和完善。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值