当前位置: 首页 > news >正文

flutter开发实战-应用更新apk下载、安装apk、启动应用实现

flutter开发实战-应用更新apk下载、安装apk、启动应用实现

在开发过程中,经常遇到需要更新下载新版本的apk文件,之后进行应用更新apk下载、安装apk、启动应用。我们在flutter工程中实现下载apk,判断当前版本与需要更新安装的版本进行比对判断,通过判断VersionCode来确定下载新版版APK

一、应用更新apk下载

当应用需要更新的时候,我们需要判断版本号,在flutter工程中versionCode是工程中的pubspec.yaml中的version确定的。

如version: 1.0.0+1

version为1.0.0,versionCode为1

需要我们获取接口,需要判断的就是versionCode确定是否需要下载apk。

1.1、获取新版本地址接口

获取新版本的接口使用的是Dio库。dio 是一个强大的 Dart HTTP 请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。

这里的请求为GET请求,

Response? response = await dio.get(requestUrl,queryParameters: params,options: Options(contentType: Headers.jsonContentType));

我这里就不写请求的逻辑了。
根据请求,获取到了

// 获取检查版本

Future<void> checkVersion() async {var params = {};ApiRepository.checkVersion(params: params,success: (response) {// {"version":"2","url":"http://wwww.laileshuo.com/download/myapp_v1.0.0_release.apk"}var object = response.object;if (object != null && (object is Map) && object.isNotEmpty) {String? versionCode = object['versionCode'];String? url = object['url'];// 判断是否需要下载更新String versionCodeStr = "";if (version != null) {versionCodeStr = "${versionCode}";}checkAppVersionUpdate(versionCodeStr: versionCodeStr, apkUrl: url);}print("checkVersion params:${params}, object:${object.toString()}");},failure: (error) {print("checkVersion params:${params}, error:${error.toString()}");});}

通过检查新版本接口获取到了url及versionCode,这里的versionCode和pubspec.yaml的进行比较看是否需要下载apk。

判断下载apk

Future<void> checkAppVersionUpdate({String? versionCodeStr, String? apkUrl}) async {try {if (versionCodeStr != null &&apkUrl != null &&versionCodeStr.isNotEmpty &&apkUrl.isNotEmpty) {String curVersionCodeStr = await PlatformUtils.getBuildNum();int versionCode = int.parse(versionCodeStr);int curVersionCode = int.parse(curVersionCodeStr);if (versionCode > curVersionCode) {// 需要更新的版本code,大于当前的版本才更新}}} catch (e) {print("appVersionUpdate apkUrl:${apkUrl}, version:${version}, exception:${e.toString()}");}}

1.2、下载Apk

在判断需要更新的时候,我们需要下载新版本的apk。下载的库我们使用的也是Dio。

下载的代码可参考https://blog.csdn.net/gloryFlow/article/details/131658621

当获取到新版的下载地址url时候,需要下载apk

void downApk(String url, String saveDestPath) {HttpApi().doDownload(url, saveDestPath, cancelToken: CancelToken(),progress: (int received, int total) {// 下载进度setState(() {_downloadRatio = (received / total);if (_downloadRatio == 1) {_downloading = false;}_downloadIndicator = (_downloadRatio * 100).toStringAsFixed(2) + '%';});}, completion: () {// 下载成功FlutterLoadingHud.showToast(message: "\"下载完成\"");}, failure: (error) {// 下载出错FlutterLoadingHud.showToast(message: error.message);});
}

下载完成后可以执行安装并且启动操作了。

二、APK安装及启动

APK安装及启动需要原生插件来实现。

2.1、创建原生插件flutter_package_manager

创建flutter plugin,我使用的工具是Android studio。

配置如下内容:

  • Project name
  • Project location
  • Description
  • Project type: Plugin
  • Android language
  • iOS language
  • Platforms

如图所示

在这里插入图片描述

我们需要实现installThenStart

/// An implementation of [FlutterPackageManagerPlatform] that uses method channels.
class MethodChannelFlutterPackageManager extends FlutterPackageManagerPlatform {/// The method channel used to interact with the native platform.final methodChannel = const MethodChannel('flutter_package_manager');Future<String?> getPlatformVersion() async {final version = await methodChannel.invokeMethod<String>('getPlatformVersion');return version;}Future<void> installThenStart(String apkFilePath, String activity) async {final result = await methodChannel.invokeMethod<void>('installThenStart');return result;}
}

可以看到定义了installThenStart,需要apkFilePath与activity作为参数。

在Android端实现,由于我这边需要静默安装(apk在后台安装,不出现安装界面的提示)

public class FlutterPackageManager implements MethodCallHandler {private static final String TAG = "FlutterPackageManager";private final Registrar registrar;/*** Plugin registration.*/public static void registerWith(Registrar registrar) {final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_package_manager");channel.setMethodCallHandler(new FlutterPackageManager(registrar));}private FlutterPackageManager(Registrar registrar) {this.registrar = registrar;}public void onMethodCall(MethodCall call, Result result) {if (call.method.equals("getPlatformVersion")) {result.success(android.os.Build.VERSION.RELEASE);} else if (call.method.equals("installThenStart")) {String path = call.arguments['filePath'];String activity = call.arguments['activity'];installApk(path, activity);} else {result.notImplemented();}}void installApk(String path, String activity) {// root权限静默安装实现 实现实际使用的是su pm install -r filePath命令。Process process = null; OutputStream out = null; InputStream in = null; try { // 请求root process = Runtime.getRuntime().exec("su"); out = process.getOutputStream(); // 调用安装 out.write(("pm install -r " + path + "\n").getBytes()); in = process.getInputStream(); int len = 0; byte[] bs = new byte[256]; while (-1 != (len = in.read(bs))) { String state = new String(bs, 0, len); if (state.equals("Success\n")) { //安装成功后的操作 startActivity(activity);} } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.flush(); out.close(); } if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } }void startActivity(String activity) {// activity格式为'com.laileshuo.app/com.laileshuo.app.MainActivity'Intent mIntent = new Intent(); val componentName = ComponentName(this, activity)val intent = Intent().setComponent(componentName)startActivity(intent)}
}

当然,工程中的AndroidManifest.xml也需要做相应的调整,如下

<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.laileshuo.app"><applicationtools:replace="android:label"android:label="我的应用"android:name="${applicationName}"android:icon="@mipmap/ic_launcher"><activityandroid:name="com.laileshuo.app.MainActivity"android:exported="true"android:launchMode="singleTop"android:theme="@style/LaunchTheme"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:hardwareAccelerated="true"android:windowSoftInputMode="adjustResize"><!-- Specifies an Android theme to apply to this Activity as soon asthe Android process has started. This theme is visible to the userwhile the Flutter UI initializes. After that, this theme continuesto determine the Window background behind the Flutter UI. --><meta-dataandroid:name="io.flutter.embedding.android.NormalTheme"android:resource="@style/NormalTheme"/><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><!-- Don't delete the meta-data below.This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --><meta-dataandroid:name="flutterEmbedding"android:value="2" /></application>
</manifest>

2.2、如果非root环境安装,可以使用open_file插件

需要在pubspec.yaml引入插件

dependencies:open_file: ^3.3.2

在可以直接使用代码安装apk

import 'package:open_file/open_file.dart';OpenFile.open(apkFilePath);

当与关于FileProvider的其他插件发生冲突时,需要配置AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="xxx.xxx.xxxxx"><application>...<providerandroid:name="androidx.core.content.FileProvider"android:authorities="${applicationId}.fileProvider"android:exported="false"android:grantUriPermissions="true"tools:replace="android:authorities"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/filepaths"tools:replace="android:resource" /></provider></application>
</manifest>

三、小结

flutter开发实战-应用更新apk下载、安装apk、启动应用实现。在开发过程中,经常遇到需要更新下载新版本的apk文件,之后进行应用更新apk下载、安装apk、启动应用。我们在flutter工程中实现下载apk,判断当前版本与需要更新安装的版本进行比对判断,通过判断VersionCode来确定下载新版版APK。内容较多,描述可能不准确,请见谅。

https://blog.csdn.net/gloryFlow/article/details/133440529

学习记录,每天不停进步。

相关文章:

flutter开发实战-应用更新apk下载、安装apk、启动应用实现

flutter开发实战-应用更新apk下载、安装apk、启动应用实现 在开发过程中&#xff0c;经常遇到需要更新下载新版本的apk文件&#xff0c;之后进行应用更新apk下载、安装apk、启动应用。我们在flutter工程中实现下载apk&#xff0c;判断当前版本与需要更新安装的版本进行比对判断…...

DispatcherServlet初始化之Spring容器创建1.0

一、前言 在SpringMVC框架中&#xff0c;DispatcherServlet扮演着非常重要的角色&#xff0c;它负责接收所有的HTTP请求并将其分发给相应的处理器。在DispatcherServlet的初始化过程中&#xff0c;会创建一个Spring容器来管理应用程序中的Bean。 二、步骤 1、加载配置文件&a…...

CSS的基础

CSS美化HTML&#xff0c;布局网页 CSS最大的价值&#xff1a;由HTML专注去做结构呈现&#xff0c;样式给CSS&#xff0c;结构&#xff08;HTML)与样式&#xff08;CSS&#xff09;相分离 CSS主要由选择器以及一条或多条声明 在<head></head>中实现CSS在<body…...

mathtype如何嵌入到word中?详细mathtype安装步骤教程

mathtype是一款功能特别强大的数学方式编辑软件&#xff0c;为用户提供各种强大的数学公式符号帮助用户进行计算&#xff0c;并且速度很快。有小伙伴知道mathtype如何嵌入到word中吗&#xff0c;这里小编就给大家详细介绍一下mathtype嵌入到word中的方法&#xff0c;有需要的小…...

云安全之访问控制的常见攻击及防御

访问控制攻击概述 访问控制漏洞即应用程序允许攻击者执行或者访问某种攻击者不具备相应权限的功能或资源。 常见的访问控制可以分为垂直访问控制、水平访问控制及多阶段访问控制 (上下文相关访问控制)&#xff0c;与其相应的访问控制漏洞为也垂直越权漏洞(普通用户可以访问或…...

Java编程技巧:跨域

目录 1、跨域概念2、后端CORS&#xff08;跨域资源共享&#xff09;配置原理3、既然请求跨域了&#xff0c;那么请求到底发出去没有&#xff1f;4、通过后端CORS&#xff08;跨域资源共享&#xff09;配置解决跨域问题代码4.1、SpringBoot&#xff08;FilterRegistrationBean&a…...

react create-react-app 配置less

环境信息&#xff1a; create-react-app:v5 react:18.2.0 node:18.16.0 如果你不必须使用 less 建议直接使用scss。 因为less配置会遇到很多问题。 配置less过程&#xff1a; 如果你只需要 sass的话&#xff0c;就可以直接使用sass。因为默认配置了scss。 npm、yarn、cnpm、…...

树的表示——孩子兄弟表示法

从图中可以看出&#xff0c;树的每个结点&#xff0c;都有不确定的指向他们的孩子的节点&#xff0c;如果我们定义这样一个结构体来便是数的结构的话&#xff1a; struct TreeNode { int val; struct TreeNodep1; struct TreeNodep1; … }; 是不能够表示一棵树的&#xff0c;因…...

Windows11安装MySQL8.1

安装过程中遇到任何问题均可以参考(这个博客只是单纯升级个版本和简化流程) Windows安装MySQL8教程-CSDN博客 到官网下载mysql8数据库软件 MySQL :: Download MySQL Community Server 下载完后,解压到你需要安装的文件夹 其中的配置文件内容了如下 [mysqld]# 设置3306端口po…...

Linux编程——经典链表list_head

1. 关于list_head struct list_head是Linux内核定义的双向链表&#xff0c;包含一个指向前驱节点和后继节点的指针的结构体。其定义如下&#xff1a; struct list_head {struct list_head *next, *prev; //双向链表&#xff0c;指向节点的指针 };1.1 链表的定义和初始化 有两…...

基于51单片机NEC协议红外遥控发送接收仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

基于51单片机NEC协议红外遥控发送接收仿真设计 讲解视频1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单&&下载链接 基于51单片机NEC协议红外遥控发送接收仿真设计 51单片机红外发送接收仿真设计( proteus仿真程序原理图报告讲解视频…...

Jmeter分布式压力测试

目录 1、场景 2、原理 3、注意事项 4、slave配置 5、master配置 6、脚本执行 1、场景 在做性能测试时&#xff0c;单台机器进行压测可能达不到预期结果。主要原因是单台机器压到一定程度会出现瓶颈。也有可能单机网卡跟不上造成结果偏差较大。 例如4C8G的window server机…...

Rust :mod.rs和lib.rs中use的作用

一、mod.rs和lib.rs mod.rs往往是把同一目录下的n个rs文件综合在一起的有效方式&#xff1b; lib.rs是一个库或子库层次综合在一起的有效方式&#xff1b; 下面举个实例来说明。生成一个rusttoc本地库&#xff08;由cargo new rusttoc --lib所生成&#xff09;&#xff0c;目录…...

ISP图像信号处理——平场校正介绍以及C++实现

参考文章1&#xff1a;http://t.csdn.cn/h8TBy 参考文章2&#xff1a;http://t.csdn.cn/6nmsT 参考网址3&#xff1a;opencv平场定标 - CSDN文库 平场校正一般先用FPN(Fixed Pattern Noise)固定图像噪声校正,即暗场校正&#xff1b;再用PRNU(Photo Response Non Uniformity)…...

【深入了解Java String类】

目录 String类 常用方法 字符串的不可变性 String的内存分析 StringBuilder类 解释可变和不可变字符串 常用方法 面试题&#xff1a;String&#xff0c;StringBuilder&#xff0c;StringBuffer之间的区别和联系 String类的OJ练习 String类 【1】直接使用&#xff0c…...

基于SpringBoot的知识管理系统

目录 前言 一、技术栈 二、系统功能介绍 用户管理 文章分类 资料分类 文章信息 论坛交流 资料下载 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#xff0c;针对这个问题开发一个…...

Pytorch基础:Tensor的reshape方法

在Pytorch中&#xff0c;reshape是Tensor的一个重要方法&#xff0c;它与Numpy中的reshape类似&#xff0c;用于返回一个改变了形状但数据和数据顺序和原来一致的新Tensor对象。注意&#xff1a;此时返回的数据对象并不一定是新的&#xff0c;这取决于应用此方法的Tensor是否是…...

【数据库——MySQL】(13)过程式对象程序设计——存储函数、错误处理以及事务管理

目录 1. 存储函数2. 存储函数的应用3. 错误处理4. 抛出异常5. 事务处理6. 事务隔离级7. 应用实例参考书籍 1. 存储函数 要 创建 存储函数&#xff0c;需要用到 CREATE 语句&#xff1a; CREATE FUNCTION 存储函数名([参数名 类型, ...])RETURNS 类型[存储函数体]注意&#xff1…...

Spring Boot的魔法:构建高性能Java应用

文章目录 Spring Boot&#xff1a;简化Java开发Spring Boot的性能优势1. 内嵌服务器2. 自动配置3. 起步依赖4. 缓存和优化5. 异步处理 实际示例&#xff1a;构建高性能的RESTful API总结 &#x1f389;欢迎来到架构设计专栏~Spring Boot的魔法&#xff1a;构建高性能Java应用 ☆…...

如何做好测试?(七)兼容性测试 (Compatibility Testing, CT)

1. 兼容性测试介绍 兼容性测试 (Compatibility Testing, CT)是一种软件测试方法&#xff0c;旨在验证应用程序在不同操作系统、浏览器、设备和网络环境下的正确运行和一致性。对于网上购物系统来说&#xff0c;兼容性测试非常重要&#xff0c;因为用户可能使用各种不同的设备和…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...