轉載:Android 靜默安裝和智慧安裝的實現方法
1 簡介
最近研究了Android的靜默安裝和智慧安裝,於是寫部落格記錄一下。 靜默安裝就是無聲無息的在後檯安裝apk,沒有任何介面提示。 智慧安裝就是有安裝介面,但全部是自動的,不需要使用者去點選。 首先強調兩點:
- 靜默安裝必須要root許可權
- 智慧安裝必須要使用者手動開啟無障礙服務
2 原理
- 靜默安裝、解除安裝的原理就是利用pm install命令來安裝apk,pm uninstall 來解除安裝apk.
- 智慧安裝是利用android系統提供的無障礙服務AccessibilityService,來模擬使用者點選,從而自動安裝.
3 pm命令介紹
(1) pm install
pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH Options: -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.
(2) pm uninstall pm uninstall 命令的用法及引數解釋如下:
pm uninstall [-k] PACKAGE
Options:
-k: keep the data and cache directories around.
上面英語很簡單,不解釋了.
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()); }
這段程式碼就是在程式中執行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());
}
和靜默安裝一樣的程式碼就不解釋了。還有,如果你不知道一個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"
/>
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>
(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;
}
}
當進入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>
重點是後面的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);
}
(5) 手動配置智慧安裝服務 程式碼執行之後,還要使用者選擇開啟智慧安裝服務,讓使用者自己去找是不明智的,因此,我們要主動跳到配置介面,程式碼如下:
//跳轉到開啟智慧安裝服務的介面
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
配置如下圖:
看到了嗎,上面顯示的就是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 程式碼下載
演示程式碼已經上傳,下載地址:
11 總結
Android智慧安裝的原理就是利用了類似鉤子的服務,這個服務還可以用於微信搶紅包的開發,怎麼樣,是不是比ios好玩兒的多呢.