Skip to main content

GeneralUpdate.Maui.Android — Execution Flow Deep Dive

Target Audience: Developers integrating auto-update into .NET MAUI Android apps

After reading you will understand:

  • AndroidBootstrap's combined two-step API design intent (ValidateAsync → ExecuteUpdateAsync)
  • HttpRangeDownloader's recoverable download: Range requests + temp files + atomic replacement
  • ExecuteUpdateAsync's internal Interlocked concurrency protection and atomic state transitions
  • SHA256 verification with automatic corrupted file cleanup
  • Android Package Installer's FileProvider + Intent trigger flow
  • AddGeneralUpdateMauiAndroid() DI registration extension design
  • Key design differences from Avalonia.Android in API style and DI strategy
  • Exception-to-UpdateFailureReason classification mapping

Table of Contents

  1. Architecture Overview
  2. Entry: DI-First Factory Design
  3. AndroidBootstrap: Combined Two-Step API
  4. Step 1: ValidateAsync — Version Check
  5. Step 2: ExecuteUpdateAsync — Atomic Complete Update
  6. Recoverable Download: HttpRangeDownloader Deep Dive
  7. SHA256 Verification & Corrupted File Cleanup
  8. APK Install: Platform-Guarded Installer
  9. Concurrency Safety: Interlocked Atomic Operations
  10. SafeInvoke: Defensive Event Firing
  11. Exception Mapping: MapFailureReason Classification
  12. Design Comparison with Avalonia.Android
  13. Key Code Path Index

1. Architecture Overview

1.1 Five-Service DI-First Architecture

Maui.Android uses a DI-first + manual assembly coexistence design:

┌──────────────────────────────────────────────────────────────┐
│ GeneralUpdateBootstrap (Static Factory) │
│ CreateDefault() → IAndroidBootstrap │
│ AddGeneralUpdateMauiAndroid(services) → IServiceCollection │
├──────────────────────────────────────────────────────────────┤
│ AndroidBootstrap (Orchestration Layer) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ IUpdate │ │ IHash │ │ IApkInstaller │ │
│ │ Downloader │ │ Validator │ │ FileProvider │ │
│ │ HTTP resume │ │ SHA256 │ │ Intent trigger │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────────┐ │
│ │ IUpdate │ │ HttpDownloadOptions │ │
│ │ Storage │ │ SSL / Proxy / Timeout / Retry / Auth │ │
│ │ Provider │ └──────────────────────────────────────┘ │
│ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘

1.2 Core Differences from Avalonia.Android

DimensionMaui.AndroidAvalonia.Android
API StyleTwo-step combined (Validate + ExecuteUpdate)Three-step explicit (Validate + DownloadAndVerify + LaunchInstaller)
DI StrategyDI-first, AddGeneralUpdateMauiAndroid()Manual assembly first, CreateDefault()
ConcurrencyInterlocked atomic opsSemaphoreSlim(1,1) gate
Event SafetySafeInvoke iterate delegatesIUpdateEventDispatcher dispatch
Platform Guard#if ANDROID compile-timeRuntime platform check
Temp File.downloading extension.part + .json sidecar
ProgressIProgress<DownloadStatistics>EventHandler<DownloadProgressChangedEventArgs>
SHA256Required (mandatory)Optional (strongly recommended)
Min APIAPI 21 (Android 5.0)API 26 (Android 8.0)

2. Entry: DI-First Factory Design

DI Registration

public static IServiceCollection AddGeneralUpdateMauiAndroid(
this IServiceCollection services, HttpClient? httpClient = null)
{
services.AddSingleton<IUpdateDownloader>(sp =>
new HttpRangeDownloader(httpClient ?? new HttpClient()));
services.AddSingleton<IHashValidator, Sha256Validator>();
services.AddSingleton<IApkInstaller, AndroidApkInstaller>();
services.AddSingleton<IUpdateStorageProvider, UpdateFileStore>();
services.AddSingleton<IUpdateLogger, DefaultUpdateLogger>();
services.AddSingleton<IAndroidBootstrap, AndroidBootstrap>();
return services;
}

Typical MAUI App Registration

// MauiProgram.cs
builder.Services.AddGeneralUpdateMauiAndroid();

3. AndroidBootstrap: Combined Two-Step API

Complete Lifecycle

State Machine

None → Checking → UpdateAvailable → Downloading → Verifying → ReadyToInstall → Installing → Completed

Atomic state transitions via Interlocked.Exchange:

private void ChangeState(UpdateState state)
{
Interlocked.Exchange(ref _currentState, (int)state);
}

4. Step 1: ValidateAsync — Version Check

ValidateInputs(packageInfo, options); // Validates non-null: CurrentVersion, Version, DownloadUrl, Sha256

var currentVersion = new Version(options.CurrentVersion);
var latestVersion = new Version(packageInfo.Version);

if (latestVersion <= currentVersion)
return UpdateCheckResult.NoUpdate();

