国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android 靜默安裝和智能安裝的實現方法

2019-10-22 18:16:24
字體:
來源:轉載
供稿:網友

1 簡介

最近研究了Android的android/285067.html">靜默安裝和智能安裝,于是寫博客記錄一下。靜默安裝就是無聲無息的在后臺安裝apk,沒有任何界面提示。智能安裝就是有安裝界面,但全部是自動的,不需要用戶去點擊。

首先強調兩點:靜默安裝必須要root權限 智能安裝必須要用戶手動開啟無障礙服務

2 原理

靜默安裝、卸載的原理就是利用pm install命令來安裝apk,pm uninstall 來卸載apk. 智能安裝是利用android系統提供的無障礙服務AccessibilityService,來模擬用戶點擊,從而自動安裝.

3 pm命令介紹

(1) pm install

pm install 命令的用法及參數解釋如下:

<code class="hljs haml">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 class="hljs livecodeserver">pm uninstall [-k] PACKAGEOptions: -k: keep the data and cache directories around.</code>

上面英語很簡單,不解釋了.

4 靜默安裝

為了方便演示,我把愛奇藝的安裝包重命名為test.apk后放在了sdcard上。你可以自己去愛奇藝官網去下載,也可以自己找一個apk放到sdcard上,但是要知道apk的包名,后面卸載的時候要用到。

先上代碼:

<code class="hljs cs">//靜默安裝 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()); }</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 class="hljs java">//愛奇藝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()); }</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 class="hljs xml" data-filtered="filtered"></accessibility-service></code>

accessibilityEventTypes:指定我們在監聽窗口中可以模擬哪些事件,typeAllMask表示所有的事件都能模擬.

accessibilityFeedbackType:指定無障礙服務的反饋方式.

canRetrieveWindowContent:指定是否允許我們的程序讀取窗口中的節點和內容,當然是true.

description: 當用戶手動配置服務時,會顯示給用戶看.

packageNames: 指定我們要監聽哪個應用程序下的窗口活動,這里寫com.android.packageinstaller表示監聽Android系統的安裝界面。

其余參數照寫即可。

res/strings.xml:

<code class="hljs xml"><resources> <string name="app_name">SlientInstallTest</string> <string name="desc">智能安裝app功能演示</string></resources></code>

(2) 創建AccessibilityService服務

<code class="hljs 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) {  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 class="hljs xml"><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 class="hljs cs"> //智能安裝 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 class="hljs cs">//跳轉到開啟智能安裝服務的界面Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);startActivity(intent);</code>

配置如下圖:

android,靜默安裝,智能安裝

看到了嗎,上面顯示的就是Service里面的label的值,如果你沒有上面的選項,請檢查AndroidManifest里面Service的配置.
點擊'智能安裝App',開啟服務,如下圖:

android,靜默安裝,智能安裝

其中的提示文字就是我們在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 class="hljs 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); } }}</code>

界面上就三個按鈕

res/layout/activity_main.xml:

<code class="hljs xml"><!--?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 class="hljs xml"></code></button>

服務配置文件

res/xml/accessibility_service_config.xml

<code class="hljs xml" data-filtered="filtered"></accessibility-service></code>

智能安裝服務

app/MyAccessibilityService.java:

<code class="hljs 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; }}</integer,></code>

最后是配置文件AndroidManifest.xml:

<code class="hljs xml"><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 運行效果

android,靜默安裝,智能安裝

10 總結

Android智能安裝的原理就是利用了類似鉤子的服務,這個服務還可以用于微信搶紅包的開發,怎么樣,是不是比ios好玩兒的多呢.


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 锦州市| 如东县| 新闻| 隆林| 鄂托克旗| 衡水市| 阿勒泰市| 通辽市| 中山市| 盐津县| 威信县| 乐清市| 绥棱县| 玛曲县| 慈利县| 利川市| 凤城市| 新建县| 敦煌市| 如东县| 望江县| 上林县| 城步| 精河县| 中阳县| 永康市| 平和县| 永修县| 五大连池市| 平定县| 吉木萨尔县| 长治县| 溆浦县| 毕节市| 措勤县| 自贡市| 内黄县| 牟定县| 淅川县| 呼伦贝尔市| 陇川县|