reference to  :  http://www.infoq.com/cn/articles/android-accessibility-installing?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global

对于国内Android设备,应用的自动批量安装/更新一直是一个痛点,在之前,第三方应用商店通常要求设备Root,然后调用系统的 PackageManagerService命令行来实现后台安装。最近,豌豆荚利用Android Accessibility(辅助功能)在业内率先实现了免Root自动批量安装功能。

这个功能实现的原理是,在后台批量下载应用后,调用系统的PackageInstaller,获取安装界面的按钮位置,然后通过Accessibility提供的模拟用户点击功能,代替用户自动点击下一步,直到安装结束。

虽然技术看起来不是特别困难,但在实现中还是有不少坑的,豌豆荚工程师向我们分享了该功能的一些技术细节和实践经验。

Android Accessibility API介绍与调用方法

对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务 帮助这些用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这可以 加强应用的可用性,例如声音提示,物理反馈,和其他可选的操作模式。

随着Android系统版本的迭代,Accessibility功能也越来越强大,它能实时地获取当前操作应用的窗口元素信息,并能够双向交互,既能获取用户的输入,也能对窗口元素进行操作,比如点击按钮。更多的介绍见Android开发者官网的Accessibility页面

调用Android Accessibility API需要三个步骤:申请权限、注册 Service、配置 Accessibility Service Info。使用Accessibility API需要的权限如下:

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>

注册Service

<service android:name="com.your.AccessibilityImpl.className"
android:label="@string/acc_auto_install_service_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="@bool/enable_accessibility">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_config" />
</service>

配置Accessibility Service Info

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/acc_description" android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:settingsActivity="com.your.settingActivity"
android:packageNames="packageName1,packageName2"
/>

需要说明的一点是,在配置配置 Accessibility Service Info时,如果明确的知道目标APP的包名,那一定要使用packageNames属性进行设置。举一个例子:

在一些使用虚拟键盘的APP中,经常会出现这样的逻辑

Button button = (Button) findViewById(R.id.button);
String num = (String) button.getText();

在一般情况下,getText方法的返回值是Java.lang.String类的实例,上面这段代码可以正确运行。但是在开启Accessibility Service之后,如果没有指定packageNames,系统会对所有APP的UI都进行Accessible的处理。在这个例子中的表现就是getText方法的返回值变成了android.text.SpannableString类的实例(Java.lang.Stringandroid.text.SpannableString都实现了java.lang.CharSequence接口),进而造成目标APP崩溃。

所以强烈建议在注册Accessibility Service时指定目标APP的packageName,以减少手机上其他应用的莫名崩溃(代码中有这样的逻辑的各位,也请默默的改为调用toString()方法吧)。

实现AccessibilityService

继承android.accessibilityservice.AccessibilityService并重载onAccessibilityEventonInterrupt方法:

public class AccessibilityImpl extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {}
@Override
public void onInterrupt() {}
}

作者 徐川 发布于 2015年5月28日 | 讨论

对于国内Android设备,应用的自动批量安装/更新一直是一个痛点,在之前,第三方应用商店通常要求设备Root,然后调用 系统的PackageManagerService命令行来实现后台安装。最近,豌豆荚利用Android Accessibility(辅助功能)在业内率先实现了免Root自动批量安装功能。

这个功能实现的原理是,在后台批量下载应用后,调用系统的PackageInstaller,获取安装界面的按钮位置,然后通过Accessibility提供的模拟用户点击功能,代替用户自动点击下一步,直到安装结束。

虽然技术看起来不是特别困难,但在实现中还是有不少坑的,豌豆荚工程师向我们分享了该功能的一些技术细节和实践经验。

Android Accessibility API介绍与调用方法

对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务 帮助这些用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这可以 加强应用的可用性,例如声音提示,物理反馈,和其他可选的操作模式。

随着Android系统版本的迭代,Accessibility功能也越来越强大,它能实时地获取当前操作应用的窗口元素信息,并能够双向交互,既能获取用户的输入,也能对窗口元素进行操作,比如点击按钮。更多的介绍见Android开发者官网的Accessibility页面

调用Android Accessibility API需要三个步骤:申请权限、注册 Service、配置 Accessibility Service Info。使用Accessibility API需要的权限如下:

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>

注册Service

<service android:name="com.your.AccessibilityImpl.className"
android:label="@string/acc_auto_install_service_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="@bool/enable_accessibility">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_config" />
</service>

