uni-app实现热更新、全量更新带状态栏进度提示

-----------------------------------uni-app--------------------------------------

checkAppVersion

// 版本更新
export function checkAppVersion(_t, isShowWarn = false) {
	// #ifdef APP-PLUS
	// 若是正在更新不执行
	if(_t.isShowModal || _t.isUpdate) return;
	const os_type = plus.os.name.toLowerCase();
	const app_version = plus.runtime.version;
	plus.runtime.getProperty(plus.runtime.appid, function(info) {
		uni.setStorageSync('VERSION', info.version);
		const res_version = info.version;
		const requestData = {
			osType: os_type,
			appVersion: app_version,
			resVersion: res_version
		}
		_t.$api.getAppVersion(requestData).then(res => {
			Log.d("版本信息:" + JSON.stringify(res));
			if (res.statusCode != 200 || !res.data.success) return;
			const data = res.data.result;
			if (data.isUpdate === 0) return;
			const app_version_db = data.appVersion;
			const res_version_db = data.resVersion;
			const app_url = data.appUrl;
			const res_url = data.resUrl;
			const app_version_desc = data.content;
			//大版本更新(涉及安卓壳子或壳子改动)
			if (app_version_db !== app_version && (parseInt(app_version.replace(/\./g, "")) <
					parseInt(app_version_db.replace(/\./g, "")))) {
				// 定义安卓apk存放路径
				const filename = "_downloads/park.apk";
				// 处理安装提示在本应用上方弹出导致下载弹窗重复弹出问题
				nativeJs("bridge", "getFileApkVersion", {
					path: plus.io.convertLocalFileSystemURL(filename)
				}, originAppVersion => {
					Log.d("原来存在的apk包版本:" + originAppVersion)
					if(parseInt(originAppVersion.replace(/\./g, "")) === parseInt(app_version_db.replace(/\./g, ""))) {
						// 如果原来存在的apk包版本比app版本高直接提示安装
						if(parseInt(originAppVersion.replace(/\./g, "")) > parseInt(app_version.replace(/\./g, ""))) {
							// 决定是否弹出安装,解决部分机型在应用界面弹出安装提示而重复提示问题
							nativeJs("bridge", "getFileCreatedTime", {
								path: plus.io.convertLocalFileSystemURL(filename)
							}, createdTime => {
								if(createdTime) {
									Log.d("现在时间" + new Date().getTime())
									Log.d("文件创建时间" + parseInt(createdTime))
									Log.d("现在时间与文件创建时间差" + (new Date().getTime() - parseInt(createdTime)))
									// 超过1分钟提示安装
									if(new Date().getTime() - parseInt(createdTime) > 60 * 1 * 1000) {
										_t.isShowModal = true;
										uni.showModal({ //提醒用户更新
											title: "版本更新",
											content: "是否立即更新",
											showCancel: false,
											complete: () => {
												_t.isShowModal = false;
											},
											success: res => {
												if(res.confirm) {
													plus.runtime.install(plus.io.convertLocalFileSystemURL(filename), //安装APP
													{
														force: true
													},
													function() {
														Log.d('安装旧包准备成功');
													},
													function() {
														Log.d('安装旧包准备失败');
													});
												}
											}
										})
									}
								}
							})
						}else {
							// 删除原来安装包缩小体积
							nativeJs("bridge", "deleteFile", {
								path: plus.io.convertLocalFileSystemURL(filename)
							});
						}
						return;
					}
					_t.isShowModal = true;
					uni.showModal({ //提醒用户更新  
						title: "版本更新",
						content: app_version_desc,
						showCancel: false,
						complete: () => {
							_t.isShowModal = false;
						},
						success: res => {
							if(res.confirm) {
								// plus.downloader.clear();
								Log.d("全量更新地址:" + app_url)
								// 清除下载任务
								if(_t.dtask) _t.dtask.abort();
								// 下载前清除以前下载的缓存,避免apk包累积应用变大
								nativeJs("bridge", "deleteFile", {
									path: plus.io.convertLocalFileSystemURL(filename)
								});
								_t.dtask = plus.downloader.createDownload(app_url, {
									method: "GET",
									filename //利用保存路径,实现下载文件的重命名
								},
								function(d, status){
									Log.d("createDownloaded下载完成" + JSON.stringify(d))
									if(status !== 200) {
										_t.isUpdate = false;
										// 清除下载任务
										_t.dtask.abort();
									}
								});
								_t.dtask.addEventListener("statechanged", function(task, status) {
									switch(task.state) {
								        case 1: // 开始  
								            break;
								        case 2: //已连接到服务器  
								            break;
								        case 3: // 已接收到数据  
								            const current = parseInt(100 * task.downloadedSize / task.totalSize);
								            nativeJs("notification", "setProgress", {
												current
											});
											break;
								        case 4: // 下载完成 
											nativeJs("notification", "compProgressNotification", {
												title: '下载完成'
											});
								            plus.runtime.install(plus.io.convertLocalFileSystemURL(task.filename), //安装APP  
								                {
								                    force: true
								                },
								                function() {
													Log.d('安装准备成功');
													_t.isUpdate = false;
								                },
								                function() {
								                    Log.d('安装准备失败');
													_t.isUpdate = false;
								                });
								            break;
								    }
								});
								nativeJs("notification", "init");
								_t.isUpdate = true;
								_t.dtask.start();
							}
						}
					})
				});
			} else {
				//小版本更新(uniapp中的改动)
				if (res_version_db !== res_version && (parseInt(res_version.replace(/\./g, "")) <
						parseInt(res_version_db.replace(/\./g, "")))) {
					uni.showLoading({
						title: '更新中...',
						mask: true
					});
					Log.d("热更新地址:" + res_url)
					_t.isUpdate = true;
					uni.downloadFile({
						url: res_url,
						success: (downloadResult) => {
							Log.d("补丁信息:" + JSON.stringify(downloadResult))
							if (downloadResult.statusCode === 200) {
								plus.runtime.install(downloadResult.tempFilePath, {
									force: true
								}, function() {
									Log.d('install success...');
									uni.hideLoading();
									_t.isUpdate = false;
									plus.runtime.restart();
									// 删除安装包
									nativeJs("bridge", "deleteFile", {
										path: plus.io.convertLocalFileSystemURL(downloadResult.tempFilePath)
									});
								}, function(e) {
									uni.hideLoading();
									_t.isUpdate = false;
									Log.d('install fail...' + JSON.stringify(e));
									// 删除安装包
									nativeJs("bridge", "deleteFile", {
										path: plus.io.convertLocalFileSystemURL(downloadResult.tempFilePath)
									});
								});
							} else {
								uni.hideLoading();
								_t.isUpdate = false;
							}
						},
						fail() {
							uni.hideLoading();
							_t.isUpdate = false;
						}
					});
				}
				if (isShowWarn && res_version_db === res_version && app_version_db ===
					app_version) {
					uni.showToast({
						title: '已是最新版本',
						icon: 'none',
						duration: 1000,
						mask: false
					});
				}
				if (isShowWarn && (parseInt(res_version.replace(/\./g, "")) > parseInt(
						res_version_db.replace(/\./g, "")) || parseInt(app_version.replace(
						/\./g, "")) > parseInt(app_version_db.replace(/\./g, "")))) {
					uni.showToast({
						title: '暂无新版本',
						icon: 'none',
						duration: 1000,
						mask: false
					});
				}
			}
		})
	});
	// #endif
}

