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

.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 作为服务端)

文章目录思路客户端权限清单&#xff08;AndroidManifest.xml&#xff09;权限列表(完整内容看 权限清单&#xff08;AndroidManifest.xml&#xff09;&#xff09;打开外部应用的权限(完整内容看 权限清单&#xff08;AndroidManifest.xml&#xff09;&#xff09;添加文件如下…...

分享111个HTML艺术时尚模板,总有一款适合您

分享111个HTML艺术时尚模板&#xff0c;总有一款适合您 111个HTML艺术时尚模板下载链接&#xff1a;https://pan.baidu.com/s/1sYo2IPma4rzeku3yCG7jGw?pwdk8dx 提取码&#xff1a;k8dx Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 时尚理发沙龙服务网站模…...

spring之Spring AOP基于注解

文章目录前言一、Spring AOP基于注解的所有通知类型1、前置通知2、后置通知3、环绕通知4、最终通知5、异常通知二、Spring AOP基于注解之切面顺序三、Spring AOP基于注解之通用切点三、Spring AOP基于注解之连接点四、Spring AOP基于注解之全注解开发前言 通知类型包括&#x…...

LeetCode题目笔记——6362. 合并两个二维数组 - 求和法

文章目录题目描述题目链接题目难度——简单方法一&#xff1a;常规双指针遍历代码/Python方法二&#xff1a;字典\哈希表代码/Python总结题目描述 给你两个 二维 整数数组 nums1 和 nums2. nums1[i] [idi, vali] 表示编号为 idi 的数字对应的值等于 vali 。nums2[i] [idi, …...

【C#基础】C# 常用语句讲解

序号系列文章3【C#基础】C# 数据类型总结4【C#基础】C# 变量和常量的使用5【C#基础】C# 运算符总结文章目录前言语句概念1&#xff0c;迭代语句1.1 for 语句1.2 foreach 语句1.3 while 语句1.4 do 语句2&#xff0c;选择语句2.1&#xff0c;if 语句2.2&#xff0c;else 语句2.3…...

腾讯云——负载均衡CLB

负载均衡 CLB 提供四层&#xff08;TCP 协议/UDP 协议/TCP SSL 协议&#xff09;和七层&#xff08;HTTP 协议/HTTPS 协议&#xff09;负载均衡。您可以通过 CLB 将业务流量分发到多个后端服务器上&#xff0c;消除单点故障并保障业务可用性。CLB 自身采用集群部署&#xff0c;…...

6.关于系统服务的思考—— native vs java

文章目录native服务 以sensor service为例Native 服务java 服务&#xff0c; 以vibrate为例java 服务 以一个demo为例native服务 以sensor service为例 service启动 SystemServer.startBootstrapServices---->>>mSystemServiceManager.startService—>>>Sen…...

SQL语句创建视图:

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍数据库中有关视图的知识,参考学校作业. 金句分享:…...

使用BP神经网络和Elman Net预测航班价格(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…...

JavaWeb9-volatile解决内存可见性和指令重排序问题

目录 1.解决内存可见性问题 2.解决指令重排序问题 3.volatile缺点 4.特使使用场景 volatile&#xff08;易变的&#xff0c;易挥发的&#xff0c;不稳定的&#xff09;可以解决内存可见性和指令重排序的问题。 1.解决内存可见性问题 代码在写入 volatile 修饰的变量时&am…...

Docker - 镜像操作命令

镜像名称一般分为两部分组成:[repository]:[tag]在没有指定tag时&#xff0c;默认是latest&#xff0c;代表最新版本的镜像1.下载docker镜像 docker pull repository:tag2.查看本地所有镜像 docker images3.创建镜像别名 docker tag repository:tag repository111:tag4.查看镜像…...

全栈之路-前端篇 | 第三讲.基础前置知识【前端标准与研发工具】学习笔记

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; 涉及 企业运维、网络安全、应用开发、物联网、人工智能、大数据 学习知识 “ 花开堪折直须折&#xff0c;莫待无花…...

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 开发坦克大战&#xff08;四&#xff09;坦克大战添加音效1. 初始化音效2. 加入游戏开始音效和坦克移动音效3. 添加坦克开火音效4. 添加装甲削减音效5. 添加坦克爆炸音效6. 添加子弹击中边界音效坦克大战添加音效 我的素材放到了百度网盘里&#xff0c;…...

New和Malloc的使用及其差异

1&#xff0c;new的使用关于new的定义&#xff1a;new其实就是告诉计算机开辟一段新的空间&#xff0c;但是和一般的声明不同的是&#xff0c;new开辟的空间在堆上&#xff0c;而一般声明的变量存放在栈上。通常来说&#xff0c;当在局部函数中new出一段新的空间&#xff0c;该…...

2023年细胞生物学复习汇总

细胞分化 1.什么是细胞分化&#xff1f;细胞分化的特点是什么&#xff1f; 答&#xff1a;&#xff08;1&#xff09;细胞分化&#xff08;cell differentiation&#xff09;是指同一来源的细胞逐渐产生出形态结构、功能特征各不相同的细胞类群的过程&#xff0c;其结果是在空间…...

光伏VSG-基于虚拟同步发电机的光伏并网逆变器系统MATLAB仿真

采用MATLAB2021b仿真&#xff01;&#xff01;&#xff01;仿真模型1光伏电池模块&#xff08;采用MATLAB自带光伏模块&#xff09;、MPPT控制模块、升压模块、VSG控制模块、电流滞环控制模块。2s时改变光照强度 &#xff01;&#xff01;&#xff01;VSG输出有功功率、无功功率…...

高可用 - 02 Keepalived_VRRP工作原理

文章目录Keepalived VS HeartbeatKeepalived的用途VRRP与工作原理物理路由器和虚拟路由器Keepalived VS Heartbeat Keepalived是Linux下一个轻量级的高可用解决方案&#xff0c;它与Heartbeat、RoseHA实现的功能类似&#xff0c;都可以实现服务或者网络的高可用&#xff0c;但…...

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修改本地仓库的文件 项目修改完成后&#xff0c;查看修改的内容 git diff上传修改之后的内容到本地暂存区 git add modified_files将本地暂存区的代码更新…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号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毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...