Android應(yīng)用內(nèi)安裝App的幾種姿勢

調(diào)用系統(tǒng)安裝程序

這種方式最為簡單尝盼,只需要調(diào)起系統(tǒng)界面即可植旧∮睾铮看代碼


    /**
     * 調(diào)用系統(tǒng)安裝界面
     *
     * @param context
     * @param apkFile
     */
    public static void startInstallActivity(Context context, File apkFile) {
        if (apkFile == null || !apkFile.exists()) return;

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
        context.startActivity(intent);
    }
    

調(diào)用系統(tǒng)pm命令

這種方式是在通過終端執(zhí)行pm命令來實現(xiàn)

pm install -r [filepath]

其中-r代表覆蓋安裝慕淡。
這種方式可以實現(xiàn)靜默安裝,不需要調(diào)起系統(tǒng)界面沸毁。但是有一個前提峰髓,就是需要root權(quán)限,沒有去root權(quán)限息尺,pm命令不能執(zhí)行携兵。獲取root權(quán)限不是本文范疇,請自行百度或者google搂誉。

滿足前提的情況下徐紧,我們可能用到以下方法。

  • 判斷設(shè)備是否有root權(quán)限,通過執(zhí)行su命令是否正確來檢查設(shè)備是否被root
    /**
     * 檢查設(shè)備是否被root
     *
     * @return
     */
    public static boolean isRoot() {
        try {
            Process process = Runtime.getRuntime().exec("su");
            process.getOutputStream().write("exit\n".getBytes());
            process.getOutputStream().flush();
            int i = process.waitFor();
            if (0 == i) {
                process = Runtime.getRuntime().exec("su");
                return true;
            }

        } catch (Exception e) {
            return false;
        }
        return false;

    }
  • 執(zhí)行終端命令
/**
     * 執(zhí)行終端命令
     *
     * @param cmd
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public static int execRootCmdSilent(String cmd) throws IOException, InterruptedException {
        int result = -1;
        DataOutputStream dos = null;

        try {
            Process p = Runtime.getRuntime().exec("su");
            dos = new DataOutputStream(p.getOutputStream());
            Log.i(TAG, cmd);
            dos.writeBytes(cmd + "\n");
            dos.flush();
            dos.writeBytes("exit\n");
            dos.flush();
            p.waitFor();
            result = p.exitValue();
        } finally {
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
  • 安裝apk,在終端執(zhí)行 pm install -r filePath
 public static void installApk(String filePath) throws IOException, InterruptedException {
    if (TextUtils.isEmpty(filePath) || !new File(filePath).exists()) return;
    execRootCmdSilent("pm install -r " + filePath);
 }

反射PackageManagerinstallPackage方法

通過查看查看系統(tǒng)安裝程序的源碼并级,可以看到系統(tǒng)安裝程序?qū)嶋H上是調(diào)用PackageManagerinstallPackage方法來實現(xiàn)安裝的拂檩。

/**
     * @deprecated replaced by {@link PackageInstaller}
     * @hide
     */
    @Deprecated
    public abstract void installPackage(
            Uri packageURI, //APK地址
            IPackageInstallObserver observer, //安裝回調(diào)
            @InstallFlags int flags,
            String installerPackageName); //被安裝APK的包名
  • installPackage方法雖然是public修飾的,但是同時被@hide了嘲碧,所以應(yīng)用程序無法直接調(diào)用稻励,這就需要用到反射機(jī)制了。

  • 調(diào)用這個方法需要系統(tǒng)權(quán)限呀潭,所以需要用系統(tǒng)簽名對apk打包钉迷,具體操作可以參考這篇文章

  • IPackageInstallObserver是一個AIDL接口,所以我們需要用到IPackageInstallObserver.aidl 這個文件钠署。你可以

    • 直接從系統(tǒng)源碼拷貝,該文件位于/frameworks/base/core/java/android/content/pm

    • 或者拷貝我的荒椭,在你的項目下創(chuàng)建app/src/main/aidl/android/content/pm/IPackageInstallObserver.aidl文件谐鼎,然后將下面的代碼復(fù)制進(jìn)去


package android.content.pm;

/**
 * API for installation callbacks from the Package Manager.
 * @hide
 */
oneway interface IPackageInstallObserver {
    void packageInstalled(in String packageName, int returnCode);
}


