测试xapk安装,优化xapkutils
This commit is contained in:
@@ -21,6 +21,7 @@ public class InstallActivity extends AppCompatActivity {
|
|||||||
setContentView(R.layout.activity_install);
|
setContentView(R.layout.activity_install);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
Log.e(TAG, "onNewIntent: ");
|
Log.e(TAG, "onNewIntent: ");
|
||||||
|
|||||||
@@ -560,14 +560,25 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void installXapk() {
|
private void installXapk() {
|
||||||
// File file = new File("/storage/emulated/0/Download/Google Chrome_135.0.7049.100_APKPure.xapk");
|
File file = new File("/storage/emulated/0/Download/X_10.89.1-release.0_APKPure.xapk");
|
||||||
File file = new File("/sdcard/Download/GoogleChrome_135.0.7049.100_APKPure.xapk");
|
// File file = new File("/sdcard/Download/X_10.89.1-release.0_APKPure.xapk");
|
||||||
|
if (!file.exists()) {
|
||||||
// XapkUtils.getInstance().installXapk(file.getAbsolutePath());
|
Toaster.show("文件不存在");
|
||||||
XapkInstaller xapkInstaller = XapkInstallerFactoryKt.createXapkInstaller(file.getAbsolutePath());
|
return;
|
||||||
if (xapkInstaller!=null) {
|
|
||||||
xapkInstaller.installXapk(MainActivity.this);
|
|
||||||
}
|
}
|
||||||
|
XapkUtils xapkUtils =new XapkUtils(MainActivity.this);
|
||||||
|
xapkUtils.setApkFilePath(file.getAbsolutePath());
|
||||||
|
xapkUtils.installXapk();
|
||||||
|
|
||||||
|
// TODO: 2025/4/21 google chrome 需要google框架
|
||||||
|
//Install failed! 7, INSTALL_FAILED_MISSING_SHARED_LIBRARY: Reconciliation failed...: Reconcile failed:
|
||||||
|
// Package com.android.chrome requires unavailable shared library com.google.android.trichromelibrary; failing!
|
||||||
|
|
||||||
|
//可以安装成功
|
||||||
|
// XapkInstaller xapkInstaller = XapkInstallerFactoryKt.createXapkInstaller(file.getAbsolutePath());
|
||||||
|
// if (xapkInstaller != null) {
|
||||||
|
// xapkInstaller.installXapk(MainActivity.this);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestPackageInstalls() {
|
private void requestPackageInstalls() {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.os.Looper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.example.mir4updater.BuildConfig;
|
import com.example.mir4updater.BuildConfig;
|
||||||
import com.example.mir4updater.utils.XapkUtils;
|
|
||||||
import com.hjq.toast.Toaster;
|
import com.hjq.toast.Toaster;
|
||||||
import com.tencent.mmkv.MMKV;
|
import com.tencent.mmkv.MMKV;
|
||||||
|
|
||||||
@@ -25,7 +24,6 @@ public class BaseApplication extends Application {
|
|||||||
// CrashReport.initCrashReport(getApplicationContext(), "8db7b63e60", false);
|
// CrashReport.initCrashReport(getApplicationContext(), "8db7b63e60", false);
|
||||||
xcrash.XCrash.init(this);
|
xcrash.XCrash.init(this);
|
||||||
Toaster.init(this);
|
Toaster.init(this);
|
||||||
XapkUtils.init(this);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package com.example.mir4updater.utils;
|
package com.example.mir4updater.utils;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentSender;
|
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
@@ -54,63 +52,59 @@ import io.reactivex.rxjava3.schedulers.Schedulers;
|
|||||||
public class XapkUtils {
|
public class XapkUtils {
|
||||||
private static final String TAG = "XapkUtils";
|
private static final String TAG = "XapkUtils";
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
private static XapkUtils sInstance;
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
private String mApkFilePath;
|
||||||
|
private File mApkFile;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
private String mUnpackPath;
|
||||||
|
@Deprecated
|
||||||
|
private long mXapkSize;
|
||||||
|
|
||||||
public static final String PACKAGE_INSTALLED_ACTION = "SESSION_API_PACKAGE_INSTALLED";
|
public static final String PACKAGE_INSTALLED_ACTION = "SESSION_API_PACKAGE_INSTALLED";
|
||||||
|
|
||||||
private XapkUtils(Context context) {
|
public XapkUtils(Context context) {
|
||||||
|
if (context == null) {
|
||||||
|
throw new RuntimeException("Context can not be null");
|
||||||
|
}
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 2025/4/18 优化为单例模式
|
|
||||||
public static void init(Context context) {
|
public void setApkFilePath(String apkFilePath) {
|
||||||
if (sInstance == null) {
|
this.mApkFilePath = apkFilePath;
|
||||||
sInstance = new XapkUtils(context);
|
this.mApkFile = new File(mApkFilePath);
|
||||||
|
if (mApkFile.exists() && mApkFile.isFile()) {
|
||||||
|
Log.e(TAG, "setApkFilePath: " + "File exists and isFile");
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "setApkFilePath: " + "File not exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static XapkUtils getInstance() {
|
public void installXapk() {
|
||||||
if (sInstance == null) {
|
if (mApkFile.exists() && mApkFile.isFile()) {
|
||||||
throw new IllegalStateException("You must be init XapkUtils first");
|
upzipXapk();
|
||||||
}
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
public void installXapk(String filePath) {
|
|
||||||
File file = new File(filePath);
|
|
||||||
if (file.exists() && file.isFile()) {
|
|
||||||
upzipXapk(file);
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "installXAPK: " + "File not exists");
|
Log.e(TAG, "installXAPK: " + "File not exists");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
private void upzipXapk() {
|
||||||
private static String unpackPath;
|
|
||||||
@Deprecated
|
|
||||||
private static long XapkSize;
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
private void upzipXapk(File XapkFile) {
|
|
||||||
Observable.create(new ObservableOnSubscribe<String>() {
|
Observable.create(new ObservableOnSubscribe<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
|
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
|
||||||
if (ZipUtil.containsEntry(XapkFile, "manifest.json")) {
|
if (ZipUtil.containsEntry(mApkFile, "manifest.json")) {
|
||||||
//判断json文件是否存在
|
//判断json文件是否存在
|
||||||
String dirName = FileUtils.getFileNoExName(XapkFile.getAbsolutePath());
|
String dirName = FileUtils.getFileNoExName(mApkFile.getAbsolutePath());
|
||||||
unpackPath = XapkFile.getParentFile().getAbsolutePath() + File.separator + dirName;
|
mUnpackPath = mContext.getExternalCacheDir()+ File.separator + "xapk_unzip" + File.separator + dirName;
|
||||||
File unpackDir = new File(unpackPath);
|
File unpackDir = new File(mUnpackPath);
|
||||||
ZipUtil.unpack(XapkFile, unpackDir);
|
ZipUtil.unpack(mApkFile, unpackDir);
|
||||||
String jsonString = JsonUtils.readJsonFile(unpackDir.getAbsolutePath() + File.separator + "manifest.json");
|
String jsonString = JsonUtils.readJsonFile(unpackDir.getAbsolutePath() + File.separator + "manifest.json");
|
||||||
emitter.onNext(jsonString);
|
emitter.onNext(jsonString);
|
||||||
} else {
|
} else {
|
||||||
Toaster.show("读取XAPK配置文件失败");
|
Toaster.show("读取XAPK配置文件失败");
|
||||||
emitter.onComplete();
|
|
||||||
}
|
}
|
||||||
|
emitter.onComplete();
|
||||||
}
|
}
|
||||||
}).subscribeOn(Schedulers.io())
|
}).subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
@@ -123,7 +117,7 @@ public class XapkUtils {
|
|||||||
@Override
|
@Override
|
||||||
public void onNext(String s) {
|
public void onNext(String s) {
|
||||||
JsonObject jsonObject = JsonParser.parseString(s).getAsJsonObject();
|
JsonObject jsonObject = JsonParser.parseString(s).getAsJsonObject();
|
||||||
XapkSize = jsonObject.get("total_size").getAsLong();
|
mXapkSize = jsonObject.get("total_size").getAsLong();
|
||||||
JsonElement split_configs_em = jsonObject.get("split_configs");
|
JsonElement split_configs_em = jsonObject.get("split_configs");
|
||||||
//获取分割的配置
|
//获取分割的配置
|
||||||
JsonElement expansions_em = jsonObject.get("expansions");
|
JsonElement expansions_em = jsonObject.get("expansions");
|
||||||
@@ -145,28 +139,28 @@ public class XapkUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
private void readConfig(JsonElement splitConfigs, JsonElement expansions, JsonElement splitApks) {
|
||||||
private void readConfig(JsonElement split_configs, JsonElement expansions, JsonElement split_apks) {
|
|
||||||
//没有split_configs只有单个apk,读取split_apks 的base字段的file文件
|
//没有split_configs只有单个apk,读取split_apks 的base字段的file文件
|
||||||
//没有expansions 没有obb文件
|
//没有expansions 没有obb文件
|
||||||
//split_apks应该是都会有的,包含一个base的json对象
|
//split_apks应该是都会有的,包含一个base的json对象
|
||||||
if (null != split_configs) {
|
if (null != splitConfigs) {
|
||||||
getSplitConfigs(split_configs.getAsJsonArray());
|
getSplitConfigs(splitConfigs.getAsJsonArray());
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "readConfig: " + "not found split_configs json data");
|
Log.e(TAG, "readConfig: " + "not found split_configs json data");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null != expansions) {
|
if (null != expansions) {
|
||||||
readOBBConfig(expansions.getAsJsonArray());
|
readObbConfig(expansions.getAsJsonArray());
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "readConfig: " + "not found expansions json data");
|
Log.e(TAG, "readConfig: " + "not found expansions json data");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null != split_apks) {
|
if (null != splitApks) {
|
||||||
getSplitApks(split_apks.getAsJsonArray());
|
getSplitApks(splitApks.getAsJsonArray());
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "readConfig: " + "not found split_apks json data");
|
Log.e(TAG, "readConfig: " + "not found split_apks json data");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> configList = new ArrayList<>();
|
List<String> configList = new ArrayList<>();
|
||||||
@@ -196,9 +190,8 @@ public class XapkUtils {
|
|||||||
*
|
*
|
||||||
* @param jsonArray
|
* @param jsonArray
|
||||||
*/
|
*/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
private void readObbConfig(JsonArray jsonArray) {
|
||||||
private void readOBBConfig(JsonArray jsonArray) {
|
if (TextUtils.isEmpty(mUnpackPath)) {
|
||||||
if (TextUtils.isEmpty(unpackPath)) {
|
|
||||||
Log.e(TAG, "readOBBConfig: " + "unpack directory is empty");
|
Log.e(TAG, "readOBBConfig: " + "unpack directory is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -245,12 +238,12 @@ public class XapkUtils {
|
|||||||
//应该直接获取SplitApks里面的file
|
//应该直接获取SplitApks里面的file
|
||||||
if (null != configList && configList.size() > 0) {
|
if (null != configList && configList.size() > 0) {
|
||||||
//split_configs不为空的情况
|
//split_configs不为空的情况
|
||||||
filePath.add(unpackPath + File.separator + apkList.get(getFileFromId(apkList, "base")).getFile());
|
filePath.add(mUnpackPath + File.separator + apkList.get(getFileFromId(apkList, "base")).getFile());
|
||||||
for (String config : configList) {
|
for (String config : configList) {
|
||||||
int position = getFileFromId(apkList, config);
|
int position = getFileFromId(apkList, config);
|
||||||
if (position != -1) {
|
if (position != -1) {
|
||||||
String file = apkList.get(position).getFile();
|
String file = apkList.get(position).getFile();
|
||||||
filePath.add(unpackPath + File.separator + file);
|
filePath.add(mUnpackPath + File.separator + file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Log.e(TAG, "installxApk: " + filePath.toString());
|
// Log.e(TAG, "installxApk: " + filePath.toString());
|
||||||
@@ -294,14 +287,13 @@ public class XapkUtils {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public void installAppatPie(List<String> apkFilePath) {
|
public void installAppatPie(List<String> apkFilePath) {
|
||||||
Log.e(TAG, "installApps: 正在安装应用 " + apkFilePath);
|
Log.e(TAG, "installApps: 正在安装应用 " + apkFilePath);
|
||||||
// File file = new File(apkFilePath);
|
// File file = new File(apkFilePath);
|
||||||
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
|
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
|
||||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
|
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
|
||||||
.SessionParams.MODE_FULL_INSTALL);
|
.SessionParams.MODE_FULL_INSTALL);
|
||||||
sessionParams.setSize(XapkSize);
|
sessionParams.setSize(mXapkSize);
|
||||||
int sessionId = createSession(packageInstaller, sessionParams);
|
int sessionId = createSession(packageInstaller, sessionParams);
|
||||||
if (sessionId != -1) {
|
if (sessionId != -1) {
|
||||||
for (String apkPath : apkFilePath) {
|
for (String apkPath : apkFilePath) {
|
||||||
@@ -339,8 +331,7 @@ public class XapkUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
private int createSession(PackageInstaller packageInstaller, PackageInstaller.SessionParams sessionParams) {
|
||||||
private static int createSession(PackageInstaller packageInstaller, PackageInstaller.SessionParams sessionParams) {
|
|
||||||
int sessionId = -1;
|
int sessionId = -1;
|
||||||
try {
|
try {
|
||||||
sessionId = packageInstaller.createSession(sessionParams);
|
sessionId = packageInstaller.createSession(sessionParams);
|
||||||
@@ -351,8 +342,7 @@ public class XapkUtils {
|
|||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
private boolean copyApkFile(PackageInstaller pi, int sessionId, String apkFilePath) {
|
||||||
private static boolean copyApkFile(PackageInstaller pi, int sessionId, String apkFilePath) {
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
File apkFile = new File(apkFilePath);
|
File apkFile = new File(apkFilePath);
|
||||||
PackageInstaller.Session session = null;
|
PackageInstaller.Session session = null;
|
||||||
@@ -385,7 +375,6 @@ public class XapkUtils {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
private boolean copyObbFile(Expansions expansions) {
|
private boolean copyObbFile(Expansions expansions) {
|
||||||
String install_location = expansions.getInstall_location();
|
String install_location = expansions.getInstall_location();
|
||||||
//install_location 不清楚是否还有其他定义
|
//install_location 不清楚是否还有其他定义
|
||||||
@@ -397,7 +386,7 @@ public class XapkUtils {
|
|||||||
Log.e(TAG, "copyObbFile: " + "file path is empty");
|
Log.e(TAG, "copyObbFile: " + "file path is empty");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
File localFile = new File(unpackPath + File.separator + file);
|
File localFile = new File(mUnpackPath + File.separator + file);
|
||||||
if (localFile.exists() && localFile.isFile()) {
|
if (localFile.exists() && localFile.isFile()) {
|
||||||
File installFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + install_path);
|
File installFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + install_path);
|
||||||
Log.e(TAG, "copyObbFile: " + "localFile: " + localFile.getAbsolutePath());
|
Log.e(TAG, "copyObbFile: " + "localFile: " + localFile.getAbsolutePath());
|
||||||
|
|||||||
Reference in New Issue
Block a user