真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Android靜默安裝和智能安裝的實(shí)現(xiàn)方法

1 簡(jiǎn)介

創(chuàng)新互聯(lián)公司專注于黃石港網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供黃石港營銷型網(wǎng)站建設(shè),黃石港網(wǎng)站制作、黃石港網(wǎng)頁設(shè)計(jì)、黃石港網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務(wù),打造黃石港網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供黃石港網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

最近研究了Android的靜默安裝和智能安裝,于是寫博客記錄一下。靜默安裝就是無聲無息的在后臺(tái)安裝apk,沒有任何界面提示。智能安裝就是有安裝界面,但全部是自動(dòng)的,不需要用戶去點(diǎn)擊。

首先強(qiáng)調(diào)兩點(diǎn):靜默安裝必須要root權(quán)限 智能安裝必須要用戶手動(dòng)開啟無障礙服務(wù)

2 原理

靜默安裝、卸載的原理就是利用pm install命令來安裝apk,pm uninstall 來卸載apk. 智能安裝是利用android系統(tǒng)提供的無障礙服務(wù)AccessibilityService,來模擬用戶點(diǎn)擊,從而自動(dòng)安裝.

3 pm命令介紹

(1) pm install

pm install 命令的用法及參數(shù)解釋如下:

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 命令的用法及參數(shù)解釋如下:

pm uninstall [-k] PACKAGE
Options:
 -k: keep the data and cache directories around.

上面英語很簡(jiǎn)單,不解釋了.

4 靜默安裝

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

先上代碼:

//靜默安裝
 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權(quán)限
  process = Runtime.getRuntime().exec("su");
  os = new DataOutputStream(process.getOutputStream());
  os.write(cmd.getBytes());
  os.writeBytes("\n");
  os.writeBytes("exit\n");
  os.flush();
  //執(zhí)行命令
  process.waitFor();
  //獲取返回結(jié)果
  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();
  }
 }
 //顯示結(jié)果
 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "錯(cuò)誤消息: " + errorMsg.toString());
 }

這段代碼就是在程序中執(zhí)行pm命令,和在adb下執(zhí)行 pm install -r /mnt/sdcard/test.apk 效果是一樣的, 關(guān)鍵的代碼是 Runtime.getRuntime().exec(“su”) ,這段代碼會(huì)要求獲取root權(quán)限,所以你的手機(jī)必須root,不想root的話,直接用模擬器也可以。

通過 Runtime.getRuntime().exec(“su”) 獲取到 process 對(duì)象后就可以寫入命令了,每寫入一條命令就要換行,寫入 ‘\n' 即可,最后寫入exit后離開命令執(zhí)行的環(huán)境.

5 靜默卸載

靜默卸載和靜默安裝是一樣的,只是命令不同,靜默卸載需要用到包名,同樣,靜默卸載也需要root權(quán)限
看代碼:

//愛奇藝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權(quán)限
  process = Runtime.getRuntime().exec("su");
  os = new DataOutputStream(process.getOutputStream());
  os.write(cmd.getBytes());
  os.writeBytes("\n");
  os.writeBytes("exit\n");
  os.flush();
  //執(zhí)行命令
  process.waitFor();
  //獲取返回結(jié)果
  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();
  }
 }
 //顯示結(jié)果
 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "錯(cuò)誤消息: " + errorMsg.toString());
 }

和靜默安裝一樣的代碼就不解釋了。還有,如果你不知道一個(gè)apk的包名,那么請(qǐng)反編譯后去看AndroidManifest.xml文件,如果這個(gè)文件打開全是亂碼,說明是被混淆過的,那么直接安裝它,然后到/data/data下面去找它的包,當(dāng)然,手機(jī)得root才能進(jìn)/data/data目錄。

6 智能安裝

