Xamarin.Android实现App内版本更新

目录

  • 1、具体的效果
  • 2、代码实现
    • 2.1 基本原理
    • 2.2 开发环境
    • 2.3 具体代码
      • 2.3.1 基本设置
      • 2.3.2 系统的权限授予
      • 2.3.3 进度条的layout文件
      • 2.3.4 核心的升级文件
  • 3、代码下载
  • 4、知识点
  • 5、参考文献

1、具体的效果

有事需要在程序内集成自动更新的功能,网上找了下,改改适配下Xamarin.Android,效果如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、代码实现

2.1 基本原理

这个功能本质上,就是使用一个Intent打开一个apk文件进行预览。Android系统遇到预览apk文件时,就会弹出“是否进行安装更新”这类的安装框。

2.2 开发环境

VS2022,.NET7,Xamarin.Android、实体手机的Android版本:11

2.3 具体代码

2.3.1 基本设置

1、允许访问http
为了安全,从Android 7.0之后,不允许直接访问http的资源,因为我们会把安装包放在http的网络环境中,因此需要进行一个设置:在AndroidManifest.xmlapplication节点中,直接添加android:usesCleartextTraffic="true"即可。Android访问http的方案说明
2、设置FileProvider
同样,为了安全,在Android7.0之后,系统安装APP必须使用FileProvider,因此需要在AndroidManifest.xml中进行配置provider
3、权限设置
为了能够下载、存放、读取安装包,需要一系列的权限。需要在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.2" package="com.updateapp" android:installLocation="auto"><uses-sdk android:minSdkVersion="28" android:targetSdkVersion="33" /><!--为了能够安装apk文件,需要下面的一系列授权--><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /><application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">  <!--这句话是为了可以访问http的资源--><!--下面的配置,是为了设置FileProvider,其中用到了file_paths配置文件,具体如下--><provider android:name="androidx.core.content.FileProvider" android:authorities="com.updateapp.fileprovider" android:exported="false" android:grantUriPermissions="true"><meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data></provider></application>
</manifest>

Resources文件下创建xml文件夹,并创建file_paths.xml配置文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android"><!--安装包文件存储路径--><external-files-pathname="my_download"path="Download" /><external-pathname="."path="." />
</paths>

以上,就是第一步,程序的基本配置

2.3.2 系统的权限授予

除了AndroidManifest.xml中进行配置权限外,还需要进行权限的程序判定及授权

