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

来源:互联网 发布:痴迷网络辅导记录表 编辑:程序博客网 时间:2024/05/16 19:09

目录(?)[+]

1 简介

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

  1. 静默安装必须要root权限
  2. 智能安装必须要用户手动开启无障碍服务

2 原理

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

3 pm命令介绍

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

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.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

pm uninstall [-k] PACKAGEOptions:  -k: keep the data and cache directories around.
  • 1
  • 2
  • 3
  • 4

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

4 静默安装

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

//静默安装    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.printStackTrace();        } 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.printStackTrace();            }        }        //显示结果        tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "错误消息: " + errorMsg.toString());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

这段代码就是在程序中执行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权限 
看代码:

//爱奇艺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.printStackTrace();        } 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.printStackTrace();            }        }        //显示结果        tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "错误消息: " + errorMsg.toString());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

和静默安装一样的代码就不解释了。还有,如果你不知道一个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:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"                       android:accessibilityEventTypes="typeAllMask"                       android:accessibilityFeedbackType="feedbackGeneric"                       android:accessibilityFlags="flagDefault"                       android:canRetrieveWindowContent="true"                       android:description="@string/desc"                       android:packageNames="com.android.packageinstaller"    />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

accessibilityEventTypes:指定我们在监听窗口中可以模拟哪些事件,typeAllMask表示所有的事件都能模拟. 
accessibilityFeedbackType:指定无障碍服务的反馈方式. 
canRetrieveWindowContent:指定是否允许我们的程序读取窗口中的节点和内容,当然是true. 
description: 当用户手动配置服务时,会显示给用户看. 
packageNames: 指定我们要监听哪个应用程序下的窗口活动,这里写com.android.packageinstaller表示监听Android系统的安装界面。 
其余参数照写即可。 
res/strings.xml:

<resources>    <string name="app_name">SlientInstallTest</string>    <string name="desc">智能安装app功能演示</string></resources>
  • 1
  • 2
  • 3
  • 4

(2) 创建AccessibilityService服务

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;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

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

(3) 在AndroidManifest中配置服务

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>        <service            android:name=".MyAccessibilityService"            android:label="智能安装App"            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/accessibility_service_config"                />        </service>    </application>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

重点是后面的service标签: 
android:label:这个就是用户看到的无障碍服务的名称 
android:permission: 需要用到BIND_ACCESSIBILITY_SERVICE这个权限. 
action: android.accessibilityservice.AccessibilityService 有了这个action,用户才能在设置里面看到我们的服务,否则用户无法开启我们写的服务,也就不能进到我们写的MyAccessibilityService里面了.所以,注意不要写错了,如果你发现无障碍服务里面没有我们写的服务,请检查这里.

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

 //智能安装    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);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

//跳转到开启智能安装服务的界面Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);startActivity(intent);
  • 1
  • 2
  • 3

配置如下图:

这里写图片描述

看到了吗,上面显示的就是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:

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.printStackTrace();        } 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.printStackTrace();            }        }        //显示结果        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.printStackTrace();        } 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.printStackTrace();            }        }        //显示结果        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);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198

界面上就三个按钮 
res/layout/activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="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">    <TextView        android:id="@+id/tv_test"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text=""        />    <Button        android:id="@+id/btn_install"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:text="安装"        />    <Button        android:id="@+id/btn_uninstall"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/btn_install"        android:text="卸载"        />    <Button        android:id="@+id/btn_set"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/btn_uninstall"        android:text="开启智能安装功能"        />    <Button        android:id="@+id/btn_smart_install"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/btn_set"        android:text="智能安装"        /></RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

服务配置文件 
res/xml/accessibility_service_config.xml

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"                       android:accessibilityEventTypes="typeAllMask"                       android:accessibilityFeedbackType="feedbackGeneric"                       android:accessibilityFlags="flagDefault"                       android:canRetrieveWindowContent="true"                       android:description="@string/desc"                       android:packageNames="com.android.packageinstaller"    />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

智能安装服务 
app/MyAccessibilityService.java:

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(), 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;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

最后是配置文件AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"          package="com.slientinstalltest">    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>        <service            android:name=".MyAccessibilityService"            android:label="智能安装App"            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/accessibility_service_config"                />        </service>    </application></manifest>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

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

9 运行效果

这里写图片描述

10 代码下载

演示代码已经上传,下载地址:

http://download.csdn.net/detail/fuchaosz/9570881

11 总结

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

12 转载请注明来自”梧桐那时雨”的博客:http://blog.csdn.net/fuchaosz/article/details/51852442

阅读全文
0 0
原创粉丝点击