.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将本地暂存区的代码更新…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