當(dāng)上述都做好了之后。

  • 調(diào)用安裝的方法
    //installPackage方法名
    private static final String INSTALL_METHOD = "installPackage";

    //InstallFlags
    public static final int INSTALL_FORWARD_LOCK = 0x00000001;
    public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
    public static final int INSTALL_ALLOW_TEST = 0x0000000
    public static final int INSTALL_EXTERNAL = 0x00000008;
    public static final int INSTALL_INTERNAL = 0x00000010;
    public static final int INSTALL_FROM_ADB = 0x00000020;
    public static final int INSTALL_ALL_USERS = 0x00000040;
    public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
    public static final int INSTALL_GRANT_RUNTIME_PERMISSIONS = 0x00000100;
    public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200;
    public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
    public static final int INSTALL_EPHEMERAL = 0x00000800;
    public static final int INSTALL_DONT_KILL_APP = 0x00001000;
    public static final int INSTALL_FORCE_SDK = 0x00002000;
    public static final int DONT_KILL_APP = 0x00000001;


    public static void installPackage(Context context, File file, IPackageInstallObserver observer)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        PackageManager packageManager = context.getPackageManager();
        Method method = PackageManager.class.getDeclaredMethod(INSTALL_METHOD, Uri.class,
                IPackageInstallObserver.class, int.class, String.class);
        method.invoke(packageManager, Uri.fromFile(file), observer, INSTALL_REPLACE_EXISTING | INSTALL_DONT_KILL_APP, GetAppInfo.getAPKPackageName(context, file.getAbsolutePath()));
    }
    
  • 監(jiān)聽安裝回調(diào)
    
    //安裝回調(diào)
    private static class PackageInstallObserver extends IPackageInstallObserver.Stub {

        @Override
        public void packageInstalled(String packageName, int returnCode) throws RemoteException {
            if(returnCode == INSTALL_SUCCEEDED){
                //安裝成功
            }
        }
    }
    
    
    //returnCode的取值
    public static final int INSTALL_SUCCEEDED = 1;
    public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
    public static final int INSTALL_FAILED_INVALID_APK = -2;
    public static final int INSTALL_FAILED_INVALID_URI = -3;
    public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
    public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
    public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
    public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
    public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
    public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
    public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
    public static final int INSTALL_FAILED_DEXOPT = -11;
    public static final int INSTALL_FAILED_OLDER_SDK = -12;
    public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
    public static final int INSTALL_FAILED_NEWER_SDK = -14;
    public static final int INSTALL_FAILED_TEST_ONLY = -15;
    public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
    public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
    public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
    public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
    public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
    public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
    public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
    public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
    public static final int INSTALL_FAILED_UID_CHANGED = -24;
    public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
    public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
    public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
    public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
    public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
    public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
    public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
    public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
    public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
    public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
    public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
    public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
    public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
    public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
    public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
    public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -113;
    public static final int NO_NATIVE_LIBRARIES = -114;
    public static final int INSTALL_FAILED_ABORTED = -115;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末趣惠,一起剝皮案震驚了整個濱河市狸棍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌味悄,老刑警劉巖草戈,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侍瑟,居然都是意外死亡唐片,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門涨颜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來费韭,“玉大人,你說我怎么就攤上這事庭瑰⌒浅郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵弹灭,是天一觀的道長督暂。 經(jīng)常有香客問我,道長穷吮,這世上最難降的妖魔是什么逻翁? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮酒来,結(jié)果婚禮上卢未,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好辽社,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布伟墙。 她就那樣靜靜地躺著,像睡著了一般滴铅。 火紅的嫁衣襯著肌膚如雪戳葵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天汉匙,我揣著相機(jī)與錄音拱烁,去河邊找鬼。 笑死噩翠,一個胖子當(dāng)著我的面吹牛戏自,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伤锚,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼擅笔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了屯援?” 一聲冷哼從身側(cè)響起猛们,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狞洋,沒想到半個月后弯淘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡吉懊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年庐橙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惕它。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡怕午,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淹魄,到底是詐尸還是另有隱情郁惜,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布甲锡,位于F島的核電站兆蕉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缤沦。R本人自食惡果不足惜虎韵,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缸废。 院中可真熱鬧包蓝,春花似錦驶社、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至硅瞧,卻和暖如春份乒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腕唧。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工或辖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枣接。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓颂暇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親但惶。 傳聞我的和親對象是個殘疾皇子蟀架,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容