SafeInvoke(AddListenerValidate, new ValidateEventArgs(packageInfo));
return UpdateCheckResult.UpdateAvailable(packageInfo);

Security design: SHA256 is mandatory — Maui.Android does not allow skipping integrity verification.


5. Step 2: ExecuteUpdateAsync — Atomic Complete Update

Four Completion Stages

StageEnum ValueTrigger
Download CompleteDownloadCompletedFile downloaded + atomically replaced
Verification CompleteVerificationCompletedSHA256 verified
Installation TriggeredInstallationTriggeredAndroid Installer Intent fired
Workflow CompleteWorkflowCompletedAll steps succeeded

6. Recoverable Download: HttpRangeDownloader

HEAD request → Content-Length, Accept-Ranges, ETag
Check partial download → {targetFilePath}.downloading
If partial exists:
→ file size ≤ Content-Length → Range: bytes={size}-
→ otherwise delete and restart
GET with Range → stream to temp file → report progress

Atomic File Replacement

// UpdateFileStore.ReplaceTemporaryWithFinal
File.Move(temporaryPath, targetPath); // Atomic rename

7. SHA256 Verification & Corrupted File Cleanup

var hashResult = await _hashValidator.ValidateSha256Async(
targetFilePath, packageInfo.Sha256, ...);

if (!hashResult.IsSuccess)
{
if (options.DeleteCorruptedPackageOnFailure)
_storageProvider.DeleteFileIfExists(targetFilePath);
throw new InvalidDataException("Integrity check failed.");
}

8. APK Install: Platform-Guarded Installer

public Task TriggerInstallAsync(string filePath, InstallOptions options, CancellationToken ct)
{
#if ANDROID
// Check INSTALL_PACKAGES permission (API 26+)
// FileProvider.GetUriForFile()
// Intent ACTION_VIEW + GRANT_READ_URI_PERMISSION + NEW_TASK
// StartActivity(intent)
#else
throw new PlatformNotSupportedException("APK installation is only supported on Android.");
#endif
}

9. Concurrency Safety: Interlocked Atomic Operations

private int _isExecuting;

public async Task<UpdateExecutionResult> ExecuteUpdateAsync(...)
{
if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) != 0)
{
return UpdateExecutionResult.Failure(
UpdateFailureReason.AlreadyInProgress, "...");
}

try { /* execute update */ }
finally { Interlocked.Exchange(ref _isExecuting, 0); }
}

Advantage over SemaphoreSlim: Lighter weight — no async wait overhead, no deadlock risk, naturally suited for "execute or reject" scenarios.


10. SafeInvoke: Defensive Event Firing

private void SafeInvoke<TEventArgs>(EventHandler<TEventArgs>? eventHandler, ...)
{
if (eventHandler is null) return;
foreach (EventHandler<TEventArgs> subscriber in eventHandler.GetInvocationList())
{
try { subscriber(this, eventArgs); }
catch (Exception ex) { _logger.LogError(...); }
}
}

Key guarantee: One subscriber's exception never blocks other subscribers.


11. Exception Mapping: MapFailureReason Classification

private static UpdateFailureReason MapFailureReason(Exception ex) => ex switch
{
ArgumentException => UpdateFailureReason.InvalidInput,
HttpRequestException => UpdateFailureReason.Network,
InvalidDataException => UpdateFailureReason.IntegrityCheckFailed,
IOException => UpdateFailureReason.FileAccess,
UnauthorizedAccessException => UpdateFailureReason.InstallPermissionDenied,
OperationCanceledException => UpdateFailureReason.Canceled,
_ => UpdateFailureReason.Unknown
};

12. Design Comparison with Avalonia.Android

AspectMaui.AndroidAvalonia.Android
FrameworkMAUI DI ecosystem deep integrationFramework-agnostic general design
API GranularityCombined (fewer APIs, internal atomicity)Explicit (more APIs, caller control)
When to ChooseUsing MAUI, need DI, prefer simple APIUsing Avalonia, need per-step custom logic, need Sidecar resume

13. Key Code Path Index

ComponentFileKey Methods
DI + FactoryServices/GeneralUpdateBootstrap.csAddGeneralUpdateMauiAndroid() / CreateDefault()
OrchestratorServices/AndroidBootstrap.csValidateAsync() / ExecuteUpdateAsync() / SafeInvoke()
DownloaderServices/HttpRangeDownloader.csDownloadAsync() / HEAD / Range
SHA256Services/Sha256Validator.csValidateSha256Async()
InstallerPlatform/Android/AndroidApkInstaller.csTriggerInstallAsync() / #if ANDROID
StorageServices/UpdateFileStore.csGetPackagePaths() / ReplaceTemporaryWithFinal()
Speed CalculatorUtilities/SpeedCalculator.csDownload speed metering
Update OptionsModels/UpdateOptions.csCurrentVersion / DeleteCorruptedPackageOnFailure
Completion StagesEnums/UpdateCompletionStage.csDownloadCompleted / VerificationCompleted / InstallationTriggered / WorkflowCompleted