目前网上大多数关于应用程序中安装apk方法的文章,都会提到智能安装(需要跳转安装页面,但不需用户操作,利用(Google辅助功能),第二种是利用adb命令把安装包push到手机中实现静默安装。

但是本篇实现的方法有所不一样。一种是通过Intent实现(不需要root,跳转到安装界面,需要用户操作),一种是通过PackageInstaller.Session的方法实现静默安装(静默安装都是需要root或者系统签名,不需用户操作)。

一.通过Intent实现安装:

实现步骤:1.在res下创建一个xml文件夹,在xml创建一个资源文件file_paths.xml

                     根据安装包所在的位置,选择不同的”path“:

                     a.<files-path name="" path=“”> :表示Context.getFilesDir()目录或者其子目录

                                                                        目录路径:/data/data/“包名”/files/“name”

                     b.<cache-path name="" path=“”> :表示Context.getCacheDir()目录或者其子目录

                                                                          目录路径:/data/data/“包名”/cache/“name”

                     c.<external-path name="" path=“”> :表示Environment.getExternalStorageDirectory()目录或者其子目录

                                                                              目录路径:/storage/emulated/0/“name”

                     d.<external-files-path name="" path=“”> :表示Context.getExternalFilesDir(null)目录或者其子目录。

                                                                                      目录路径:/storage/emulated/0/Android/data/"包名"/files/“name”

                     e.<external-cache-path name="" path=“”> :表示Context.getExternalCacheDir()目录或者其子目录。

                                                                                    目录路径:/storage/emulated/0/Android/data/"包名"/cache/“name”

                    当中的path->为external-path的子目录路径,name-> 子目录路径的名称(Uri中代替path显示)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path
            name="intentInstall"
            path="" />
    </paths>
</resources>

                  2.在工程Main目录下的AndroidManifest文件下,添加一个FileProvider(用于返回安装包的Uri)

                    android:thorities="包名+provider"

                    android:name="android.support.v4.content.FileProvider"

                    android:exported="false"(处于应用安全考虑,假如工程为单模块,建议设为false,多模块可自行斟酌)

                    android:grantUriPermission="true"(用于权限确认)

                    添加数据源,resource指向刚刚常见的xml文件。

<provider
            android:authorities="com.example.lyybbc.provider"
            android:name="android.support.v4.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data android:name="file_paths"
                android:resource="@xml/file_paths"/>
        </provider>

              3.代码中完成操作:

                 a.前提:把1,2步骤完成

                   获取安装包的包名/路径,安装包的Uri要用FileProvider(content://)开头的

String apkName = "appInstall.apk";
        String apkFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + apkName;
        File apkFile = new File(apkFilePath);
        Uri uri = FileProvider.getUriForFile(getBaseContext(), "om.example.lyybbc.provider", apkFile);

                b.通过setDataAndType,把uri传入intent中,添加FLAG_GRANT_PERSISTABLE_URI_PERMISSION。

Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        startActivity(intent);

这样,点击安装按钮或者通过Rxjava响应该方法时,会跳转到安装页面,有用户进行安装确认操作。

 

二.通过PackageInstaller.Session进行静默安装(程序需要系统签名)

实现步骤:1.获取安装包的包名/路径信息,创建安装包的File对象,并获取file的字节大小

String apkName = "appInstall.apk";
        String apkFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + apkName;

        File apkFile = new File(apkFilePath);
        long apkFileLength = apkFile.length();

                            2.获取PackageManager用于创建Installer对象,并注册安装状态CallBack(监听系统静默安装状态)

PackageManager pm = getPackageManager();
        PackageInstaller packageInstaller = pm.getPackageInstaller();
        packageInstaller.registerSessionCallback(new PackageInstaller.SessionCallback() {
            @Override
            public void onCreated(int sessionId) {
                Log.e(TAG, "Install Start sessionId-> " + sessionId);
            }

            @Override
            public void onBadgingChanged(int sessionId) {}

            @Override
            public void onActiveChanged(int sessionId, boolean active) {}

            @Override
            public void onProgressChanged(int sessionId, float progress) {}

            @Override
            public void onFinished(int sessionId, boolean success) {
                if (success) {
                    Log.e(TAG, "Silent Install Success");
                } else {
                    Log.e(TAG, "Silent Install Fail");
                }
            }
        });

                         3.关于安装调用前的准备,创建sessionId,buffer,输入流inputSteam,输出流outputSteam等参数

int count;
        int sessionId;
        byte[] buffer = new byte[65536];

        InputStream inputStream;
        OutputStream outputStream;
        PackageInstaller.Session session = null;
        PackageInstaller.SessionParams sessionParams;

                      4.实现静默安装操作:

                         a.创建sessionParams对象(模式选择MODE_FULL_INSTALL)

                         b.利用sessionParams通过Installer生成sessionId(本次安装任务的ID)

                         c.installer根据sessionId创建session对象。

sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
sessionId =  packageInstaller.createSession(sessionParams);
session = packageInstaller.openSession(sessionId);

                        e.传入文件作为文件流,通过outpustream以字节的方式读取安装包数据,

inputStream = new FileInputStream(apkFile);
            outputStream = session.openWrite(apkName, 0, apkFileLength);

            while((count = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, count);
                float progress = ((float)count / (float)apkFileLength);
                session.setStagingProgress(progress);  //传入进度数据
            }
            session.fsync(outputStream);    //把outStream的数据都传入session中

            inputStream.close();
            outputStream.flush();
            outputStream.close();

                        f.生成一个PendingIntent实现广播接受并提交数据

Intent intent = new Intent();
PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, intent, 0);
session.commit(pendingIntent.getIntentSender());

                      g.结束安装后,弃用session

finally {
            if (session != null) {
                session.abandon();  //安装结束,弃用session
            }
        }

到此,静默安装完成。callback的onFinished会调用。可以根据返回的布尔型参数判断是否安装成功。假如要监听安装进度,可以通过session.setStagingProgress(progress)方式,向callback传入进度数据(在onProgressChanged获取)

 

以上两个方法经实践都成功。文章中有错误的地方,望朋友们可以指出,毕竟有批评才有进步。

假如觉得本文有用,那就留个言点个赞吧 ^ ^

完整程序Github地址:https://github.com/sparrowleung/packageInstall