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

news2024/11/26 21:30:40

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">
   <application
        tools:replace="android:label"
        android:label="我的应用"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android: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 as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android: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-data
            android: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>
        ...
        <provider
                android:name="androidx.core.content.FileProvider"
                android:authorities="${applicationId}.fileProvider"
                android:exported="false"
                android:grantUriPermissions="true"
                tools:replace="android:authorities">
            <meta-data
                    android: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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1053134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg 命令:从入门到精通 | ffmpeg 命令分类查询

FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg 命令分类查询 FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg 命令分类查询ffmpeg -versionffmpeg -buildconfffmpeg -formatsffmpeg -muxersffmpeg -demuxersffmpeg -codecsffmpeg -decodersffmpeg -encodersffmpeg -bsfsffmpeg…

LabVIEW学习笔记五:错误,visa关闭超时(错误-1073807339)

写的串口调试工具&#xff0c;其中出现了这个错误 这是串口接收的部分&#xff0c;如果没有在很短的时间内收到外界发进来的数据&#xff0c;这里就会报错。 先在网上查了一下&#xff0c;这个问题很常见&#xff0c;我找到了官方的解答&#xff1a; VISA读取或写入时出现超时…

视频高效剪辑,批量调整视频速度,让视频更加精彩

你是否曾经需要调整多个视频的速度&#xff0c;但却苦于手动操作效率低下&#xff1f;如果你也遇到了这样的问题&#xff0c;那么是时候采取行动&#xff0c;使用一款高效易用的视频处理工具了。 首先&#xff0c;我们要进入好简单批量智剪&#xff0c;并在板块栏里选择“任务…

26358-2022 旅游度假区等级划分 思维导图

声明 本文是学习GB-T 26358-2022 旅游度假区等级划分. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了旅游度假区的等级划分和依据、总则、基本条件、省级和国家级旅游度假区条件。 本文件适用于旅游度假区的等级认定与复核依据…

pandas数据处理之构建联邦学习数据

本文以天池比赛《车辆贷款违约预测》的数据为例&#xff0c;通过pandas处理数据&#xff0c;构建联邦学习数据&#xff0c;用于FATE框架联邦学习。 通过pandas处理数据 1. 读取数据 下载car_loan_train.csv数据后&#xff0c;用pandas读取数据。 import pandas as pddatapd…

Transformers.js v2.6 现已发布

&#x1f92f; 新增了 14 种架构 在这次发布中&#xff0c;我们添加了大量的新架构&#xff1a;BLOOM、MPT、BeiT、CamemBERT、CodeLlama、GPT NeoX、GPT-J、HerBERT、mBART、mBART-50、OPT、ResNet、WavLM 和 XLM。这将支持架构的总数提升到了 46 个&#xff01;以下是一些示例…

IntelliJ IDEA 控制台中文乱码的四种解决方法

前言 IntelliJ IDEA 如果不进行配置的话&#xff0c;运行程序时控制台有时候会遇到中文乱码&#xff0c;中文乱码问题非常严重&#xff0c;甚至影响我们对信息的获取和程序的跟踪。开发体验非常不好。 本文中我总结出四点用于解决控制台中文乱码问题的方法&#xff0c;希望有助…

分布式事务-TCC异常-空回滚

1、空回滚问题&#xff1a; 因为是全局事务&#xff0c;A服务调用服务C的try时服务出现异常服务B因为网络或其他原因还没执行try方法&#xff0c;TCC因为C的try出现异常让所有的服务执行cancel方法&#xff0c;比如B的try是扣减积分 cancel是增加积分&#xff0c;还没扣减就增…

【动态规划】动态规划经典例题 力扣牛客

文章目录 跳台阶 BM63 简单跳台阶扩展 JZ71 简单打家结舍 LC198 中等打家劫舍2 LC213中等最长连续递增序列 LC674 简单乘积最大子数组LC152 中等最长递增子序列LC300 中等最长重复子数组LC718最长公共子串NC BM66最长公共子序列LC1143 中等完全平方数LC279零钱兑换 LC322 中等单…

【图像分割】图像检测(分割、特征提取)、各种特征(面积等)的测量和过滤(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于SSM+Vue的医院住院综合服务管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

前端最新支持四级及以下结构仿企查查、天眼查关联投资机构 股权结构 tree树形结构 控股结构

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。通过组件化开发&#xff0c;可以有效实现单…

数据结构——堆(C语言)

本篇会解决一下几个问题&#xff1a; 1.堆是什么&#xff1f; 2.如何形成一个堆&#xff1f; 3.堆的应用场景 堆是什么&#xff1f; 堆总是一颗完全二叉树堆的某个节点总是不大于或不小于父亲节点 如图&#xff0c;在小堆中&#xff0c;父亲节点总是小于孩子节点的。 如图&a…

外包干了3个月,整个人都萎靡不振了。。。。。

先说一下自己的情况&#xff0c;本科生生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测…

基于Java的美容院管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

Android 内存泄漏分析思路和案例剖析

分析思路 内存泄漏是指 Android 进程中&#xff0c;某些对象已经不再使用&#xff0c;但被一些生命周期更长的对象引用&#xff0c;导致其占用的内存资源无法被GC回收&#xff0c;内存占用不断增加的一种现象&#xff1b;内存泄漏是导致我们应用性能下降、卡顿的一种常见因素&…

ROS2 中的轻量级、自动化、受控回放

一、说明 这篇文章描述了一种在 ROS2 中实现受控重播器的轻量级方法。用以测试中将现象重新播放一遍&#xff0c;以实现调参或故障定位的目的。所有源代码都可以在这里找到。该帖子也可在此处获得。 二、问题&#xff1a;不同步重播 任何曾经认真开发过 ROS2 的人都会知道这个问…

microsoft excel 公式 计算本金 利息 月供 和总利息

//可使用 Excel 公式指导算出贷款的月还款额。 PMT(rate, nper, pv, [fv], [type])//返回根据定期固定付款和固定利率而定的投资在已知期间内的本金偿付额。 PPMT(rate, per, nper, pv, [fv], [type])//基于固定利率及等额分期付款方式&#xff0c;返回给定期数内对投资的利息…

C++ Primer----1.5类简介 章节练习

头文件 Sales_item.h #ifndef SALESITEM_H #define SALESITEM_H #include <iostream> #include <string>class Sales_item{ public:Sales_item(const std::string &book):isbn(book),units_sold(0),revenue(0.0){}Sales_item(std::istream &is){ is >&…

在macOS上从源代码构建编译Aseprite到组装成App应用(macOS上如何不开着终端窗口)

这里记录一下自己构建 Aseprite 流程。关于如何构建可能已经有很多文章写了&#xff0c;但是都没有提到一点&#xff1a;启动的时候需要点击可执行程序&#xff0c;这样是需要终端窗口一直开着的的&#xff08;如下图&#xff09;&#xff0c;关闭终端就会退出程序&#xff0c;…