Android 静默安装和智能安装的实现方法

Android 静默安装和智能安装的实现方法,第1张

概述1简介最近研究了Android的静默安装智能安装,于是写博客记录一下。静默安装就是无声无息的在后台安装apk,没有任何界面提示。智能安装就是有安装界面,但全部是自动的,不需要用户去点击。

1 简介

最近研究了AndroID的静默安装和智能安装,于是写博客记录一下。静默安装就是无声无息的在后台安装apk,没有任何界面提示。智能安装就是有安装界面,但全部是自动的,不需要用户去点击。

首先强调两点:静默安装必须要root权限 智能安装必须要用户手动开启无障碍服务

2 原理

静默安装、卸载的原理就是利用pm install命令来安装apk,pm uninstall 来卸载apk. 智能安装是利用androID系统提供的无障碍服务AccessibilityService,来模拟用户点击,从而自动安装.

3 pm命令介绍

(1) pm install

pm install 命令的用法及参数解释如下:

<code >pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_name] [-s] [-f] PATHOptions: -l: install the package with FORWARD_LOCK. -r: reinstall an exisiting app,keePing its data. -t: allow test .apks to be installed. -i: specify the installer package name. -s: install package on sdcard. -f: install package on internal flash.</code>

(2) pm uninstall

pm uninstall 命令的用法及参数解释如下:

<code >pm uninstall [-k] PACKAGEOptions: -k: keep the data and cache directorIEs around.</code>

上面英语很简单,不解释了.

4 静默安装

为了方便演示,我把爱奇艺的安装包重命名为test.apk后放在了sdcard上。你可以自己去爱奇艺官网去下载,也可以自己找一个apk放到sdcard上,但是要知道apk的包名,后面卸载的时候要用到。

先上代码:

<code >//静默安装 private voID installSlIEnt() { String cmd = "pm install -r /mnt/sdcard/test.apk"; Process process = null; DataOutputStream os = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; try {  //静默安装需要root权限  process = Runtime.getRuntime().exec("su");  os = new DataOutputStream(process.getoutputStream());  os.write(cmd.getBytes());  os.writeBytes("\n");  os.writeBytes("exit\n");  os.flush();  //执行命令  process.waitFor();  //获取返回结果  successMsg = new StringBuilder();  errorMsg = new StringBuilder();  successResult = new BufferedReader(new inputStreamReader(process.getinputStream()));  errorResult = new BufferedReader(new inputStreamReader(process.getErrorStream()));  String s;  while ((s = successResult.readline()) != null) {  successMsg.append(s);  }  while ((s = errorResult.readline()) != null) {  errorMsg.append(s);  } } catch (Exception e) {  e.@R_419_1715@(); } finally {  try {  if (os != null) {   os.close();  }  if (process != null) {   process.destroy();  }  if (successResult != null) {   successResult.close();  }  if (errorResult != null) {   errorResult.close();  }  } catch (Exception e) {  e.@R_419_1715@();  } } //显示结果 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "错误消息: " + errorMsg.toString()); }</code>

这段代码就是在程序中执行pm命令,和在adb下执行 pm install -r /mnt/sdcard/test.apk 效果是一样的,关键的代码是 Runtime.getRuntime().exec(“su”) ,这段代码会要求获取root权限,所以你的手机必须root,不想root的话,直接用模拟器也可以。

通过 Runtime.getRuntime().exec(“su”) 获取到 process 对象后就可以写入命令了,每写入一条命令就要换行,写入 ‘\n' 即可,最后写入exit后离开命令执行的环境.

5 静默卸载

静默卸载和静默安装是一样的,只是命令不同,静默卸载需要用到包名,同样,静默卸载也需要root权限
看代码:

<code >//爱奇艺apk的包名private static final String PACKAGE_name = "com.qiyi.vIDeo";//静默卸载 private voID uninstallSlIEnt() { String cmd = "pm uninstall " + PACKAGE_name; Process process = null; DataOutputStream os = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; try {  //卸载也需要root权限  process = Runtime.getRuntime().exec("su");  os = new DataOutputStream(process.getoutputStream());  os.write(cmd.getBytes());  os.writeBytes("\n");  os.writeBytes("exit\n");  os.flush();  //执行命令  process.waitFor();  //获取返回结果  successMsg = new StringBuilder();  errorMsg = new StringBuilder();  successResult = new BufferedReader(new inputStreamReader(process.getinputStream()));  errorResult = new BufferedReader(new inputStreamReader(process.getErrorStream()));  String s;  while ((s = successResult.readline()) != null) {  successMsg.append(s);  }  while ((s = errorResult.readline()) != null) {  errorMsg.append(s);  } } catch (Exception e) {  e.@R_419_1715@(); } finally {  try {  if (os != null) {   os.close();  }  if (process != null) {   process.destroy();  }  if (successResult != null) {   successResult.close();  }  if (errorResult != null) {   errorResult.close();  }  } catch (Exception e) {  e.@R_419_1715@();  } } //显示结果 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "错误消息: " + errorMsg.toString()); }</code>

和静默安装一样的代码就不解释了。还有,如果你不知道一个apk的包名,那么请反编译后去看AndroIDManifest.xml文件,如果这个文件打开全是乱码,说明是被混淆过的,那么直接安装它,然后到/data/data下面去找它的包,当然,手机得root才能进/data/data目录。

6 智能安装

智能安装就稍微麻烦点了,原理是用到了androID提供的AccessibilityService服务,这个服务可以获取屏幕上的节点,一个节点也就是一个vIEw,我们写的xml文件中每个标签就是一个节点,然后可以模拟用户的 *** 作,对这些节点进行点击、滑动等 *** 作。我们就是利用这个原理,来自动点击安装按钮的,当然使用这个服务必须用户手动开启无障碍服务。下面我们来看具体的实现方法。

(1) 创建AccessibilityService配置文件

在res目录下创建XML目录,然后在xml目录下创建一个accessibility_service_config.xml文件,内容如下
res/xml/accessibility_service_config.xml:

<code  data-filtered="filtered"></accessibility-service></code>

accessibilityEventTypes:指定我们在监听窗口中可以模拟哪些事件,typeAllMask表示所有的事件都能模拟.

accessibilityFeedbackType:指定无障碍服务的反馈方式.

canRetrIEveWindowContent:指定是否允许我们的程序读取窗口中的节点和内容,当然是true.

description: 当用户手动配置服务时,会显示给用户看.

packagenames: 指定我们要监听哪个应用程序下的窗口活动,这里写com.androID.packageinstaller表示监听AndroID系统的安装界面。

其余参数照写即可。

res/strings.xml:

<code ><resources> <string name="app_name">SlIEntInstallTest</string> <string name="desc">智能安装app功能演示</string></resources></code>

(2) 创建AccessibilityService服务