protected override void OnCreate(Bundle savedInstanceState)
{base.OnCreate(savedInstanceState);Xamarin.Essentials.Platform.Init(this, savedInstanceState);SetContentView(Resource.Layout.activity_main);Toolbar toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);SetSupportActionBar(toolbar);FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);fab.Click += FabOnClick;//版本跟踪,这个是和Android不一样的地方VersionTracking.Track();//初始化自动升级的功能autoUpdater=new AutoUpdater(this);try{//6.0之后才能使用动态授权if(Build.VERSION.SdkInt>=BuildVersionCodes.M){string[] permissions ={Manifest.Permission.ReadExternalStorage,Manifest.Permission.WriteExternalStorage,Manifest.Permission.AccessWifiState,Manifest.Permission.Internet};List<string> permissionList = new List<string>();for (int i = 0; i < permissions.Length; i++){if(ActivityCompat.CheckSelfPermission(this, permissions[i])!=Permission.Granted){ permissionList.Add(permissions[i]);}}//if(permissionList.Count==0){//更新程序autoUpdater.checkUpdate();}else{//获取授权ActivityCompat.RequestPermissions(this, permissions, 100);}}}catch(Exception e){Toast.MakeText(this,"发生异常:"+e.Message,ToastLength.Long).Show();}
}public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);bool checkPermissionFlag = true;if (requestCode == 100){for(int i = 0; i < permissions.Length; i++){if (grantResults[i]== Permission.Granted){checkPermissionFlag = checkPermissionFlag && true;}else{checkPermissionFlag = checkPermissionFlag && false;}}if(!checkPermissionFlag){//授权程序Snackbar.Make(View.Inflate(this,Resource.Id.activity_main_layout,null),"需要授权",Snackbar.LengthIndefinite).SetAction("ok",new Action<View>(delegate (View obj){ActivityCompat.RequestPermissions(this, permissions, 100);})).Show();}else{//更新程序Toast.MakeText(this, "授权后,可以进行更新程序啦!", ToastLength.Long).Show();autoUpdater.checkUpdate();}}base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

2.3.3 进度条的layout文件

在layout文件夹中添加progress.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/titleBar"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:id="@+id/txtStatus"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="状态"android:textSize="10sp"android:textStyle="normal" /><ProgressBarandroid:id="@+id/progress"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_toLeftOf="@id/txtStatus" /></LinearLayout>
</LinearLayout>

2.3.4 核心的升级文件

using Android.App;
using Android.Content;
using Android.Net;
using Android.OS;
using Android.Runtime;
using Android.Systems;
using Android.Util;
using Android.Views;
using Android.Widget;
using Java.IO;
using Java.Net;
using Java.Util.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Context = Android.Content.Context;
using Environment = Android.OS.Environment;namespace UpdateApp
{public class AutoUpdater{private Android.App.AlertDialog confirmDialog = null; //确认是否下载的对话框private Android.App.AlertDialog loadingDialog = null; //正在下载的对话框public MainActivity mainActivity;private UpdateHandler updateHandler;// 保存APK的文件名private static string saveFileName = "my.apk";private static File apkFile;// 进度条与通知UI刷新的handler和msg常量public ProgressBar mProgress;public TextView txtStatus;public int progress;// 当前进度public AutoUpdater(MainActivity activity) {mainActivity = activity;updateHandler = new UpdateHandler(this);apkFile =new File(mainActivity.GetExternalFilesDir(Environment.DirectoryDownloads), saveFileName);}//主方法public void checkUpdate(){//新开启一个线程,进行下载及逻辑判断Task.Run(() => {//获取本地的版本名称(一般而言就是1.0、1.1、1.2的纯数字)string localVersionName = VersionTracking.CurrentVersion;//获取服务器的版本string remoteServerVersion = "2.2"; //远程获取服务器上最新版本,这儿省事儿了,直接默认取了一个较大的值if (Convert.ToDouble(localVersionName)< Convert.ToDouble(remoteServerVersion)){//启动升级的界面updateHandler.SendEmptyMessage((int)UpdateStatusEnum.BeginLoad);}});}//弹框进行下载public void ShowUpdateDialog(){Android.App.AlertDialog.Builder builder = null;builder = new Android.App.AlertDialog.Builder(mainActivity);confirmDialog = builder.SetTitle("软件版本更新").SetMessage("有最新的软件包,请下载并安装!").SetPositiveButton("立即下载", (s, e) => { //确定按钮及内部方法ShowDownloadDialog();confirmDialog.Dismiss();}).SetNegativeButton("以后再说", (s, e) => { //界面上的关闭按钮及方法confirmDialog.Dismiss();}).Create();confirmDialog.Show();}//弹出确认下载的进度条的内容private void ShowDownloadDialog(){CancellationTokenSource cts = new CancellationTokenSource();CancellationToken cancellationToken = cts.Token;Android.App.AlertDialog.Builder builder = null;builder = new Android.App.AlertDialog.Builder(mainActivity);View view = mainActivity.LayoutInflater.Inflate(Resource.Layout.progress, null, false);mProgress = view.FindViewById<ProgressBar>(Resource.Id.progress);txtStatus = view.FindViewById<TextView>(Resource.Id.txtStatus);loadingDialog = builder.SetView(view).SetTitle("正在更新").SetNegativeButton("取消下载", (s, e) => { //界面上的关闭按钮及方法cts.Cancel();}).Create();loadingDialog.Show();DownloadApk(cancellationToken);}//下载APPprivate void DownloadApk(CancellationToken cancellationToken){Task.Run(() => {try { URL url = new URL(@"http://xxx/xxx/xxx/com.updateapp.apk");//apk的网络地址URLConnection conn = url.OpenConnection();conn.Connect();int length = conn.ContentLength;System.IO.Stream ins = conn.InputStream;FileOutputStream fos = new FileOutputStream(apkFile);int count = 0;byte[] buf = new byte[1024];while (!cancellationToken.IsCancellationRequested){int numread = ins.Read(buf);count += numread;progress = (int)(((float)count / length) * 100);//下载进度Message message = new Message();message.What = (int)UpdateStatusEnum.Loading;Bundle extras = new Bundle();extras.PutInt("progress", progress);message.Data = extras;updateHandler.SendMessage(message);if (numread <= 0){Message msg = new Message();//下载完成msg.What = (int)UpdateStatusEnum.Finish; extras.PutInt("progress", 100);msg.Data = extras;updateHandler.SendMessage(msg);//关闭下载框if(loadingDialog!=null) loadingDialog.Dismiss();    break;}fos.Write(buf, 0, numread);}fos.Close();ins.Close();}catch (System.OperationCanceledException el){Log.Info("info", "用户取消了操作!"+el.Message);}catch (AggregateException e){foreach (Exception ex in e.InnerExceptions){Log.Info("info", "发生异常!" + ex.Message);}}}, cancellationToken);}public void installAPK(){try{if(!apkFile.Exists()){Toast.MakeText(mainActivity, "下载的文件不存在!", ToastLength.Short).Show();return;}//这儿是整个的核心Intent intent = new Intent();intent.SetAction(Intent.ActionView);intent.AddFlags(ActivityFlags.NewTask);intent.AddFlags(ActivityFlags.GrantReadUriPermission);intent.AddFlags(ActivityFlags.GrantWriteUriPermission);if (Build.VERSION.SdkInt >=Android.OS.BuildVersionCodes.N){string packageName = mainActivity.ApplicationContext.PackageName;string authority = new StringBuilder(packageName).Append(".fileprovider").ToString();Android.Net.Uri apkUri = FileProvider.GetUriForFile(mainActivity, authority, apkFile);intent.SetDataAndType(apkUri, "application/vnd.android.package-archive");}else{intent.SetDataAndType(Android.Net.Uri.FromFile(apkFile), "application/vnd.android.package-archive");}mainActivity.StartActivity(intent);}catch (Exception ex){Toast.MakeText(mainActivity, "安装installAPK发生异常"+ex.Message, ToastLength.Short).Show();}}}//状态枚举 public enum UpdateStatusEnum:int{ BeginLoad=1,Loading=2,Finish=3}//Handler事件public class UpdateHandler : Android.OS.Handler{private WeakReference<AutoUpdater> weakReference;[Obsolete]public UpdateHandler(AutoUpdater autoUpdater){weakReference = new WeakReference<AutoUpdater>(autoUpdater);}public override void HandleMessage(Message msg){AutoUpdater targetActivity;bool isGetSuccess = weakReference.TryGetTarget(out targetActivity);if (isGetSuccess){switch (msg.What){case (int)UpdateStatusEnum.BeginLoad:targetActivity.ShowUpdateDialog();break;case (int)UpdateStatusEnum.Loading://获取状态数据,并进行展示int progress = msg.Data.GetInt("progress");targetActivity.txtStatus.SetText(progress + "%",TextView.BufferType.Normal);targetActivity.mProgress.SetProgress(progress, true);break;case (int)UpdateStatusEnum.Finish:Toast.MakeText(targetActivity.mainActivity, "下载完毕", ToastLength.Long).Show();targetActivity.installAPK();break;default:break;}}base.HandleMessage(msg);}}}

上面是这个的核心

3、代码下载

代码下载

4、知识点

1、Handler的用法,C#与Java还是不同的,这里涉及到的知识点是匿名类和委托。C#的匿名类是一个field的集合,不能包含方法
2、Android中更新应用的逻辑

每个 Android 应用均有一个唯一的应用 ID,像 Java 或 Kotlin 软件包名称一样,例如 com.example.myapp。此 ID 可以作为每个应用在设备上的唯一标识。Android 设备一次只能安装一个具有指定应用 ID 的应用。
为了让 Android 平台接受更新,更新必须满足以下条件:
应用更新的应用 ID 必须与已安装应用的应用 ID 相同。
应用更新的签名证书必须与已安装应用的签名证书相同,或者必须包含有效的 proof-of-rotation。
应用更新的版本代码必须高于或等于已安装应用的版本代码。
在某些情况下,用户可能需要接受更新。
请注意,如果多个更新具有相同的签名证书并且具有相同或更高的版本代码,Android 内部并没有防范措施能够阻止不同的安装程序更新应用。
如要安装不符合上述条件的应用,用户必须先卸载当前已安装的版本,而卸载操作会清除设备上的所有应用数据。

5、参考文献

主要参考了前两个
1、Android App自动安装
2、Android APP 自动更新实现(适用Android9.0)
3、【Android】APP检测版本升级更新、apk安装
4、Andrioid FileProvider在Xamarin.Forms中的使用
5、Xamarin.Android 中 Handler 的使用

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【开发篇】二、属性绑定与校验

文章目录 1、ConfigurationProperties自定义Bean属性绑定2、EnableConfigurationProperties注解3、ConfigurationProperties第三方Bean属性绑定4、松散绑定5、常用计量单位6、数据校验7、yaml绑定值的坑--关于进制 1、ConfigurationProperties自定义Bean属性绑定 前面读取yaml…

链表的回文判断

思路: 找中间节点–>逆置->比较 代码&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) { struct ListNode*slowhead; struct ListNode*f…

AI数字人:最强声音驱动面部表情模型VideoReTalking

目录 1 VideoReTalking论文解读 1.1 介绍 1.2 相关工作 1.2.1 视频编辑中的音频配音 1.2.2 基于音频的单图像面部动画 1.3 框架 1.3.1 语义引导重演网络 1.3.2 口型同步网络 1.3.3 身份感知增强网络 1.3.4 后期处理 1.4 训练 1.4.1 每个模块的训练 1.4.2 评估 1.…

8+单基因+细胞凋亡+WGCNA+单细胞+实验验证

今天给同学们分享一篇单基因细胞凋亡WGCNA实验验证的生信文章“RASGRP2 is a potential immune-related biomarker and regulates mitochondrial-dependent apoptosis in lung adenocarcinoma”&#xff0c;这篇文章于2023年2月3日发表在Front Immunol期刊上&#xff0c;影响因…

软件测试缺陷报告详解

【软件测试行业现状】2023年了你还敢学软件测试&#xff1f;未来已寄..测试人该何去何从&#xff1f;【自动化测试、测试开发、性能测试】 缺陷报告是描述软件缺陷现象和重现步骤地集合。软件缺陷报告Software Bug Report&#xff08;SBR&#xff09;或软件问题报告Software Pr…

【效率提升】maven 转 gradle 实战 | 京东云技术团队

一、灵魂三问 1、gradle 是什么&#xff1f; 一个打包工具&#xff0c; 是一个开源构建自动化工具&#xff0c;足够灵活&#xff0c;可以构建几乎任何类型的软件&#xff0c;高性能、可扩展、能洞察等。其中洞察&#xff0c;可以用于分析构建过程中数据&#xff0c;提供分析参…

一招解除csdn复制限制

先看这个代码 python读取英文pdf翻译成中文pdf文件导出代码 想要复制代码&#xff0c;csdn有限制怎么办&#xff08;csdn流氓&#xff0c;无耻&#xff09; 解除方法 ctrlu 看效果

UOS Deepin Linux 安装 anaconda

UOS Deepin Linux 安装 anaconda 下载 anaconda 官网下载 国内开源镜像站下载 官网下载 anaconda 官网&#xff1a; https://www.anaconda.com/ 点击右上角 Free Download 按钮 跳转值下载页面&#xff1a;https://www.anaconda.com/download 国内开源镜像站下载 清华大学开源…

TouchGFX之画布控件

TouchGFX的画布控件&#xff0c;在使用相对较小的存储空间的同时保持高性能&#xff0c;可提供平滑、抗锯齿效果良好的几何图形绘制。 TouchGFX 设计器中可用的画布控件&#xff1a; LineCircleShapeLine Progress圆形进度条 存储空间分配和使用​ 为了生成反锯齿效果良好的…

下载github.com上的依赖资源

下载github.com上的依赖资源&#xff08;需要反复试才能成功&#xff0c;所以单独安装&#xff09; export GIT_TRACE1 export GIT_CURL_VERBOSE1 pip install githttps://github.com/PanQiWei/AutoGPTQ.git -i https://pypi.mirrors.ustc.edu.cn/simple --trusted-hostpypi.mi…

stm32之GPIO库函数点灯分析

stm32官方为了方便开发者&#xff0c;利用CubeMX 生成HAL库有关的C代码。HAL库就是硬件抽象层(hardware abstraction layer)&#xff0c;生成一系列的函数帮助我们快速生成工程&#xff0c;脱离复杂的寄存器配置。stm32相对于51来功能强大&#xff0c;但是寄存器的数量也不是一…

2023华为杯研究生数学建模C题分析

完整的分析查看文末名片获取&#xff01; 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比性&#xff0c;不同专家评审的作品集合之间应有一些交集。但有的交集大了&#xff0c;则…