android 静默安装apk,适配android Q静默

前提条件
在android 系统下静默安装apk需要时系统级应用 或 加了白名单具备系统权限App;

解决方案

一: 在低版本SDK中应该是SDK4.0(API15)及以下,具体没考证,可以通过PackageManager类的installPackage方法来静默安装;

//context 上下文;packageName包名; file 文件
private void installSilent(Context context, String packageName, File file) {
        PackageManager pm = context.getPackageManager();
        pm.installPackage(Uri.fromFile(file), new IPackageInstallObserver() {
            @Override
            public void packageInstalled(String basePackageName, int returnCode) throws RemoteException {
              
			  if(basePackageName==null||returnCode!=1){
				//静默安装失败,采用通过Intent跳转手动安装
			  }else{
				//静默安装成功
			  }
			  
            }

            @Override
            public IBinder asBinder() {
                return null;
            }
        }, PackageManager.INSTALL_REPLACE_EXISTING, packageName);
}

二:当在SDK版本为O 即8.0(API27)及以下,SDK4.0(API15)以上,PackageManager包下就找不到installPackage()方法。

分析原因为在SDK8.0等版本下,google考虑安全等原因,移除或私有了PackageManager中一些函数、删除了 IPackageInstallObserver.class,IPackageInstallObserver$Stub.class;PackageManager中移除了个别方法

  1. import android.content.pm.IPackageInstallObserver;  
  2. import android.content.pm.IPackageInstallObserver$Stub;  
  3. import android.content.pm.PackageManager;  

解决方案:

1、在你的SDK路径下找到 \platforms\android-API版本号\android.jar,修改后缀为.zip;然后解压android.zip; 注意:解压一个低版本PackageManager等未做删减的,再解压一个你项目所使用的SDK版本的android.jar;

2、在android.zip文件夹中进入 android\android\content\pm ;然后将低版本PackageManager等未做删减的 PackageManager.classIPackageInstallObserver.class 、IPackageInstallObserver$Stub.class;拷贝一份然后粘贴到你项目所使用的SDK版本的android.jar中,然后再将android文件压缩成android.zip,然后再修改后缀为.jar;

3、再将修改后的android.jar拷贝到SDK路径  \platforms\android-API版本号\ 下,覆盖掉被google删减的android.jar; 注意覆盖前最好先备份一下原始android.jar;

4、重启android studio;类和方法就能导报出来了; 

 

如下图:

三:当在SDK版本为P 即9.0(API28)及以上, 上面的解决方法不再适用。这时需要使用 PackageInstaller类 创建Session,并广播通知系统来静默安装;

直接贴代码如下:注意 该操作是耗时的,切勿在主线程中操作,否则会ANR;一定通过子线程来操作;



public class InstallManager {

    private static final String TAG = "InstallManager";

    /*******************************************************  android P 静默安装适配 *****************************************************************/
    private final static String ACTION = "wait.install.result";
    private final static SynchronousQueue<Intent> mInstallResults = new SynchronousQueue<>();

    /**
     * @param context
     * @param filePath
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static synchronized boolean installForP(Context context, final String filePath) {

        boolean success = false;
        File apkFile = new File(filePath);
        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        sessionParams.setSize(apkFile.length());
        int sessionId = createSession(packageInstaller, sessionParams);

        if (sessionId != -1) {
            boolean copySuccess = copyInstallFile(packageInstaller, sessionId, filePath);

            if (copySuccess) {
                boolean installSuccess = execInstallCommand(context, packageInstaller, sessionId, filePath);
                if (installSuccess) {
                    success = true;
                }
            }
        }
        return success;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static int createSession(PackageInstaller packageInstaller, PackageInstaller.SessionParams sessionParams) {
        int sessionId = -1;
        try {
            sessionId = packageInstaller.createSession(sessionParams);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sessionId;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static boolean copyInstallFile(PackageInstaller packageInstaller, int sessionId, String apkFilePath) {

        InputStream in = null;
        OutputStream out = null;
        PackageInstaller.Session session = null;
        boolean success = false;
        try {
            File apkFile = new File(apkFilePath);
            session = packageInstaller.openSession(sessionId);
            out = session.openWrite("base.apk", 0, apkFile.length());
            in = new FileInputStream(apkFile);
            int total = 0, c;
            byte[] buffer = new byte[65536];
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
            }
            session.fsync(out);
            Log.i(TAG, "streamed " + total + " bytes");
            success = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            session.close();
        }
        return success;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static boolean execInstallCommand(Context context, PackageInstaller packageInstaller, int sessionId, String filePath) {
        PackageInstaller.Session session = null;
        boolean success = false;
        try {
            session = packageInstaller.openSession(sessionId);
            Intent intent = new Intent();
            intent.setAction(ACTION);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            session.commit(pendingIntent.getIntentSender());
            
            final Intent result = mInstallResults.poll(60, TimeUnit.SECONDS);

            if (result != null) {
                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                        PackageInstaller.STATUS_FAILURE);

                if (status == PackageInstaller.STATUS_SUCCESS) {
                    success = true;
                } else {
                    Log.e(TAG, result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
                }
            } else {
                success = false;
            }

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

        } finally {
            session.close();
        }

        return success;
    }


}

 

注:公司是做手机及OS的, 以上方案均在我负责的项目中验证过(负一屏、浏览器、应用市场),是OK的;

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 24
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值