<code >public class MyAccessibilityService extends AccessibilityService { private static final String TAG = "[TAG]"; private Map<integer,boolean=""> handleMap = new HashMap<>(); @OverrIDe public voID onAccessibilityEvent(AccessibilityEvent event) { AccessibilityNodeInfo nodeInfo = event.getSource(); if (nodeInfo != null) {  int eventType = event.getEventType();  if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {  if (handleMap.get(event.getwindowID()) == null) {   boolean handled = iterateNodesAndHandle(nodeInfo);   if (handled) {   handleMap.put(event.getwindowID(),true);   }  }  } } } @OverrIDe public voID onInterrupt() { } //遍历节点,模拟点击安装按钮 private boolean iterateNodesAndHandle(AccessibilityNodeInfo nodeInfo) { if (nodeInfo != null) {  int childCount = nodeInfo.getChildCount();  if ("androID.Widget.button".equals(nodeInfo.getClassname())) {  String nodeCotent = nodeInfo.getText().toString();  Log.d(TAG,"content is: " + nodeCotent);  if ("安装".equals(nodeCotent) || "完成".equals(nodeCotent) || "确定".equals(nodeCotent)) {   nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CliCK);   return true;  }  }  //遇到ScrollVIEw的时候模拟滑动一下  else if ("androID.Widget.ScrollVIEw".equals(nodeInfo.getClassname())) {  nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCRolL_FORWARD);  }  for (int i = 0; i < childCount; i++) {  AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);  if (iterateNodesAndHandle(childNodeInfo)) {   return true;  }  } } return false; }}</integer,></code>

当进入apk安装界面就会回调onAccessibilityEvent()这个方法,我们只关心TYPE_WINDOW_CONTENT_CHANGED和TYPE_WINDOW_STATE_CHANGED两个事件,为了防止重复处理事件,用一个map来过滤事件,后面递归遍历节点,找到'安装' ‘完成' ‘确定' 的按钮,就点击,由于安装界面需要滚动一下才能出现安装按钮,所以遇到ScrollVIEw的时候就滚动一下.

(3) 在AndroIDManifest中配置服务

<code ><uses-permission androID:name="androID.permission.READ_EXTERNAL_STORAGE">  <intent-filter>  <category androID:name="androID.intent.category.LAUNCHER">  </category></action></intent-filter> </activity> <service androID:label="智能安装App" androID:name=".MyAccessibilityService" androID:permission="androID.permission.BIND_ACCESSIBIliTY_SERVICE">  <intent-filter>  </action></intent-filter>  <Meta-data androID:name="androID.accessibilityservice" androID:resource="@xml/accessibility_service_config"> </Meta-data></service> </application></uses-permission></code>

重点是后面的service标签:

androID:label:这个就是用户看到的无障碍服务的名称

androID:permission: 需要用到BIND_ACCESSIBIliTY_SERVICE这个权限.

action: androID.accessibilityservice.AccessibilityService 有了这个action,用户才能在设置里面看到我们的服务,否则用户无法开启我们写的服务,也就不能进到我们写的MyAccessibilityService里面了.所以,注意不要写错了,如果你发现无障碍服务里面没有我们写的服务,请检查这里.

(4) 调用智能安装代码

前面准备工作完毕后,现在要用了,调用智能安装的代码如下:

<code > //智能安装 private voID smartInstall() { Uri uri = Uri.fromfile(new file("/mnt/sdcard/test.apk")); Intent localintent = new Intent(Intent.ACTION_VIEW); localintent.setDataAndType(uri,"application/vnd.androID.package-archive"); startActivity(localintent); }</code>

(5) 手动配置智能安装服务

代码运行之后,还要用户选择开启智能安装服务,让用户自己去找是不明智的,因此,我们要主动跳到配置界面,代码如下:

<code >//跳转到开启智能安装服务的界面Intent intent = new Intent(Settings.ACTION_ACCESSIBIliTY_SETTINGS);startActivity(intent);</code>

配置如下图:

看到了吗,上面显示的就是Service里面的label的值,如果你没有上面的选项,请检查AndroIDManifest里面Service的配置.
点击'智能安装App',开启服务,如下图:

其中的提示文字就是我们在res/xml/accessibility_service_config.xml文件中配置的description属性

7 只能我们写的app可以自动安装

这样写完代码可以运行,点击按钮自动安装sdcard上的test.apk.但是你会发现,所有apk都会自动安装,这就不符合我们的要求了,我们要求只能通过我们写的app来自动安装,其他apk还是要用户手动去点。怎么解决这个问题呢?

思路是:在MainActivity中创建一个public static boolean flag,在MyAccessibilityService的onAccessibilityEvent()中加一个flag判断,然后调用智能安装前flag设为true,创建apk安装事件的广播接收器,当apk安装完成后,设置falg为false,这样其他apk就不能自动安装了,就解决了这个问题

下面上完整代码.

8 完整代码

app/MainActivity.java:

<code >public class MainActivity extends AppCompatActivity implements VIEw.OnClickListener { private static final String TAG = "[TAG][MainActivity]"; private static final String PACKAGE_name = "com.qiyi.vIDeo"; private String apkPath = "/mnt/sdcard/test.apk"; public static boolean flag = false;//控制只能自己的app才能执行智能安装 private TextVIEw tvTest; private Myinstallreceiver receiver; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); tvTest = (TextVIEw) findVIEwByID(R.ID.tv_test); findVIEwByID(R.ID.btn_install).setonClickListener(this); findVIEwByID(R.ID.btn_uninstall).setonClickListener(this); findVIEwByID(R.ID.btn_set).setonClickListener(this); findVIEwByID(R.ID.btn_smart_install).setonClickListener(this); //注册apk安装监听 receiver = new Myinstallreceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("androID.intent.action.PACKAGE_ADDED"); filter.addAction("androID.intent.action.PACKAGE_REMOVED"); filter.addDataScheme("package"); this.registerReceiver(receiver,filter); } @OverrIDe public voID onClick(VIEw v) { switch (v.getID()) {  //静默安装  case R.ID.btn_install:  installSlIEnt();  break;  //静默卸载  case R.ID.btn_uninstall:  uninstallSlIEnt();  break;  //设置无障碍服务  case R.ID.btn_set:  //跳转到开启无障碍服务的界面  Intent intent = new Intent(Settings.ACTION_ACCESSIBIliTY_SETTINGS);  startActivity(intent);  break;  //智能安装  case R.ID.btn_smart_install:  //控制只能自己的app才能智能安装  flag = true;  smartInstall();  break; } } //静默安装 private voID installSlIEnt() { String cmd = "pm install -r /mnt/sdcard/test.apk"; Process process = null; DataOutputStream os = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; try {  //静默安装需要root权限  process = Runtime.getRuntime().exec("su");  os = new DataOutputStream(process.getoutputStream());  os.write(cmd.getBytes());  os.writeBytes("\n");  os.writeBytes("exit\n");  os.flush();  //执行命令  process.waitFor();  //获取返回结果  successMsg = new StringBuilder();  errorMsg = new StringBuilder();  successResult = new BufferedReader(new inputStreamReader(process.getinputStream()));  errorResult = new BufferedReader(new inputStreamReader(process.getErrorStream()));  String s;  while ((s = successResult.readline()) != null) {  successMsg.append(s);  }  while ((s = errorResult.readline()) != null) {  errorMsg.append(s);  } } catch (Exception e) {  e.@R_419_1715@(); } finally {  try {  if (os != null) {   os.close();  }  if (process != null) {   process.destroy();  }  if (successResult != null) {   successResult.close();  }  if (errorResult != null) {   errorResult.close();  }  } catch (Exception e) {  e.@R_419_1715@();  } } //显示结果 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "错误消息: " + errorMsg.toString()); } //静默卸载 private voID uninstallSlIEnt() { String cmd = "pm uninstall " + PACKAGE_name; Process process = null; DataOutputStream os = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; try {  //卸载也需要root权限  process = Runtime.getRuntime().exec("su");  os = new DataOutputStream(process.getoutputStream());  os.write(cmd.getBytes());  os.writeBytes("\n");  os.writeBytes("exit\n");  os.flush();  //执行命令  process.waitFor();  //获取返回结果  successMsg = new StringBuilder();  errorMsg = new StringBuilder();  successResult = new BufferedReader(new inputStreamReader(process.getinputStream()));  errorResult = new BufferedReader(new inputStreamReader(process.getErrorStream()));  String s;  while ((s = successResult.readline()) != null) {  successMsg.append(s);  }  while ((s = errorResult.readline()) != null) {  errorMsg.append(s);  } } catch (Exception e) {  e.@R_419_1715@(); } finally {  try {  if (os != null) {   os.close();  }  if (process != null) {   process.destroy();  }  if (successResult != null) {   successResult.close();  }  if (errorResult != null) {   errorResult.close();  }  } catch (Exception e) {  e.@R_419_1715@();  } } //显示结果 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "错误消息: " + errorMsg.toString()); } //智能安装 private voID smartInstall() { Uri uri = Uri.fromfile(new file(apkPath)); Intent localintent = new Intent(Intent.ACTION_VIEW); localintent.setDataAndType(uri,"application/vnd.androID.package-archive"); startActivity(localintent); } //监听apk安装 private class Myinstallreceiver extends broadcastReceiver { @OverrIDe public voID onReceive(Context context,Intent intent) {  if (intent.getAction().equals("androID.intent.action.PACKAGE_ADDED")) { // install  String packagename = intent.getDataString();  Log.i(TAG,"安装了 :" + packagename);  //安装完毕,设置flag,从而使得其余的apk不能自动安装  flag = false;  }  if (intent.getAction().equals("androID.intent.action.PACKAGE_REMOVED")) { // uninstall  String packagename = intent.getDataString();  Log.i(TAG,"卸载了 :" + packagename);  } } } @OverrIDe protected voID onDestroy() { super.onDestroy(); if (receiver != null) {  unregisterReceiver(receiver); } }}</code>

界面上就三个按钮

res/layout/activity_main.xml:

<code ><!--?xml version="1.0" enCoding="utf-8"?--><relativelayout androID:layout_height="match_parent" androID:layout_wIDth="match_parent" androID:paddingbottom="@dimen/activity_vertical_margin" androID:paddingleft="@dimen/activity_horizontal_margin" androID:paddingright="@dimen/activity_horizontal_margin" androID:paddingtop="@dimen/activity_vertical_margin" xmlns:androID="https://schemas.androID.com/apk/res/androID" xmlns:tools="https://schemas.androID.com/tools"> <textvIEw androID:ID="@+ID/tv_test" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" androID:text=""><button androID:ID="@+ID/btn_install" androID:layout_centerinparent="true" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" androID:text="安装"></button><button androID:ID="@+ID/btn_uninstall" androID:layout_below="@ID/btn_install" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" androID:text="卸载"></button><button androID:ID="@+ID/btn_set" androID:layout_below="@ID/btn_uninstall" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" androID:text="开启智能安装功能"></button></textvIEw></relativelayout></code><button androID:ID="@+ID/btn_smart_install" androID:layout_below="@ID/btn_set" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" androID:text="智能安装"><code ></code></button>

服务配置文件

res/xml/accessibility_service_config.xml

<code  data-filtered="filtered"></accessibility-service></code>

智能安装服务

app/MyAccessibilityService.java:

<code >public class MyAccessibilityService extends AccessibilityService { private static final String TAG = "[TAG]"; private Map<integer,boolean=""> handleMap = new HashMap<>(); @OverrIDe public voID onAccessibilityEvent(AccessibilityEvent event) { AccessibilityNodeInfo nodeInfo = event.getSource(); if (nodeInfo != null && MainActivity.flag) {  int eventType = event.getEventType();  if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {  if (handleMap.get(event.getwindowID()) == null) {   boolean handled = iterateNodesAndHandle(nodeInfo);   if (handled) {   handleMap.put(event.getwindowID(),></code>

最后是配置文件AndroIDManifest.xml:

<code ><manifest package="com.slIEntinstalltest" xmlns:androID="https://schemas.androID.com/apk/res/androID"> <uses-permission androID:name="androID.permission.READ_EXTERNAL_STORAGE">  <intent-filter>  <category androID:name="androID.intent.category.LAUNCHER">  </category></action></intent-filter> </activity> <service androID:label="智能安装App" androID:name=".MyAccessibilityService" androID:permission="androID.permission.BIND_ACCESSIBIliTY_SERVICE">  <intent-filter>  </action></intent-filter>  <Meta-data androID:name="androID.accessibilityservice" androID:resource="@xml/accessibility_service_config"> </Meta-data></service> </application></uses-permission></manifest></code>

注意:请把自己要安装的apk放到sdcard上,并且修改代码中的apk路径和包名

9 运行效果

 

10 总结

AndroID智能安装的原理就是利用了类似钩子的服务,这个服务还可以用于微信抢红包的开发,怎么样,是不是比ios好玩儿的多呢.

您可能感兴趣的文章:Android静默安装实现方案 仿360手机助手秒装和智能安装功能Android 静默安装和卸载的方法Android实现静默安装实例代码Android实现静默安装的两种方法Android无需root实现apk的静默安装android实现静默安装与卸载的方法 总结

以上是内存溢出为你收集整理的Android 静默安装和智能安装的实现方法全部内容,希望文章能够帮你解决Android 静默安装和智能安装的实现方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1143038.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存