如果你们也是厌倦了每次升级都要重新下载一整个包,重新安装。那么可以试试这个热更新方案,其实热更新这个技术在移动端并不少见,好几年前做原生APP开发的应该都接触过这个技术,这次我也是用到了uniapp的热更新,所以记录一下,方便后期直接使用。
技术不难,请往下看 ~
首先我是创建了一个uniapp项目,啥代码都没有写。咱们就把这个当做是实际项目中已经开发好的项目,毕竟只是演示嘛。所以这个版本就当做是1.0.0版本吧
好,那么接下来,我们就在这个1.0.0版本中加入热更新的代码吧。
一、编写热更新代码到旧版本
首先打开项目里的App.vue代码,在onLaunch中编写,因为我们希望App一启动的时候就会去检测是否有新版本需要去更新。具体代码如下:
<script>
export default {
onLaunch: function() {
console.log('App Launch');
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid,(widgetInfo)=>{
console.log(widgetInfo);
uni.request({
url:'http://192.168.3.20:8080/UpdateProject/update/check',
data:{
versionCode:widgetInfo.versionCode
},
success:function(res){
console.log(res);
if(res.data.code==0){
if(res.data.data.down){
//下载地址根据实际来修改,我这个是拼接前缀的下载地址
let downUrl="http://192.168.3.20:8080"+res.data.data.down;
console.log("url:"+downUrl);
uni.downloadFile({
url:downUrl,
success:function(downloadResult){
if(downloadResult.statusCode==200){
plus.runtime.install(downloadResult.tempFilePath,{
force:false
},function(){
console.log("安装成功");
plus.runtime.restart();
},function(e){
console.log(e);
console.log("安装失败...")
});
}
}
})
}
}else{
console.log("已经是最新版本,无需更新")
}
}
})
})
// #endif
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
代码已经给你了,但是听我解说一下哈, 首先,// #ifdef APP-PLUS 这个玩意不是个注释,这叫条件编译
条件编译(在组件,css,js , .json配置中都可以使用):
在某种条件下才会编译执行或者不执行的代码块
语法: // #条件 平台
条件:
ifdef : if defined 正向条件,意思就是在xxx平台下才会编译生效
ifndef : if not defined 反向条件,意思就是在xxx平台下不会生效,其他平台都生效
endif : 结束标记,意味着条件结束
平台:
APP-PLUS 只会在app端环境下生效
APP-PLUS-NVUE 只会在app nvue环境下生效
H5 只会在H5环境下生效
MP 微信小程序/支付宝小程序/百度小程序/头条小程序/QQ小程序
MP-WEIXIN 只会在微信小程序环境下生效
MP-ALIPAY 只会在支付宝小程序环境下生效
MP-BAIDU 只会在百度小程序环境下生效
MP-TOUTIAO 只会在字节跳动小程序环境下生效
MP-360 只会在360小程序环境下生效
MP-QQ 只会在QQ小程序环境下生效
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) 就是获取程序自身配置,通过自身的appid获取程序包信息
widgetInfo 里面就包含了各种包信息,其中就有版本号,版本名称
就是这个配置,比如我们是1.0.0版本,版本号就是100,等我们修改了bug或者新增了功能,版本我们就可以改成1.0.1,版本号就改成101。这样实际运营时,我们就可以知道目前安装的版本是多少,以及进行版本比较了
再往下uni.request就是个正常的请求,把自己当前的版本号,记得是数字的那个,不是那个带点的。发给后台,后台去查询最新的版本的版本号进行比较,如果发送的版本小于后台查询的版本,那么说明你需要更新版本了,会返回下载地址等,如果发送的版本不小于后台查询的版本,那么就无需返回下载地址,我是这样写的逻辑。
所以我判断返回code是否是0,0的话,代表需要更新,然后当下载地址不为空时就去下载了。下载成功后,就使用plus.runtime.install去安装即可
二、编写新版本
接下来我们先编写一点代码,也就是实际项目中,我们修复了bug或者新增了功能之后,变成了新的版本。我这里就是简单加行代码表示新增了功能
然后要去manifest.json中修改版本信息
三、生成wgt包
我们基于新版本生成wgt包,也就是升级包。点击发行选择 原生App-制作应用wgt包
加不加混淆看你自己,加了更安全,一定程度上防止反编译
我们可以看到wgt包已经生成了。
四、java后台编写(只做参考,实际项目自己修改)
我的后台是个springboot+layui+mybatis
而且我只做了查询列表,上传wgt包,跟检查是否需要更新功能
表结构:
Controller
@RestController
@RequestMapping("/update")
public class UpdateController {
//tomcat虚拟路径,上传的wgt存放在这里,实际项目中存放在ftp服务器,或者oss上
@Value("${upload.filePath}")
private String filePath;
@Autowired
private UpdateService updateService;
/**
* 分页查询App版本列表
* @param page 页码
* @param limit 每页大小
* @return 分页后的版本列表数据
*/
@RequestMapping("/list")
public ResultTableBean getListByPage(int page,int limit){
ResultTableBean rtb=new ResultTableBean();
List<UpdateBean> list = updateService.findUpdateBeanList(page, limit);
int updateCount = updateService.getUpdateCount();
rtb.setCode(0);
rtb.setData(list);
rtb.setCount(updateCount);
return rtb;
}
/**
* 检查是否需要更新
* @param versionCode 版本号
* @return 结果
*/
@RequestMapping("/check")
public ResultBean checkAppNeedUpdate(int versionCode){
ResultBean rb=new ResultBean();
UpdateBean updateBean = updateService.checkUpdate(versionCode);
if (updateBean!=null){
rb.setCode(0);
rb.setData(updateBean);
}else {
rb.setCode(1);
}
return rb;
}
@RequestMapping("/add")
public ResultBean uploadAppVersion(@RequestParam("app") MultipartFile file, UpdateBean bean){
ResultBean rb=new ResultBean();
//获取文件名
String originalFilename = file.getOriginalFilename();
//获取文件的后缀名
String suffixName = originalFilename.substring(originalFilename.lastIndexOf("."));
//为了避免同一个文件夹下文件名重复问题,所以随机生成uuid作为文件名
String fileName= UUID.randomUUID()+suffixName;
File file1=new File(filePath+fileName);
try {
//文件存放到虚拟路径下
file.transferTo(file1);
//保存虚拟路径到数据库
bean.setDown("/appVersion/"+fileName);
boolean result = updateService.addUpdateBean(bean);
if (result) {
rb.setCode(0);
}else {
rb.setCode(1);
}
} catch (IOException e) {
e.printStackTrace();
rb.setCode(2);
}
return rb;
}
}
Service
@Service
public class UpdateServiceImpl implements UpdateService {
@Autowired
private UpdateMapper updateMapper;
@Override
public List<UpdateBean> findUpdateBeanList(int page, int limit) {
return updateMapper.findUpdateBeanList((page-1)*limit,limit);
}
@Override
public int getUpdateCount() {
return updateMapper.getUpdateCount();
}
@Override
public boolean addUpdateBean(UpdateBean bean) {
int row = updateMapper.addUpdateBean(bean);
if(row>0){
return true;
}
return false;
}
@Override
public UpdateBean checkUpdate(int versionCode) {
UpdateBean updateBeanLast = updateMapper.findUpdateBeanLast();
if (updateBeanLast!=null){
if (updateBeanLast.getCode()>versionCode){
return updateBeanLast;
}
}
return null;
}
}
Mapper
@Repository
@Mapper
public interface UpdateMapper {
/**
* 分页查询版本列表
* @param start 起始位置
* @param end 结束位置
* @return 版本列表
*/
public List<UpdateBean> findUpdateBeanList(int start,int end);
/**
* 获取版本列表总个数
* @return 总个数
*/
public int getUpdateCount();
/**
* 查询最新的版本
* @return 最新的版本
*/
public UpdateBean findUpdateBeanLast();
/**
* 新增APP版本
* @param bean 版本信息
* @return 受影响的行数
*/
public int addUpdateBean(UpdateBean bean);
}
Mapper.xml
<select id="findUpdateBeanList" resultType="com.xxx.update.bean.UpdateBean">
select *from tb_update order by upload_time desc limit #{param1},#{param2}
</select>
<select id="getUpdateCount" resultType="int">
select count(*) from tb_update
</select>
<select id="findUpdateBeanLast" resultType="com.xxx.update.bean.UpdateBean">
select *from tb_update order by upload_time desc limit 1
</select>
<insert id="addUpdateBean" parameterType="com.xxx.update.bean.UpdateBean">
insert into tb_update values(null,#{name},#{code},#{desc},#{down},now())
</insert>
前端页面代码就不放了,意义不大
前端效果图如下:
这样就OK了,上传了这个新版本包
升级后效果:
好了,到此就告一段落吧!
本人个人原创,如有雷同,纯属巧合,或者与本人联系,做改动。请转载或者CV组合标明出处,谢谢!(如有疑问或错误欢迎指出,本人QQ:752231513)