1. 程式人生 > >Android 如何完整的獲取到使用者已安裝應用列表

Android 如何完整的獲取到使用者已安裝應用列表

    接到產品經理的預研需求,說希望獲取使用者已安裝應用列表。這個問題應該不難,只要是要把相關的知識點整理和驗證一下。

    對於獲取使用者已安裝應用列表,我個人是很熟悉的,因為我的華為手機上,手機管家天天會在通知欄彈出”xxx應用嘗試獲取使用者已安裝應用列表被禁止”。所以,很明顯,跟許可權是有關係的。於是,我嘗試去查詢到底是manifest清單中的哪一個use-permission引起。結果,找了很久,翻了很久,並沒有哪個許可權對已安裝的應用列表負責。

    但奇怪的是,我的手機上幾乎全部的軟體都聲明瞭這個許可權。於是,嘗試去求助其他組員,諮詢了幾個,不少人一臉懵逼的表示這是個什麼玩意。在他們的手機上壓根就沒有見過這個東西。

    在寫demo驗證的過程中,發現非常簡單的一個demo,居然也宣告使用了該許可權。 一開始懷疑,難道是檢測到了相關程式碼自動申請了許可權?發現全部註釋後還是會宣告。 後來,將清單檔案中的唯一的訪問Internet許可權去掉,這樣才正常。

    所以,得出了一個結論就是,國內部分廠商比如華為、oppo,他們將”獲取使用者已安裝應用列表”的許可權暴露給了使用者,讓使用者可以自由決定允許或者禁止應用訪問該資訊。同時,這個許可權類似於附加的預設許可權,一旦app聲明瞭任何許可權,那麼”獲取使用者已安裝應用列表”的許可權也會被附加進來。但這個許可權也不是太敏感,所以對於使用者是無感知的。這裡的無感知指的是不會在應用中去主動讓我們彈窗申請許可權,手機管家彈出的通知不算。

好吧,說了這麼多,看一下過程中的3種方案。

方案1

private void getAppList() {
    PackageManager pm = getPackageManager();
    // Return a List of all packages that are installed on the device.
    List<PackageInfo> packages = pm.getInstalledPackages(0);
    for (PackageInfo packageInfo : packages) {
        // 判斷系統/非系統應用
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) // 非系統應用 { System.out.println("MainActivity.getAppList, packageInfo=" + packageInfo.packageName); } else { // 系統應用 } } }

此方法在華為、oppo手機上,把許可權禁止後,就不能正確獲取到已安裝應用列表了。

方案2

    考慮到方案1受許可權的影響,於是考慮用adb命令去獲取已安裝的應用列表。

命令:adb shell pm list package -3

    上面的命令可以獲取到手機上已安裝的第3方應用列表,去掉-3這個引數可以獲取到全部的應用列表。本來對這個方案抱挺大的期望的,但是最終發現在oppo手機上,如果禁止了獲取已安裝應用列表的許可權,那麼結果就會受到影響,無奈又不行。

小插曲:在程式碼呼叫命令列過程中遇到個坑,

private void runCommand() {
    try {
        Process process = Runtime.getRuntime().exec("adb shell pm list package -3");
        BufferedReader bis = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = null;
        while ((line = bis.readLine()) != null) {
            System.out.println("MainActivity.runCommand, line=" + line);
        }
    } catch (IOException e) {
        System.out.println("MainActivity.runCommand,e=" + e);
    }
}

    用程式碼去執行了 adb shell pm list package -3的命令,發現一直報IOException,最終耗費一定的時間,定位到問題。我們使用adb shell是因為手機跟pc要連線,但是在手機上執行時,其實不用加adb shell,直接執行”pm list package -3”即可。

方案3

    採用getPackageManager().queryIntentActivities(intent,PackageManager.MATCH_ALL)去查詢是否有符合指定意圖的Activity,從而判斷是否安裝了某應用。

該方法返回的是ResolveInfo列表,而ResolveInfo包含的是IntentFilter資訊。

以下結論都經過demo的驗證:

  • 清單檔案的宣告中必須包含IntentFilter資訊,queryIntentActivities方法才能查詢到。
  • IntentFilter中不能包含data資訊,如果有定義,則查詢不到資訊了,連啟動的Activity都找不到。這裡暫時沒有去檢視The Fucking Source Code。

  • 新增 android:exported與否對於此方法的結果沒有影響

  • 在華為等對應用安裝列表有許可權控制的手機上,採用隱式的Intent獲取不到正確的資訊,就連每個應用的啟動Activity都獲取不到。

  • 顯示的Intent則不受許可權的影響,均可以獲取到。

結論

    採用第三種方案,用顯示的Intent(一般指定包名或者類名)去查詢是否安裝了某應用在各個廠商各個系統的手機上是可行的,但是隻能獲取到指定的,而不是全部。