nativeJs

// 通过uniapp自带一套执行
export function nativeJs(targetClass, method, params = {}, successCallback, errorCallback) {
	// #ifdef APP-PLUS
	return plus.bridge.exec(targetClass, method, [plus.bridge.callbackId(typeof successCallback ===
		'function' ? successCallback : null, typeof errorCallback === 'function' ? errorCallback :
		null), JSON.stringify(params)]);
	// #endif
}

-----------------------------------android--------------------------------------

BridgeForUniApp

package com.park;

import android.content.Intent;
import android.util.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.park.util.AppUtil;

import org.json.JSONArray;

import java.io.File;

import io.dcloud.common.DHInterface.IWebview;
import io.dcloud.common.DHInterface.StandardFeature;
import io.dcloud.common.util.JSUtil;

public class BridgeForUniApp extends StandardFeature {
    /**
     * h5调此跳转
     * @param pWebView
     * @param arr
     */
    public void goTargetActivity(IWebview pWebView, JSONArray arr){
        Intent intent = new Intent(pWebView.getContext(), RawActivity.class);
        intent.putExtra("CallBackID", arr.optString(0));
        JSONObject params = JSON.parseObject(arr.optString(1));
        intent.putExtra("access_token", params.getString("token"));
        Log.d("params", params.toJSONString());
        pWebView.getContext().startActivity(intent);
    }