智能安裝就稍微麻煩點(diǎn)了,原理是用到了android提供的AccessibilityService服務(wù),這個(gè)服務(wù)可以獲取屏幕上的節(jié)點(diǎn),一個(gè)節(jié)點(diǎn)也就是一個(gè)view,我們寫的xml文件中每個(gè)標(biāo)簽就是一個(gè)節(jié)點(diǎn),然后可以模擬用戶的操作,對(duì)這些節(jié)點(diǎn)進(jìn)行點(diǎn)擊、滑動(dòng)等操作。我們就是利用這個(gè)原理,來自動(dòng)點(diǎn)擊安裝按鈕的,當(dāng)然使用這個(gè)服務(wù)必須用戶手動(dòng)開啟無障礙服務(wù)。下面我們來看具體的實(shí)現(xiàn)方法。

(1) 創(chuàng)建AccessibilityService配置文件

在res目錄下創(chuàng)建xml目錄,然后在xml目錄下創(chuàng)建一個(gè)accessibility_service_config.xml文件,內(nèi)容如下
res/xml/accessibility_service_config.xml:

accessibilityEventTypes:指定我們?cè)诒O(jiān)聽窗口中可以模擬哪些事件,typeAllMask表示所有的事件都能模擬.

accessibilityFeedbackType:指定無障礙服務(wù)的反饋方式.

canRetrieveWindowContent:指定是否允許我們的程序讀取窗口中的節(jié)點(diǎn)和內(nèi)容,當(dāng)然是true.

description: 當(dāng)用戶手動(dòng)配置服務(wù)時(shí),會(huì)顯示給用戶看.

packageNames: 指定我們要監(jiān)聽哪個(gè)應(yīng)用程序下的窗口活動(dòng),這里寫com.android.packageinstaller表示監(jiān)聽Android系統(tǒng)的安裝界面。

其余參數(shù)照寫即可。

res/strings.xml:


 SlientInstallTest
 智能安裝app功能演示

(2) 創(chuàng)建AccessibilityService服務(wù)

public class MyAccessibilityService extends AccessibilityService {
 private static final String TAG = "[TAG]";
 private Map 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() {
 }
 //遍歷節(jié)點(diǎn),模擬點(diǎn)擊安裝按鈕
 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的時(shí)候模擬滑動(dòng)一下
  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;
 }
}

當(dāng)進(jìn)入apk安裝界面就會(huì)回調(diào)onAccessibilityEvent()這個(gè)方法,我們只關(guān)心TYPE_WINDOW_CONTENT_CHANGED和TYPE_WINDOW_STATE_CHANGED兩個(gè)事件,為了防止重復(fù)處理事件,用一個(gè)map來過濾事件,后面遞歸遍歷節(jié)點(diǎn),找到'安裝' ‘完成' ‘確定' 的按鈕,就點(diǎn)擊,由于安裝界面需要滾動(dòng)一下才能出現(xiàn)安裝按鈕,所以遇到ScrollView的時(shí)候就滾動(dòng)一下.

(3) 在AndroidManifest中配置服務(wù)


  
  
  
 
 
  
  
  
 
 

重點(diǎn)是后面的service標(biāo)簽:

android:label:這個(gè)就是用戶看到的無障礙服務(wù)的名稱

android:permission: 需要用到BIND_ACCESSIBILITY_SERVICE這個(gè)權(quán)限.

action: android.accessibilityservice.AccessibilityService 有了這個(gè)action,用戶才能在設(shè)置里面看到我們的服務(wù),否則用戶無法開啟我們寫的服務(wù),也就不能進(jìn)到我們寫的MyAccessibilityService里面了.所以,注意不要寫錯(cuò)了,如果你發(fā)現(xiàn)無障礙服務(wù)里面沒有我們寫的服務(wù),請(qǐng)檢查這里.

(4) 調(diào)用智能安裝代碼

前面準(zhǔn)備工作完畢后,現(xiàn)在要用了,調(diào)用智能安裝的代碼如下:

 //智能安裝
 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) 手動(dòng)配置智能安裝服務(wù)

代碼運(yùn)行之后,還要用戶選擇開啟智能安裝服務(wù),讓用戶自己去找是不明智的,因此,我們要主動(dòng)跳到配置界面,代碼如下:

//跳轉(zhuǎn)到開啟智能安裝服務(wù)的界面
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);

配置如下圖:

Android 靜默安裝和智能安裝的實(shí)現(xiàn)方法

看到了嗎,上面顯示的就是Service里面的label的值,如果你沒有上面的選項(xiàng),請(qǐng)檢查AndroidManifest里面Service的配置.
點(diǎn)擊'智能安裝App',開啟服務(wù),如下圖:

Android 靜默安裝和智能安裝的實(shí)現(xiàn)方法

其中的提示文字就是我們?cè)趓es/xml/accessibility_service_config.xml文件中配置的description屬性

7 只能我們寫的app可以自動(dòng)安裝

這樣寫完代碼可以運(yùn)行,點(diǎn)擊按鈕自動(dòng)安裝sdcard上的test.apk.但是你會(huì)發(fā)現(xiàn),所有apk都會(huì)自動(dòng)安裝,這就不符合我們的要求了,我們要求只能通過我們寫的app來自動(dòng)安裝,其他apk還是要用戶手動(dòng)去點(diǎn)。怎么解決這個(gè)問題呢?

思路是:在MainActivity中創(chuàng)建一個(gè)public static boolean flag,在MyAccessibilityService的onAccessibilityEvent()中加一個(gè)flag判斷,然后調(diào)用智能安裝前flag設(shè)為true,創(chuàng)建apk安裝事件的廣播接收器,當(dāng)apk安裝完成后,設(shè)置falg為false,這樣其他apk就不能自動(dòng)安裝了,就解決了這個(gè)問題

下面上完整代碼.

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才能執(zhí)行智能安裝
 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);
 //注冊(cè)apk安裝監(jiān)聽
 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;
  //設(shè)置無障礙服務(wù)
  case R.id.btn_set:
  //跳轉(zhuǎn)到開啟無障礙服務(wù)的界面
  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權(quán)限
  process = Runtime.getRuntime().exec("su");
  os = new DataOutputStream(process.getOutputStream());
  os.write(cmd.getBytes());
  os.writeBytes("\n");
  os.writeBytes("exit\n");
  os.flush();
  //執(zhí)行命令
  process.waitFor();
  //獲取返回結(jié)果
  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();
  }
 }
 //顯示結(jié)果
 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "錯(cuò)誤消息: " + 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權(quán)限
  process = Runtime.getRuntime().exec("su");
  os = new DataOutputStream(process.getOutputStream());
  os.write(cmd.getBytes());
  os.writeBytes("\n");
  os.writeBytes("exit\n");
  os.flush();
  //執(zhí)行命令
  process.waitFor();
  //獲取返回結(jié)果
  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();
  }
 }
 //顯示結(jié)果
 tvTest.setText("成功消息:" + successMsg.toString() + "\n" + "錯(cuò)誤消息: " + 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);
 }
 //監(jiān)聽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);
  //安裝完畢,設(shè)置flag,從而使得其余的apk不能自動(dòng)安裝
  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);
 }
 }
}

界面上就三個(gè)按鈕

res/layout/activity_main.xml:



 

服務(wù)配置文件

res/xml/accessibility_service_config.xml

智能安裝服務(wù)

app/MyAccessibilityService.java:

public class MyAccessibilityService extends AccessibilityService {
 private static final String TAG = "[TAG]";
 private Map 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() {
 }
 //遍歷節(jié)點(diǎn),模擬點(diǎn)擊安裝按鈕
 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的時(shí)候模擬滑動(dòng)一下
  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;
 }
}

最后是配置文件AndroidManifest.xml:


 
  
  
  
 
 
  
  
  
 
 

注意:請(qǐng)把自己要安裝的apk放到sdcard上,并且修改代碼中的apk路徑和包名

9 運(yùn)行效果

Android 靜默安裝和智能安裝的實(shí)現(xiàn)方法 

10 總結(jié)

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


名稱欄目:Android靜默安裝和智能安裝的實(shí)現(xiàn)方法
本文網(wǎng)址:http://weahome.cn/article/psjsie.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部