Android自动抢红包,自动安装原理之AccessibilityService

euom2269 8年前
   <p>前段时间看别人博客的时候偶然间看到了<a href="http://www.open-open.com/lib/view/open1468638472552.html">Android微信自动回复功能</a>,最后的效果也很不错,博主在文中提到了<code>AccessibilityService</code>,以前压根没接触过这东西,表示一脸懵逼。也是这个原因我去找了AccessibilityService相关的资料好好的看了一遍,发现这个东西真的太NB了,网上对AccessibilityService的应用还是有不少的文章的,但是详细的介绍资料还是比较少,对于刚刚学习这个的同学看完很多资料还是一脸茫然,于是才有了本文。我相信当你在看完本文之后,再去看前面的那篇文章,我相信会轻松很多。</p>    <p>AccessibilityService是什么,官网是这样解释的</p>    <blockquote>     <p>Accessibility services are intended to assist users with disabilities in using Android devices and apps. They run in the background and receive callbacks by the system whenAccessibilityEvents are fired.</p>    </blockquote>    <p>也就是说这是个辅助功能,目的是帮助残疾人去使用Android设备和应用。它在后台运行,可以接收系统的回调。</p>    <p>可见AccessibilityService的出现Google的初衷是帮助残疾人去使用Android设备,但是当你对它足够了解了之后你会发现它的作用不仅仅只是这样。对windows编程有了解的同学肯定知道hook(钩子),AccessibilityService和windows下的hook有那么一点的相似。AccessibilityService可以拦截到系统发出的一些消息(比如窗体状态的改变,通知栏状态的改变,View被点击了等等),当拦截到这些事件我们就可以去做一些我们想做的事儿了~~~<br> AccessibilityService能做些什么呢? 比如自动化测试、自动抢红包、自动安装等等。在带来便利的同事,也还是有需要注意的地方:当你开启了辅助功能之后会对你的隐私信息带来一些风险,所以还是需要谨慎的去开启第三方的辅助功能。当然这对于我们开发者而言都不是事,因为我们完全可以自己去开发属于我们的辅助功能。</p>    <blockquote>     <h3>本文学习目录</h3>     <p>1.AccessibilityService的创建与配置<br> 2.怎么去使用AccessibilityService<br> 3.一步一步构建一个apk自动安装器</p>    </blockquote>    <h2>1.AccessibilityService的创建与配置</h2>    <p>第一步就是自己去创建一个类继承于AccessibilityService,并实现必须实现的两个方法</p>    <pre>  <code class="language-java">public class MyAccessibilityService extends AccessibilityService {      @Override      public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {        }        @Override      public void onInterrupt() {        }  }</code></pre>    <p>随后就是在res/xml目录下新建一个xml文件(文件名随意),后面会对这个文件作详细的介绍。</p>    <pre>  <code class="language-java"><accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"      android:accessibilityEventTypes="typeAllMask"      android:accessibilityFeedbackType="feedbackGeneric"        android:canRetrieveWindowContent="true"      android:description="@string/my_accessibility_description"      android:notificationTimeout="100"      android:packageNames="com.tencent.mobileqq,com.android.packageinstaller" /></code></pre>    <p>最后就是去配置清单文件了</p>    <pre>  <code class="language-java"> <service              android:name=".service.MyAccessibilityService"              android:enabled="true"              android:exported="true"              android:label="@string/app_name"              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/my_services_config" />//在meta-data里申明配置信息          </service></code></pre>    <p>别忘记了添加权限</p>    <pre>  <code class="language-java"><uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /></code></pre>    <p>到这我们就可以把应用跑到我们的手机上了,然后打开辅助功能,去开启我们刚刚创建的这个辅助功能。如下图:</p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/41e28eb013f3ab5d2c380a8149c4889d.png"></p>    <p>当然因为什么都没做,开启了也没什么用,下面就去看看怎么使用吧!</p>    <h2>2.怎么去使用AccessibilityService</h2>    <p>先说说之前创建的那个xml文件中各个属性的含义吧</p>    <ul>     <li><code>accessibilityEventTypes</code>: 用来设置响应事件的类型,比如typeAllMask就是响应全部事件,typeNotificationStateChanged就是响应通知状态的改变,如果需要响应多种事件类型可以以 ‘ | ’ 隔开。</li>     <li><code>accessibilityFeedbackType</code>: 给用户的反馈方式,比如语音、震动等,这里用处不大。</li>     <li><code>canRetrieveWindowContent</code> :是否可以获取活动窗体的内容,这个设置为true才可以取得窗体中的控件和事件源</li>     <li><code>description</code> :辅助功能的描述</li>     <li><code>notificationTimeout</code> :两个相同类型事件发送到服务的事件间隔,单位毫秒</li>     <li><code>packageNames</code> :指定响应某个应用的事件,取值为应用的包名,多个以‘ , ’ 隔开。没有此属性则表示响应全部应用。这里我填写的是手机qq和系统安装器的包名</li>    </ul>    <p>然后进入到我们的<code>MyAccessibilityService</code>中,定位到<code>onAccessibilityEvent</code>方法编写如下代码</p>    <pre>  <code class="language-java">log("-------------------------------------------------------------");          int eventType = event.getEventType();//事件类型          log("packageName:" + event.getPackageName() + "");//响应事件的包名,也就是哪个应用才响应了这个事件          log("source:" + event.getSource() + "");//事件源信息          log("source class:" + event.getClassName() + "");//事件源的类名,比如android.widget.TextView          log("event type(int):" + eventType + "");            switch (eventType) {              case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件                  log("event type:TYPE_NOTIFICATION_STATE_CHANGED");                  break;              case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://窗体状态改变                  log("event type:TYPE_WINDOW_STATE_CHANGED");                  break;              case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED://View获取到焦点                  log("event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");                  break;              case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:                  log("event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");                  break;              case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:                  log("event type:TYPE_GESTURE_DETECTION_END");                  break;              case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:                  log("event type:TYPE_WINDOW_CONTENT_CHANGED");                  break;              case AccessibilityEvent.TYPE_VIEW_CLICKED:                  log("event type:TYPE_VIEW_CLICKED");                  break;              case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:                  log("event type:TYPE_VIEW_TEXT_CHANGED");                  break;              case AccessibilityEvent.TYPE_VIEW_SCROLLED:                  log("event type:TYPE_VIEW_SCROLLED");                  break;              case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:                  log("event type:TYPE_VIEW_TEXT_SELECTION_CHANGED");                  break;          }            for (CharSequence txt : event.getText()) {              log("text:" + txt);//输出当前事件包含的文本信息          }            log("-------------------------------------------------------------");</code></pre>    <p>log方法就是打印信息用的,是对Log的一个简单封装。</p>    <p>在运行一次程序,打开我们的手机qq可以看见输出日志如下</p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/e618e334a4f9861cd81bfdc26300a410.png"></p>    <p>可以很清晰的看见当我们打开qq或触发很多的事件,第一个响应的事件是<code>TYPE_WINDOW_STATE_CHANGED</code>,触发此事件的事件源是<code>com.tencent.mobileqq.activity.SplashActivity</code></p>    <p>接着,当我点击最上面的 ‘ 电话 ’,会得到如下日志:</p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/6afc9d864b8661c6bfc24e4be7e71c39.png"></p>    <p>我就不一一截图操作的日志了,下来大家可以自行尝试。到这里你用该对<code>onAccessibilityEvent</code>有了进一步的了解。</p>    <h2>3.一步一步构建一个apk自动安装器</h2>    <p>这一节来个实战的应用:做一个apk的自动安装器,点击apk文件即可开始自动安装。</p>    <p>国内的rom厂商大家都很清楚:百(shang)花(xin)齐(bing)放(kuang),已经把原生的rom改的面目全非。所以想做一个面对全部手机的apk的自动安装器还是比较麻烦的。我的手机是魅族的,下图是魅族手机apk安装的步骤(其他手机可能会略有不同,下来自己更改不同部分即可,这里主要讲解原理):</p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/d8a64e495c1ac294f2e712f46eceb30d.png"></p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/523c44ebebf4bb0b4315ad347407dd97.png"></p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/18dcf1c4d89ccf04ddcdf42c67a44bdf.png"></p>    <p>可以看见点击安装包之后会弹出一个确认框,点击继续之后会出现下一步,点击了下一步就可以点击安装了。</p>    <p>好了,下面我们可以来创建一个自动安装的服务了,步骤和第一节描述的一样将<code>packageNames</code>指定成<code>com.android.packageinstaller</code>就可以了。这里我们先别着急去实现功能(就算你想去实现,也摸不着头脑),我们还是像第二节那样去输出日志信息,<strong>查看日志输出信息(对响应事件信息的打印)是使用AccessibilityService的重点</strong>,看完日志的输出信息,然后对其分析,才会知道怎么去做。</p>    <p>我们来看看在安装apk文件时候输出的部分日志信息:</p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/54d17fc239d3733881423f33fb9361cd.png"></p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/40160d5511405939d589f0e63c5740f5.png"></p>    <p><img alt="Android自动抢红包,自动安装原理之AccessibilityService" src="https://simg.open-open.com/show/83eae1cdc85a19cd4f8250ecfb1ecc2e.png"></p>    <p>从日志信息中可以看出弹出对话框中继续是个Button,接着的下一步是个TextView。到这里我们可以开始编写我们的代码了。</p>    <pre>  <code class="language-java">public class AutoInstallService extends AccessibilityService {        @Override      public void onAccessibilityEvent(AccessibilityEvent event) {          PrintUtils.printEvent(event);          findAndPerformActionButton("继续");          findAndPerformActionTextView("下一步");          findAndPerformActionTextView("安装");      }          private void findAndPerformActionButton(String text) {          if (getRootInActiveWindow() == null)//取得当前激活窗体的根节点              return;          //通过文字找到当前的节点          List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);          for (int i = 0; i < nodes.size(); i++) {              AccessibilityNodeInfo node = nodes.get(i);              // 执行点击行为              if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {                  node.performAction(AccessibilityNodeInfo.ACTION_CLICK);              }          }      }        private void findAndPerformActionTextView(String text) {          if (getRootInActiveWindow() == null)              return;          //通过文字找到当前的节点          List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);          for (int i = 0; i < nodes.size(); i++) {              AccessibilityNodeInfo node = nodes.get(i);              // 执行按钮点击行为              if (node.getClassName().equals("android.widget.TextView") && node.isEnabled()) {                  node.performAction(AccessibilityNodeInfo.ACTION_CLICK);              }          }      }  }</code></pre>    <p>例子很简单,代码已经注释的比较详细,我就不再对代码作更多的讲解了,有疑问可以留言。</p>    <p>到这里你应该对AccessibilityService有了进一步的认识,会发现原来红包助手之类软件的实现的原理也没那么难。是不是有点自己去做一个红包助手的想法了?现在再去看看文章开始的那篇文章你的收获会更大:<a href="/misc/goto?guid=4959675371720738717">Android微信自动回复功能</a></p>    <h3>补充说明</h3>    <p>AccessibilityService中还有几个常用的方法 onServiceConnected、onInterrupt、onGesture。看名字大致也知道什么时候会被调用。</p>    <p>在onServiceConnected中可以去配置AccessibilityService的一些信息,也就是之前在xml文件可以在这里通过代码配置,不过这里没法配置canRetrieveWindowContent属性,刚开始也被这个坑了很久</p>    <pre>  <code class="language-java">@Override      protected void onServiceConnected() {          super.onServiceConnected();          PrintUtils.log("onServiceConnected");  //        //可用代码配置当前Service的信息  //        AccessibilityServiceInfo info = new AccessibilityServiceInfo();  //        info.packageNames = new String[]{"com.android.packageinstaller", "com.tencent.mobileqq", "com.trs.gygdapp"}; //监听过滤的包名  //        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; //监听哪些行为  //        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; //反馈  //        info.notificationTimeout = 100; //通知的时间  //        setServiceInfo(info);      }</code></pre>    <p><a href="/misc/goto?guid=4959675371626467387">官方文档</a></p>    <p><a href="/misc/goto?guid=4959675615737066363">本文源码</a></p>    <p><br>  </p>    <p>来源:http://www.jianshu.com/p/65afab3d1e2a</p>    <p> </p>