Android静默安装研究总结

背景

公司的产品越来越贴近行业定制化,大多数设备需要远程自动更新app,无需人工介入,所以静默安装的功能必须支持,由于拥有整个系统方案,所以顺利拿到系统签名,所以本文总结下,在拥有系统权限条件下,应用程序实现5.1到9.0的静默安装方案

前提

1,获取系统权限
2,API21以上

方案1:

创建子进程,运行 pm 指令:
此方法在5.1版本上可行,7.0需要修正下指令才能成功。9.0此方法失效。

5.0 的指令 pm install -r /sdcard/demo.apk
7.0 的指令 pm install -i com.demo.insatll /sdcard/demo.apk

    public static boolean installApk(String apkPath){

        //String [ ] args = { "pm" , "install" , "-i" , "com.example", apkPath } ;//7.0用这个,参考的博客说要加 --user,但是我发现使用了反而不成功。
        String [ ] args = { "pm" , "install" , "-r" , apkPath } ;
        ProcessBuilder processBuilder = new ProcessBuilder (args) ;

        Process process = null ;
        BufferedReader successResult = null ;
        BufferedReader errorResult = null ;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder errorMsg = new StringBuilder();


        try {

            process = processBuilder.start();
            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) ;
            }

            return  process.waitFor() == 0 || successMsg.toString().contains("Success");

        }catch (IOException e){

            e.printStackTrace();

        }catch (InterruptedException e){

            e.printStackTrace();

        }finally {

            try {
                if ( successResult != null ) {
                    successResult.close() ;
                }
                if ( errorResult != null ) {
                    errorResult.close() ;
                }
            } catch ( IOException e ) {
                e . printStackTrace() ;
            }
            if ( process != null ) {
                process. destroy() ;
            }
        }

        return  false;

    }



方案2:

获取PackageManager,反射installPackage方法。

此方法也有的特点如下:

1,需要版本5.1以上,5.1下insatllPackage方法需要修改入参,本章针对5.1之前的设备采用方案1。
2,installPackage方法7.0已经标识为不推荐调用,9.0彻底找不到了。
3,此方法为异步,结果在PackageInstallObserver的回调方法返回,如需要监听真正是否安装成功,需要在回调方法内判断结果。

    public static boolean installAPK(Context context,String apkPath){


        try {

            PackageManager packageManager = context.getPackageManager();
            Class<?> pmClz = packageManager.getClass();
            Class<?> aClass = Class.forName("android.app.PackageInstallObserver");
            Constructor<?> constructor = aClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            Object installObserver = constructor.newInstance();
            Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, aClass, int.class, String.class);
            method.setAccessible(true);
            method.invoke(packageManager, Uri.fromFile(new File(apkPath)), installObserver, 2, null);

            return true;

        }catch (Exception e){
            e.printStackTrace();
        }


        return false;


    }



方案3:

调用PackageInstaller的API。

1,此方法在5.1,7.1,8.0,9.0系统上均验证通过。
2,此方法为同步方法,会阻塞。

参考博客入口,感谢这位作者。

 public static boolean installAPK(Context context, String apkPath){

        File file=new File(apkPath);
        String apkName=apkPath.substring(apkPath.lastIndexOf(File.separator)+1,apkPath.lastIndexOf(".apk"));
        PackageManager packageManager = context.getPackageManager();
        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
        PackageInstaller.SessionParams params=new PackageInstaller
                .SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        PackageInstaller.Session session=null;
        OutputStream outputStream=null;
        FileInputStream inputStream=null;

        try {

            //创建Session
            int sessionId = packageInstaller.createSession(params);
            //开启Session
            session=packageInstaller.openSession(sessionId);
            //获取输出流,用于将apk写入session
            outputStream = session.openWrite(apkName, 0, -1);
            inputStream=new FileInputStream(file);
            byte[] buffer=new byte[4096];
            int n;
            //读取apk文件写入session
            while ((n=inputStream.read(buffer))>0){
                outputStream.write(buffer,0,n);
            }

            //写完需要关闭流,否则会抛异常“files still open”
            inputStream.close();
            inputStream=null;
            outputStream.flush();
            outputStream.close();
            outputStream = null;

            //配置安装完成后发起的intent,通常是打开activity(这里我做了修改,修改为广播,intent并未设置目标参数,后面有需求在这里修改补充)
            Intent intent=new Intent();
            PendingIntent pendingIntent= PendingIntent.getBroadcast(context,0,intent,0);
            IntentSender intentSender = pendingIntent.getIntentSender();
            //提交启动安装
            session.commit(intentSender);
            return true;

        }catch (Exception e){
            e.printStackTrace();
            if(session!=null){
                session.abandon();
            }
        }finally {
            if(outputStream!=null){
                try {
                    outputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(inputStream!=null){
                try {
                    inputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }


        return false;


    }



参考博客
再次感谢这些作者对我的帮助。

扫描二维码关注公众号,回复: 14215904 查看本文章

Mr.Jonas Android静默安装实现

duanzhaozhao jing_install

空白的泡 7.0 apk 安装 (pm 命令)

兰心之舞动 Android7.0的静默安装失败问题研究

落后程序员 Android 9.0静默安装整理
————————————————

转载于:https://blog.csdn.net/lucky_tom/article/details/109090643

猜你喜欢

转载自blog.csdn.net/weixin_42602900/article/details/125067295