    /**
     * 删除文件
     * @param pWebView
     * @param arr
     * @return
     */
    public Boolean deleteFile(IWebview pWebView, JSONArray arr){
        JSONObject params = JSON.parseObject(arr.optString(1));
        File file = new File(params.getString("path"));
        if(file.exists()) file.delete();
        Log.d("h5","删除文件成功" + params.getString("path"));
        return true;
    }

    /**
     * 获取apk版本号
     * @param pWebView
     * @param arr
     * @return
     */
    public void getFileApkVersion(IWebview pWebView, JSONArray arr){
        JSONObject params = JSON.parseObject(arr.optString(1));
        JSUtil.execCallback(pWebView, arr.optString(0), AppUtil.getFileApkVersion(pWebView.getActivity(), params.getString("path")), JSUtil.OK,false);
    }

    /**
     * 根据文件路径获取文件创建时间
     * @param pWebView
     * @param arr
     * @return
     */
    public void getFileCreatedTime(IWebview pWebView, JSONArray arr){
        JSONObject params = JSON.parseObject(arr.optString(1));
        File file = new File(params.getString("path"));
        if(!file.exists()) return;
        JSUtil.execCallback(pWebView, arr.optString(0), file.lastModified(), JSUtil.OK,false);
    }
}

DownloadProgress

package com.park;

import org.json.JSONArray;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.android.park.R;
import com.park.util.NotificationUtils;
import io.dcloud.common.DHInterface.IWebview;
import io.dcloud.common.DHInterface.StandardFeature;

/**
 * app下载进度
 * @author 蓝之静云
 */
public class DownloadProgress extends StandardFeature {
    static final int ids = 1000;
    // 通知工具栏
    NotificationUtils notificationUtils;

    public void init(IWebview pWebView, JSONArray array){
        if(null != notificationUtils) notificationUtils.clearNotification();
        notificationUtils = new NotificationUtils(pWebView.getActivity());
    }

    /**
     * 初始设置
     *
     * @param pWebView
     * @param array
     */
    public void setNotification(IWebview pWebView, JSONArray array) {
        JSONObject params = JSON.parseObject(array.optString(1));
        String title = params.getString("title");
        String content = params.getString("content");
        notificationUtils.sendNotification(ids, title, content, R.mipmap.ic_launcher);
    }

    /**
     * 进度显示
     *
     * @param pWebView
     * @param array
     */
    public void setProgress(IWebview pWebView, final JSONArray array) {
        JSONObject params = JSON.parseObject(array.optString(1));
        notificationUtils.setNotification(Integer.parseInt(params.getString("current")));
    }

    /**
     * 下载完成后
     *
     * @param pWebView
     * @param array
     */
    public void compProgressNotification(IWebview pWebView, JSONArray array) {
        JSONObject params = JSON.parseObject(array.optString(1));
        String title = params.getString("title");
        notificationUtils = new NotificationUtils(pWebView.getActivity());
        notificationUtils.sendNotification(ids, title, "请去安装体验吧", R.mipmap.ic_launcher);
    }
}

AppUtil

package com.park.util;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

import java.io.File;

/**
 * Created by lenovo on 2019/10/17.
 */

public class AppUtil {
	/**
	 * 获取apk文件的包信息
	 * @param context
	 * @param apkPath
	 * @return
	 */
	public static String getFileApkVersion(Context context, String apkPath) {
		File apkFile = new File(apkPath);
		if(!apkFile.exists()) return "";
		PackageManager pm = context.getPackageManager();
		PackageInfo info = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
		if (info != null) {
			ApplicationInfo appInfo = info.applicationInfo;
			String packageName = appInfo.packageName;  //得到安装包名称
			String version = info.versionName;//获取安装包的版本号
			int versionCode = info.versionCode;
			try {
				return version;
			} catch (OutOfMemoryError e) {
				Log.d("yue-tag", "GetApkInfo: " + e);
			}
		}
		return "";
	}
}

NotificationUtils

package com.park.util;

import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;

import com.android.park.R;

import static android.app.Notification.VISIBILITY_SECRET;
import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;

