uniapp静默更新
注意:这篇文章仅针对有Root权限的Android设备实现静默更新
先来看看uniapp端的静默更新效果:
什么鬼,需要我自己点算个什么静默更新....
于是就想到用Android原生实现静默更新再搞个插件什么的在uniapp上运行
一、准备开发环境
JAVA环境 jdk1.8
Android Studio
App离线SDK下载:请下载2.9.8 版本的android平台SDK
官网SDK解压后如下图
二、导入项目
导入uni插件原生项目 UniPlugin-Hello-AS工程请在App离线SDK中查找
点击Android Studio菜单选项File—>New—>Import Project。
三、创建插件Library
选择Android Library
输入 Library名称Update点击finish
然后在app目录下的build.gradle下引入 Update
将app libs目录下的 uniapp-v8-release.aar包(名字可能不一样)复制到 Update libs下
然后修改modle Update中的build.gradle文件 引入aar包 点击右上角同步即可
四、然后在modle目录下创建SilentInstallation类集成UniMoudle
package android.laboreryao.update;
import android.util.Log;
import android.widget.Toast;
import com.taobao.weex.bridge.JSCallback;
import java.io.File;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.common.UniModule;
/**
- 功能介绍:静默安装<br/>
- 调用方式: / <br/>
- <p/>
- 作 者: LaborerYao - 228598641@qq.com <br/>
- 创建电脑: <br/>
- 创建时间: 2021/6/25 18:01 <br/>
- 最后编辑: 2021/6/25 - yyl
-
@author LaborerYao
*/
public class SilentInstallation extends UniModule {
/**- 静默安装
- @param apkPath
- @param jsCallback result true 安装成功 false 失败
*/
@UniJSMethod(uiThread = true)
public void UpdateLotteryMachineApp(String apkPath, JSCallback jsCallback) {
Log.e("LaborerYao", "Update!!!apkPath==" apkPath);
if (!isRoot()) {
Toast.makeText(mWXSDKInstance.getContext(), "没有ROOT权限,不能使用静默安装", Toast.LENGTH_SHORT).show();
jsCallback.invoke(false);
}
SilentInstall installHelper = new SilentInstall();
final boolean result = installHelper.install(apkPath);
jsCallback.invoke(result);
}
/**
- 判断手机是否拥有Root权限。
- @return 有root权限返回true,否则返回false。
*/
public boolean isRoot() {
boolean bool = false;
try {
bool = new File("/system/bin/su").exists() || new File("/system/xbin/su").exists();
} catch (Exception e) {
e.printStackTrace();
}
return bool;
}
}
五、创建静默安装实现类,调用"pm install -r"命令进行静默安装
package android.laboreryao.update;
/**
- 功能介绍: <br/>
- 调用方式: / <br/>
- <p/>
- 作 者: LaborerYao - 228598641@qq.com <br/>
- 创建电脑: <br/>
- 创建时间: 2021/6/25 14:49 <br/>
- 最后编辑: 2021/6/25 - yyl
- @author LaborerYao
*/
import android.util.Log;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
public class SilentInstall {
/**
* 执行具体的静默安装逻辑,需要手机ROOT。
* @param apkPath
* 要安装的apk文件的路径
* @return 安装成功返回true,安装失败返回false。
*/
public boolean install(String apkPath) {
boolean result = false;
DataOutputStream dataOutputStream = null;
BufferedReader errorStream = null;
try {
// 申请su权限
Process process = Runtime.getRuntime().exec("su");
dataOutputStream = new DataOutputStream(process.getOutputStream());
// 执行pm install命令
String command = "pm install -r " apkPath "\n";
dataOutputStream.write(command.getBytes(Charset.forName("utf-8")));
dataOutputStream.flush();
dataOutputStream.writeBytes("exit\n");
dataOutputStream.flush();
process.waitFor();
errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String msg = "";
String line;
// 读取命令的执行结果
while ((line = errorStream.readLine()) != null) {
msg = line;
}
Log.d("TAG", "install msg is " msg);
// 如果执行结果中包含Failure字样就认为是安装失败,否则就认为安装成功
if (!msg.contains("Failure")) {
result = true;
}
} catch (Exception e) {
Log.e("TAG", e.getMessage(), e);
} finally {
try {
if (dataOutputStream != null) {
dataOutputStream.close();
}
if (errorStream != null) {
errorStream.close();
}
} catch (IOException e) {
Log.e("TAG", e.getMessage(), e);
}
}
return result;
}
}
六、创建BootBroadcastReceiver 广播用来监听apk安装完成自动打开
package android.laboreryao.update;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
- 功能介绍:自启动Receiver<br/>
- 调用方式: / <br/>
- <p/>
- 作 者: LaborerYao - 228598641@qq.com <br/>
- 创建电脑: MaiyuanAndroid2 <br/>
- 创建时间: 2021/6/25 18:01 <br/>
- 最后编辑: 2021/6/25 - yyl
-
@author LaborerYao
*/
public class BootBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "sjft";public static final String EXTRA_VOLUME_STATE =
"android.os.storage.extra.VOLUME_STATE";public static final int STATE_UNMOUNTED = 0;
public static final int STATE_CHECKING = 1;
public static final int STATE_MOUNTED = 2;
public static final int STATE_MOUNTED_READ_ONLY = 3;
public static final int STATE_FORMATTING = 4;
public static final int STATE_EJECTING = 5;
public static final int STATE_UNMOUNTABLE = 6;
public static final int STATE_REMOVED = 7;
public static final int STATE_BAD_REMOVAL = 8;@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if (action.equals("android.intent.action.PACKAGE_REPLACED")) {
String packageName = intent.getData().getSchemeSpecificPart();
Log.v(TAG, "BootBroadcastReceiver packageName:" packageName);
if (context.getPackageName().equals(packageName)) {
//Intent launchIntent = new Intent(context, MainActivity.class);//重新启动应用
//此处如果不想写死启动的Activity,也可以通过如下方法获取默认的启动Activity
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(launchIntent);
}
}
}
}
七、在AndroidManifest注册广播 并添加对应权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="android.laboreryao.update">
<uses-permission
android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application>
<receiver android:name=".BootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
</application>
</manifest>
八、打包插件arr包

九、将arr包放到uniapp项目 配置本地插件 打自定义基座 使用基座调试