.net开发安卓入门-自动升级(配合.net6 webapi 作为服务端)
文章目录
- 思路
- 客户端
- 权限清单(AndroidManifest.xml)
- 权限列表(完整内容看 权限清单(AndroidManifest.xml))
- 打开外部应用的权限(完整内容看 权限清单(AndroidManifest.xml))
- 添加文件如下图
- provider_paths.xml内容
- 升级类库代码
- 调用代码
- 事件回调
- 注意:这里是安卓11,因为是已经确定版本了,所以没做判断,正确做法应该如下
- 服务端接口
- 注意事项:在iis中或者.netcore中下载apk配置方式不一样
- 完整代码
思路
- 服务端提供版本信息和apk下载地址
- 客户端通过对比版本进行文件下载安装升级
客户端
权限清单(AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0.2" package="com.companyname.boshiac.forklift.app" android:installLocation="auto"><uses-sdk android:minSdkVersion="29" android:targetSdkVersion="33" /><application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true"><provider android:name="androidx.core.content.FileProvider" android:authorities="com.companyname.boshiac.forklift.app.fileprovider" android:exported="false" android:grantUriPermissions="true"><meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /></provider></application><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /><uses-permission android:name="android.permission.DELETE_CACHE_FILES" /><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /><uses-permission android:name="android.permission.INTERNET" />
</manifest>
权限列表(完整内容看 权限清单(AndroidManifest.xml))
安装权限、文件读写权限等都是必要的权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
打开外部应用的权限(完整内容看 权限清单(AndroidManifest.xml))
在application节点中加入下面代码
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.companyname.boshiac.forklift.app.fileprovider" android:exported="false" android:grantUriPermissions="true"><meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /></provider>
添加文件如下图

provider_paths.xml内容
根据自己的权限需要开放对应的目录权限就可以了
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android"><!--1、对应内部内存卡根目录:Context.getFileDir()--><files-pathname="int_root"path="/" /><!--2、对应应用默认缓存根目录:Context.getCacheDir()--><cache-pathname="app_cache"path="/" /><!--3、对应外部内存卡根目录:Environment.getExternalStorageDirectory()--><external-pathname="ext_root"path="pictures/" /><!--4、对应外部内存卡根目录下的APP公共目录:Context.getExternalFileDir(String)--><external-files-pathname="ext_pub"path="/" /><!--5、对应外部内存卡根目录下的APP缓存目录:Context.getExternalCacheDir()--><external-cache-pathname="ext_cache"path="/" /></paths>
升级类库代码
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;namespace BOSHIAC.Forklift.App
{internal class UpgradeService{private string apkUrl;private string versionUrl;private string version;/// <summary>/// 更新了/// </summary>public event Action<string> UpgradeEvent;public UpgradeService(string versionUrl, string apkUrl, string currentVersion){this.versionUrl = versionUrl;this.apkUrl = apkUrl;version = currentVersion;}public void Start(){Task.Run(async () =>{var client = new HttpClient();while (true){try{var response = await client.GetAsync(versionUrl);var hostVersion = response.Content.ReadAsStringAsync().Result;if (hostVersion != version){using (var stream = client.GetStreamAsync(apkUrl).Result){var downloaddir = Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDownloads).AbsolutePath;var fileName = Path.Combine(downloaddir, "forklift.apk");if (File.Exists(fileName))File.Delete(fileName);using (var fs = new FileStream(fileName, FileMode.CreateNew)){stream.CopyTo(fs);fs.Flush();UpgradeEvent?.Invoke(fileName);return;}}}}catch (Exception ex){throw;}finally{Task.Delay(TimeSpan.FromSeconds(30)).Wait();}}});}}
}
调用代码
UpgradeService service = new UpgradeService("http://192.168.69.82/api/Upgrade/Version", "http://192.168.69.82/apks/forklift.apk", this.PackageManager.GetPackageInfo(this.PackageName, 0).VersionName);service.UpgradeEvent += Service_UpgradeEvent;service.Start();
事件回调
private void Service_UpgradeEvent(string file){this.RunOnUiThread(() =>{// var f = this.PackageManager.CanRequestPackageInstalls();// this.GetPackageManager().canRequestPackageInstalls();var alertDialog = new Android.App.AlertDialog.Builder(this).SetTitle("升级提示").SetMessage("检测到新的版本,必须升级哦!").SetIcon(Resource.Mipmap.ic_launcher).SetPositiveButton("升级", (des, dee) =>{try{Intent install = new Intent(Intent.ActionView);Java.IO.File fileName = new Java.IO.File(file);Android.Net.Uri uri = FileProvider.GetUriForFile(Android.App.Application.Context, "com.companyname.boshiac.forklift.app.fileprovider", fileName) ;//打开新版本应用的 install.SetFlags(ActivityFlags.NewTask);install.SetFlags(ActivityFlags.GrantReadUriPermission);install.SetDataAndType(uri, "application/vnd.android.package-archive");// "application/vnd.android.package-archive"StartActivity(install);}catch (System.Exception ex){;}}).SetCancelable(false).Create();alertDialog.Show();});}
注意:这里是安卓11,因为是已经确定版本了,所以没做判断,正确做法应该如下
Intent i = new Intent(Intent.ActionView);var saveFolder = Android.OS.Environment.ExternalStorageDirectory;var file = string.Format("{0}/{1}{2}", saveFolder, this.PackageName, ".apk");Java.IO.File apkFile = new Java.IO.File(file);Intent intent = new Intent(Intent.ActionView);intent.SetFlags(ActivityFlags.NewTask);if (Build.VERSION.SdkInt >= BuildVersionCodes.N){intent.SetFlags(ActivityFlags.GrantReadUriPermission);Android.Net.Uri uri = FileProvider.GetUriForFile(this, PackageName + ".fileprovider", apkFile);intent.SetDataAndType(uri, "application/vnd.android.package-archive");}else{intent.SetDataAndType(Android.Net.Uri.FromFile(new Java.IO.File(file)), "application/vnd.android.package-archive");}StartActivity(intent);
https://blog.csdn.net/qq_38977099/article/details/119115061
服务端接口
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.IO;namespace Boshi_HaiNan_Pda.WebApi.Controllers
{[Route("api/[controller]")][ApiController]public class UpgradeController : ControllerBase{private IWebHostEnvironment environment;private string versionJsonPath;public UpgradeController(IWebHostEnvironment hostingEnvironment){environment = hostingEnvironment;var dir = System.IO.Path.Combine(environment.WebRootPath, "apks\\");if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);versionJsonPath = System.IO.Path.Combine(dir, "version.json"); }[HttpGet("Version")]public string GetVersion(){if (!System.IO.File.Exists(versionJsonPath))return "0.0.0";return System.IO.File.ReadAllText(versionJsonPath).ToLower();} }
}
注意事项:在iis中或者.netcore中下载apk配置方式不一样
在iis中配置网络上有很多文章,都是配置mime,”application/vnd.android.package-archive“ 这个是没有问题的,如下配置

在.netcore中需要做如下配置
program.cs 或者startup文件中增加如下代码
app.UseStaticFiles(new StaticFileOptions{//FileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory()),//设置不限制content-type 该设置可以下载所有类型的文件,但是不建议这么设置,因为不安全//下面设置可以下载apk和nupkg类型的文件ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>{{ ".apk", "application/vnd.android.package-archive" }})}).UseStaticFiles();
完整代码
https://download.csdn.net/download/iml6yu/87463366
相关文章:
.net开发安卓入门-自动升级(配合.net6 webapi 作为服务端)
文章目录思路客户端权限清单(AndroidManifest.xml)权限列表(完整内容看 权限清单(AndroidManifest.xml))打开外部应用的权限(完整内容看 权限清单(AndroidManifest.xml))添加文件如下…...
分享111个HTML艺术时尚模板,总有一款适合您
分享111个HTML艺术时尚模板,总有一款适合您 111个HTML艺术时尚模板下载链接:https://pan.baidu.com/s/1sYo2IPma4rzeku3yCG7jGw?pwdk8dx 提取码:k8dx Python采集代码下载链接:采集代码.zip - 蓝奏云 时尚理发沙龙服务网站模…...
spring之Spring AOP基于注解
文章目录前言一、Spring AOP基于注解的所有通知类型1、前置通知2、后置通知3、环绕通知4、最终通知5、异常通知二、Spring AOP基于注解之切面顺序三、Spring AOP基于注解之通用切点三、Spring AOP基于注解之连接点四、Spring AOP基于注解之全注解开发前言 通知类型包括&#x…...
LeetCode题目笔记——6362. 合并两个二维数组 - 求和法
文章目录题目描述题目链接题目难度——简单方法一:常规双指针遍历代码/Python方法二:字典\哈希表代码/Python总结题目描述 给你两个 二维 整数数组 nums1 和 nums2. nums1[i] [idi, vali] 表示编号为 idi 的数字对应的值等于 vali 。nums2[i] [idi, …...
【C#基础】C# 常用语句讲解
序号系列文章3【C#基础】C# 数据类型总结4【C#基础】C# 变量和常量的使用5【C#基础】C# 运算符总结文章目录前言语句概念1,迭代语句1.1 for 语句1.2 foreach 语句1.3 while 语句1.4 do 语句2,选择语句2.1,if 语句2.2,else 语句2.3…...
腾讯云——负载均衡CLB
负载均衡 CLB 提供四层(TCP 协议/UDP 协议/TCP SSL 协议)和七层(HTTP 协议/HTTPS 协议)负载均衡。您可以通过 CLB 将业务流量分发到多个后端服务器上,消除单点故障并保障业务可用性。CLB 自身采用集群部署,…...
6.关于系统服务的思考—— native vs java
文章目录native服务 以sensor service为例Native 服务java 服务, 以vibrate为例java 服务 以一个demo为例native服务 以sensor service为例 service启动 SystemServer.startBootstrapServices---->>>mSystemServiceManager.startService—>>>Sen…...
SQL语句创建视图:
前言 🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏: 🍔🍟🌯 c语言初阶 🔑个人信条: 🌵知行合一 🍉本篇简介:>:介绍数据库中有关视图的知识,参考学校作业. 金句分享:…...
使用BP神经网络和Elman Net预测航班价格(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...
JavaWeb9-volatile解决内存可见性和指令重排序问题
目录 1.解决内存可见性问题 2.解决指令重排序问题 3.volatile缺点 4.特使使用场景 volatile(易变的,易挥发的,不稳定的)可以解决内存可见性和指令重排序的问题。 1.解决内存可见性问题 代码在写入 volatile 修饰的变量时&am…...
Docker - 镜像操作命令
镜像名称一般分为两部分组成:[repository]:[tag]在没有指定tag时,默认是latest,代表最新版本的镜像1.下载docker镜像 docker pull repository:tag2.查看本地所有镜像 docker images3.创建镜像别名 docker tag repository:tag repository111:tag4.查看镜像…...
全栈之路-前端篇 | 第三讲.基础前置知识【前端标准与研发工具】学习笔记
欢迎关注「全栈工程师修炼指南」公众号 点击 👇 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习! 涉及 企业运维、网络安全、应用开发、物联网、人工智能、大数据 学习知识 “ 花开堪折直须折,莫待无花…...
Tomcat 线上调优记录
原始Tomcat配置 启动参数Plaintext-Xms256m -Xmx512m -XX:MaxPermSize128m Tomcat 参数配置XML<Executor name"tomcatThreadPool" namePrefix"catalina-exec-" maxThreads"1500" minSpareThreads"50" maxIdleTime"600000&q…...
学习 Python 之 Pygame 开发坦克大战(四)
学习 Python 之 Pygame 开发坦克大战(四)坦克大战添加音效1. 初始化音效2. 加入游戏开始音效和坦克移动音效3. 添加坦克开火音效4. 添加装甲削减音效5. 添加坦克爆炸音效6. 添加子弹击中边界音效坦克大战添加音效 我的素材放到了百度网盘里,…...
New和Malloc的使用及其差异
1,new的使用关于new的定义:new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该…...
2023年细胞生物学复习汇总
细胞分化 1.什么是细胞分化?细胞分化的特点是什么? 答:(1)细胞分化(cell differentiation)是指同一来源的细胞逐渐产生出形态结构、功能特征各不相同的细胞类群的过程,其结果是在空间…...
光伏VSG-基于虚拟同步发电机的光伏并网逆变器系统MATLAB仿真
采用MATLAB2021b仿真!!!仿真模型1光伏电池模块(采用MATLAB自带光伏模块)、MPPT控制模块、升压模块、VSG控制模块、电流滞环控制模块。2s时改变光照强度 !!!VSG输出有功功率、无功功率…...
高可用 - 02 Keepalived_VRRP工作原理
文章目录Keepalived VS HeartbeatKeepalived的用途VRRP与工作原理物理路由器和虚拟路由器Keepalived VS Heartbeat Keepalived是Linux下一个轻量级的高可用解决方案,它与Heartbeat、RoseHA实现的功能类似,都可以实现服务或者网络的高可用,但…...
vue实现xml在线编辑功能
先看效果 避免误会 这是一个在线编辑器 我们可以在这上面随意的编写xml代码格式 我们修改上面的内容之后 就可以在控制台输出内容 如果这正是您想要的东西 那就可以先创建一个vue项目 我们先引入依赖 npm install brace -S npm install element-ui -S npm install vue-cli…...
GitHub Workflow
GitHub Workflow 基本流程 把远程仓库克隆到本地 git clone xxxx.git在本地切换至新的分支 git checkout -b new_branch修改本地仓库的文件 项目修改完成后,查看修改的内容 git diff上传修改之后的内容到本地暂存区 git add modified_files将本地暂存区的代码更新…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx
“Reactive Stream Processing in Industrial IoT using DDS and Rx” 是指在工业物联网(IIoT)场景中,结合 DDS(Data Distribution Service) 和 Rx(Reactive Extensions) 技术,实现 …...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
【版本控制】GitHub Desktop 入门教程与开源协作全流程解析
目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork(创建个人副本)步骤 2: Clone(克隆…...