/**
 * 
 *     @author WTY
 *     desc  : 通知栏工具类
 *     revise:
 * 
*/
public class NotificationUtils extends ContextWrapper { public static final String CHANNEL_ID = "default"; private static final String CHANNEL_NAME = "Default_Channel"; private NotificationManager mManager; private int[] flags; public NotificationUtils(Context base) { super(base); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上 createNotificationChannel(); } } @TargetApi(Build.VERSION_CODES.O) private void createNotificationChannel() { //第一个参数:channel_id //第二个参数:channel_name //第三个参数:设置通知重要性级别 //注意:该级别必须要在 NotificationChannel 的构造函数中指定,总共要五个级别; //范围是从 NotificationManager.IMPORTANCE_NONE(0) ~ NotificationManager.IMPORTANCE_HIGH(4) NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); channel.canBypassDnd();//是否绕过请勿打扰模式 channel.enableLights(true);//是否在桌面icon右上角展示小红点 channel.setLockscreenVisibility(VISIBILITY_SECRET);//锁屏显示通知 channel.setLightColor(Color.RED);//闪关灯的灯光颜色 channel.canShowBadge();//桌面launcher的消息角标 channel.enableVibration(true);//是否允许震动 channel.getAudioAttributes();//获取系统通知响铃声音的配置 channel.getGroup();//获取通知取到组 channel.setBypassDnd(true);//设置可绕过 请勿打扰模式 channel.setSound(null, null); channel.setVibrationPattern(new long[]{100, 100, 200});//设置震动模式 channel.shouldShowLights();//是否会有灯光 channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知 getManager().createNotificationChannel(channel); } /** * 获取创建一个NotificationManager的对象 * @return NotificationManager对象 */ public NotificationManager getManager() { if (mManager == null) { mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); } return mManager; } /** * 清空所有的通知 */ public void clearNotification(){ getManager().cancelAll(); } /** * 获取Notification * @param title title * @param content content */ public Notification getNotification(String title, String content , int icon){ Notification build; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上 //通知用到NotificationCompat()这个V4库中的方法。但是在实际使用时发现书上的代码已经过时并且Android8.0已经不支持这种写法 Notification.Builder builder = getChannelNotification(title, content, icon); build = builder.build(); } else { NotificationCompat.Builder builder = getNotificationCompat(title, content, icon); build = builder.build(); } if (flags!=null && flags.length>0){ for (int a=0 ; a<flags.length ; a++){ build.flags |= flags[a]; } } return build; } /** * 建议使用这个发送通知 * 调用该方法可以发送通知 * @param notifyId notifyId * @param title title * @param content content */ public void sendNotification(int notifyId, String title, String content , int icon) { Notification build; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上 //通知用到NotificationCompat()这个V4库中的方法。但是在实际使用时发现书上的代码已经过时并且Android8.0已经不支持这种写法 Notification.Builder builder = getChannelNotification(title, content, icon); build = builder.build(); } else { NotificationCompat.Builder builder = getNotificationCompat(title, content, icon); build = builder.build(); } if (flags!=null && flags.length>0){ for (int a=0 ; a<flags.length ; a++){ build.flags |= flags[a]; } } getManager().notify(notifyId, build); } /** * 调用该方法可以发送通知 * @param notifyId notifyId * @param title title * @param content content */ public void sendNotificationCompat(int notifyId, String title, String content , int icon) { NotificationCompat.Builder builder = getNotificationCompat(title, content, icon); Notification build = builder.build(); if (flags!=null && flags.length>0){ for (int a=0 ; a<flags.length ; a++){ build.flags |= flags[a]; } } getManager().notify(notifyId, build); } private NotificationCompat.Builder getNotificationCompat(String title, String content, int icon) { NotificationCompat.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID); } else { //注意用下面这个方法,在8.0以上无法出现通知栏。8.0之前是正常的。这里需要增强判断逻辑 builder = new NotificationCompat.Builder(getApplicationContext()); builder.setPriority(PRIORITY_DEFAULT); } builder.setContentTitle(title); builder.setContentText(content); builder.setSmallIcon(icon); builder.setPriority(priority); builder.setOnlyAlertOnce(onlyAlertOnce); builder.setOngoing(ongoing); if (remoteViews!=null){ builder.setContent(remoteViews); } if (intent!=null){ builder.setContentIntent(intent); } if (ticker!=null && ticker.length()>0){ builder.setTicker(ticker); } if (when!=0){ builder.setWhen(when); } if (sound!=null){ builder.setSound(sound); } if (defaults!=0){ builder.setDefaults(defaults); } //点击自动删除通知 builder.setAutoCancel(true); return builder; } @RequiresApi(api = Build.VERSION_CODES.O) private Notification.Builder getChannelNotification(String title, String content, int icon){ Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID); Notification.Builder notificationBuilder = builder //设置标题 .setContentTitle(title) //消息内容 .setContentText(content) //设置通知的图标 .setSmallIcon(icon) //让通知左右滑的时候是否可以取消通知 .setOngoing(ongoing) //设置优先级 .setPriority(priority) //是否提示一次.true - 如果Notification已经存在状态栏即使在调用notify函数也不会更新 .setOnlyAlertOnce(onlyAlertOnce) .setAutoCancel(true); if (remoteViews!=null){ //设置自定义view通知栏 notificationBuilder.setContent(remoteViews); } if (intent!=null){ notificationBuilder.setContentIntent(intent); } if (ticker!=null && ticker.length()>0){ //设置状态栏的标题 notificationBuilder.setTicker(ticker); } if (when!=0){ //设置通知时间,默认为系统发出通知的时间,通常不用设置 notificationBuilder.setWhen(when); } if (sound!=null){ //设置sound notificationBuilder.setSound(sound); } if (defaults!=0){ //设置默认的提示音 notificationBuilder.setDefaults(defaults); } if (pattern!=null){ //自定义震动效果 notificationBuilder.setVibrate(pattern); } return notificationBuilder; } private boolean ongoing = false; private RemoteViews remoteViews = null; private PendingIntent intent = null; private String ticker = ""; private int priority = Notification.PRIORITY_DEFAULT; private boolean onlyAlertOnce = false; private long when = 0; private Uri sound = null; private int defaults = 0; private long[] pattern = null; /** * 让通知左右滑的时候是否可以取消通知 * @param ongoing 是否可以取消通知 * @return */ public NotificationUtils setOngoing(boolean ongoing){ this.ongoing = ongoing; return this; } /** * 设置自定义view通知栏布局 * @param remoteViews view * @return */ public NotificationUtils setContent(RemoteViews remoteViews){ this.remoteViews = remoteViews; return this; } /** * 设置内容点击 * @param intent intent * @return */ public NotificationUtils setContentIntent(PendingIntent intent){ this.intent = intent; return this; } /** * 设置状态栏的标题 * @param ticker 状态栏的标题 * @return */ public NotificationUtils setTicker(String ticker){ this.ticker = ticker; return this; } /** * 设置优先级 * 注意: * Android 8.0以及上,在 NotificationChannel 的构造函数中指定,总共要五个级别; * Android 7.1(API 25)及以下的设备,还得调用NotificationCompat 的 setPriority方法来设置 * * @param priority 优先级,默认是Notification.PRIORITY_DEFAULT * @return */ public NotificationUtils setPriority(int priority){ this.priority = priority; return this; } /** * 是否提示一次.true - 如果Notification已经存在状态栏即使在调用notify函数也不会更新 * @param onlyAlertOnce 是否只提示一次,默认是false * @return */ public NotificationUtils setOnlyAlertOnce(boolean onlyAlertOnce){ this.onlyAlertOnce = onlyAlertOnce; return this; } /** * 设置通知时间,默认为系统发出通知的时间,通常不用设置 * @param when when * @return */ public NotificationUtils setWhen(long when){ this.when = when; return this; } /** * 设置sound * @param sound sound * @return */ public NotificationUtils setSound(Uri sound){ this.sound = sound; return this; } /** * 设置默认的提示音 * @param defaults defaults * @return */ public NotificationUtils setDefaults(int defaults){ this.defaults = defaults; return this; } /** * 自定义震动效果 * @param pattern pattern * @return */ public NotificationUtils setVibrate(long[] pattern){ this.pattern = pattern; return this; } /** * 设置flag标签 * @param flags flags * @return */ public NotificationUtils setFlags(int... flags){ this.flags = flags; return this; } public void setNotification(int progress) { if (this==null){ return; } Intent intent = new Intent(); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); RemoteViews remoteViews = new RemoteViews(this.getPackageName(), R.layout.remote_notification_view); remoteViews.setTextViewText(R.id.tvTitle, "正在下载:"); remoteViews.setProgressBar(R.id.pb, 100, progress, false); NotificationManager manager = this.getManager(); Notification notification = this.setContentIntent(pendingIntent) .setContent(remoteViews) .setFlags(Notification.FLAG_AUTO_CANCEL) .setOnlyAlertOnce(true) .getNotification("新消息", "应用开始更新", R.mipmap.ic_launcher); //下载成功或者失败 if (progress == 100 || progress == -1) { this.clearNotification(); } else { manager.notify(1, notification); } } }

你可能感兴趣的:(uni-app,java,前端,android)