配置Accessibility Service Info

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/acc_description" android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:settingsActivity="com.your.settingActivity"
android:packageNames="packageName1,packageName2"
/>

需要说明的一点是,在配置配置 Accessibility Service Info时,如果明确的知道目标APP的包名,那一定要使用packageNames属性进行设置。举一个例子:

在一些使用虚拟键盘的APP中,经常会出现这样的逻辑

Button button = (Button) findViewById(R.id.button);
String num = (String) button.getText();

在一般情况下,getText方法的返回值是Java.lang.String类的实例,上面这段代码可以正确运行。但是在开启Accessibility Service之后,如果没有指定packageNames,系统会对所有APP的UI都进行Accessible的处理。在这个例子中的表现就是getText方法的返回值变成了android.text.SpannableString类的实例(Java.lang.Stringandroid.text.SpannableString都实现了java.lang.CharSequence接口),进而造成目标APP崩溃。

所以强烈建议在注册Accessibility Service时指定目标APP的packageName,以减少手机上其他应用的莫名崩溃(代码中有这样的逻辑的各位,也请默默的改为调用toString()方法吧)。

实现AccessibilityService

继承android.accessibilityservice.AccessibilityService并重载onAccessibilityEventonInterrupt方法:

public class AccessibilityImpl extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {}
@Override
public void onInterrupt() {}
}

以onAccessibilityEvent与onInterrupt为入口实现业务逻辑代码。

如何获取UI元素

onAccessibilityEvent中,使用参数event的getSource方法获取到的AccessibilityNodeInfo实例,即为触发这次事件的UI节点。

如果需要获取当前界面上的其它元素,需要获取到当前界面UI Tree的根节点后再使用findAccessibilityNodeInfosByText或者findAccessibilityNodeInfosByViewId方法进行获取。

需要注意的一点是,findAccessibilityNodeInfosByText在获取UI元素时的判断逻辑是contains而非equals,在使用时可能要根据具体业务逻辑做进一步的处理。

模拟用户点击

实现AccessibilityService,并获取界面上UI元素之后,可以使用下面的代码来模拟用户点击:

nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);

需要注意的是,在触发事件之前需要确定该UI元素在界面上是否依旧存在。使用该方法还可以模拟用户的其它操作,甚至是复制粘贴这种行为,具体可以参考AccessibilityNodeInfo

reference to : http://stackoverflow.com/questions/18094982/detect-if-my-accessibility-service-is-enabled

check is  our AccessibilityService is swith on

