在Android 6.0到Android 9.0操作系统中,谷歌已经提供了原生API方法来进行应用的静默安装,称为“Package Installer Session”。但是需要注意的是,这方法需要设备在所有者的设备上儿且有系统权限,例如:系统应用。因此,除非你的App有这样的权限,否则不能被直接在用户的设备上用这种方法进行静默安装。
前提:不管是方案一和方案二
1,需要在清单文件添加 入权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission
android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="permission.REQUEST_DELETE_PACKAGES" />
<uses-permission
android:name="android.permission.DELETE_PACKAGES"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="androidpermissionBIND DEVICEADMIN" />
2,在清单文件application标签下添加如下
coreApp="true"
android:sharedUserId="android.uid.system"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
方案一
以下是一种典型的使用方法:
class InstallUtil {
private Context mContext;
private String mApkFilePath;
private static String TAG = "InstallUtil";
private static int UN_INSTALL_SUCCESS = 1;
public InstallUtil(Context context, String apkFilePath) {
this.mContext = context;
this.mApkFilePath = apkFilePath;
}
@RequiresApi(api = 24)
public void install24() {
boolean isGranted = isGranted();
if (isGranted) {
install();
} else {
Toast.makeText(mContext,"请开启本应用的安装权限",Toast.LENGTH_SHORT).show();
}
}
public boolean isGranted() {
return !TextUtils.isEmpty(mApkFilePath);
}
@RequiresApi(api = 26)
private void install() {
File file = new File(mApkFilePath);
try {
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setSize(file.length());
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
InputStream in = new FileInputStream(file);
OutputStream out = session.openWrite("CocosDemo", 0, file.length());
byte[] buffer = new byte[65536];
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
session.fsync(out);
in.close();
out.close();
session.commit(PendingIntent.getBroadcast(mContext, sessionId,
new Intent("android.intent.action.PACKAGE_ADDED"), 0).getIntentSender());
} catch (Exception e) {
Log.e(TAG,"安装失败:"+e.getMessage());
e.printStackTrace();
}
}
}
在方法install24里检测了是否有安装权限,如果有就进行安装。在我们安装之前都是要检测一下你的App是否获得了安装权限,如果没有的话就需要用户手动去开启。
这是一个比较简单的实现,并没有兼容所有的Android设备和版本。实际上,Android设备的厂商和版本非常多,可能每个都有自己不同的实现方式和限制,因此这个静默安装的方法可能不是所有设备都能使用
方案二
下面是一个Android静默安装的示例方法,它在Android 6.0到9.0的版本下都可以运行,并可以返回一个标示安装状态的boolean值。注意,此方法需要应用具有系统级别的权限。
需要在 AndroidManifest.xml 文件中添加安装应用的权限:
Java方法部分:
import android.content.Context;
import android.content.pm.PackageInstaller;
import android.os.Build;
public class InstallerUtil {
public static boolean installPackage(Context context, String apkPath) {
if(context == null || apkPath == null){
return false;
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(packageName);
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
OutputStream outputStream = session.openWrite(apkPath, 0, -1);
File file = new File(apkPath);
InputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[65536];
int c;
while ((c = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, c);
}
session.fsync(outputStream);
inputStream.close();
outputStream.close();
session.commit(Intents.getPendingIntent(context, sessionId, createInstallIntent(), 0).getIntentSender());
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
这段代码的核心思想是创建一个Package Installer的session来进行安装。代码依次执行的是:创建SessionParams,然后创建PackageInstaller.Session, 并将apk文件写入到Session。在写入完成后,这个Session会commit,然后Package Installer会处理这个Session来完成安装过程。
注意,android从8.0开始增加了未知安装的权限检查,所以,如果你的应用要在8.0及以后的版本运行,你需要在安装APK前检查并请求安装未知应用源的权限。
但是,这个方法需要你的应用拥有系统级别的权限,也就是说,你的应用必须被签名并安装到/system/priv-app目录下才能正常工作,不然会出现安全异常。
战线,对于6.0到9.0版本这个范围内的静默安装,是非常有挑战性的,因为涉及到系统级别的API调用与权限,一个完全免root,兼容性好,可以静默安装的方法是相当困难的。对于普通应用开发者来说,要实现真正的静默安装,基本上是无解的。