// To check if service is enabled
private boolean isAccessibilitySettingsOn(Context mContext) {
int accessibilityEnabled = 0;
final String service = mContext.getPackageName() + File.separator + mContext.getPackageName() + ".accesibility.MyAccesibilityService";
boolean accessibilityFound = false;
try {
accessibilityEnabled = Settings.Secure.getInt(
mContext.getApplicationContext().getContentResolver(),
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled);
} catch (Exception e) {
Log.e(TAG, "Error finding setting, default accessibility to not found: "
+ e.getMessage());
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); if (accessibilityEnabled == 1) {
Log.v(TAG, "***ACCESSIBILIY IS ENABLED*** -----------------");
String settingValue = Settings.Secure.getString(
mContext.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(settingValue);
while (splitter.hasNext()) {
String accessabilityService = splitter.next(); Log.v(TAG, "-------------- > accessabilityService :: " + accessabilityService);
if (accessabilityService.equalsIgnoreCase(service)) {
Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
} else {
Log.v(TAG, "***ACCESSIBILIY IS DISABLED***");
} return accessibilityFound;
}

go to setting ui :

if (!isAccessibilitySettingsOn(this)) {
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
}

[Android Pro] Android 4.1 使用 Accessibility实现免Root自动批量安装功能的更多相关文章

  1. [Android Pro] Android 手机root 并 安装 BusyBox pro 和 Android Terminal Emulator

    Android root 工具:http://www.z4root.cn/yijianrootshouji/ 推荐的是:root精灵手机版 BusyBox 称为 Linux 工具里的瑞士军刀.简单的说 ...

  2. [Android Pro] Android开发实践:自定义ViewGroup的onLayout()分析

    reference to : http://www.linuxidc.com/Linux/2014-12/110165.htm 前一篇文章主要讲了自定义View为什么要重载onMeasure()方法( ...

  3. [Android Pro] Android开发实践:为什么要继承onMeasure()

    reference to : http://www.linuxidc.com/Linux/2014-12/110164.htm Android开 发中偶尔会用到自定义View,一般情况下,自定义Vie ...

  4. [Android Pro] android 4.4 Android原生权限管理:AppOps

    reference : http://m.blog.csdn.net/blog/langzxz/45308199 reference : http://blog.csdn.net/hyhyl1990/ ...

  5. [Android Pro] Android 4.3 NotificationListenerService使用详解

    reference to : http://blog.csdn.net/yihongyuelan/article/details/40977323 概况 Android在4.3的版本中(即API 18 ...

  6. [Android Pro] Android签名与认证详细分析之二(CERT.RSA剖析)

    转载自: http://www.thinksaas.cn/group/topic/335449/ http://blog.csdn.net/u010571535/article/details/899 ...

  7. [Android Pro] android 杀死进程的方法

    1: 杀死自己进程的方法 android.os.Process.killProcess(Process.myPid()); 2:杀死别人进程的方法(不能杀死自己) -------a: activity ...

  8. [Android Pro] Android权限设置android.permission完整列表

    android.permission.ACCESS_CHECKIN_PROPERTIES允许读写访问"properties”表在checkin数据库中,改值可以修改上传( Allows re ...

  9. [Android Pro] Android的Animation之LayoutAnimation使用方法

    用于为一个里面的控件,或者是一个里面的控件设置动画效果,可以在文件中设置,亦可以在代码中设置. 一种直接在XML文件中设置 1.  在res/anim文件夹下新建一个XML文件,名为list_anim ...

随机推荐

  1. 在PHP中遍历数据库表中的数据

    数据库中的数据: //1.分别将每一行的每一列遍历出来 //mysql_fetch_row()函数在每一次遍历后会将指针向后自动移动一个单位 while($row=mysql_fetch_row($r ...

  2. Maven编译项目报错:某些类找不到符号

      遇到Maven在编译项目源码时候出现找不到符号错误,主要归结为以下几个问题: 1. 可能项目编码格式不统一. 2. 可能项目编码使用的JDK版本不统一. 项目可能是当前项目也可能是继承的父项目,还 ...

  3. Html中各种空格的显示

    一.使用全角空格 全角空格被解释为汉字,所以不会被被解释为HTML分隔符,可以按照实际的空格数显示. 二.使用空格的替代符号 替代符号就是在需要显示空格的地方加入替代符号,这些符号会被浏览器解释为空格 ...

  4. WebStorm设置左侧菜单栏背景色和样式

    WebStrom一直以来都是默认的白色主题,今天想修改了下主题皮肤,结果导致左侧项目资源栏和顶部菜单栏也变成了黑色,结果无法改变回来,网上查了各种帖子,居然也没找到解决方法,自己研究了半天,终于搞定了 ...

  5. mysql 主从数据库设置方法

    1.主从数据库都需开启bin-log日志 2.在my.ini(windows)或my.cnf(linux)配置文件中添加 server-id = 1(主从配置 id 必须不同) 例子: [mysqld ...

  6. Bootstrap IIFE

    在Bootstrap源码(具体请看<Bootstrap源码解析>)和其他jQuery插件经常看到如下的写法: +function ($) { }(window.jQuery); 这种写法称 ...

  7. 4.了解AngularJS模块和依赖注入

    1.模块和依赖注入概述 1.了解模块 AngularJS模块是一种容器,把代码隔离并组织成简洁,整齐,可复用的块. 模块本身不提供直接的功能:包含其他提供功能的对象的实例:控制器,过滤器,服务,动画 ...

  8. 常用js正则归类

    //定义正则表达式 //title,只能是数字字母汉字开头,不能以下划线结尾,不包含特殊字符3-20 var title = /^(?!_)(?!.*?_$)[)(\sa-zA-Z0-9_\u4e00 ...

  9. Droid4x快照还原

    一.问题描述 1. Droid4x还原快照可以通过VirtualBox 先还原快照 2. virtualbox 还原快照之后 如果没有用virtualbox启动 并关闭     而是直接启动Droid ...

  10. WinForm多线程编程简单Demo

    需要搭建一个可以监控报告生成的CS(WinForm)工具,即CS不断Run,执行获取数据生成报告,经过研究和实践,选择了使用"WinForm多线程编程"的解决方案.当然参考了园中相 ...