You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms移动端:实现应用退出后仍完成任务(如下载)

在Xamarin.Forms中实现应用退出后持续运行的后台任务(如下载)

Xamarin.Forms本身没有统一的跨平台后台任务API,必须针对iOS和Android各自的系统机制实现,以下是具体方案:

Android 平台:前台服务(Foreground Service)

Android 8.0+对后台服务限制严格,只有前台服务能在应用退出后持续运行,且必须显示通知告知用户。

实现步骤:

  1. 创建后台服务类,继承自Service,在其中处理下载逻辑并启动前台通知
  2. 在AndroidManifest.xml中声明必要权限
  3. 通过Xamarin.Forms依赖服务启动该服务

代码示例:

后台服务类

[Service]
public class DownloadService : Service
{
    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        // 创建前台通知渠道(Android 8.0+必需)
        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            var channel = new NotificationChannel("download_channel", "下载任务", NotificationImportance.Default);
            var notificationManager = (NotificationManager)GetSystemService(NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }

        // 构建前台通知
        var notification = new Notification.Builder(this, "download_channel")
            .SetContentTitle("下载进行中")
            .SetContentText("正在完成文件下载")
            .SetSmallIcon(Resource.Drawable.ic_download)
            .Build();

        // 启动前台服务
        StartForeground(1, notification);

        // 异步执行下载任务
        _ = Task.Run(async () =>
        {
            var downloadUrl = intent.GetStringExtra("url");
            await ExecuteDownload(downloadUrl);
            // 下载完成后停止前台服务和自身
            StopForeground(true);
            StopSelf();
        });

        return StartCommandResult.Sticky;
    }

    private async Task ExecuteDownload(string url)
    {
        // 实现具体下载逻辑
        using var httpClient = new HttpClient();
        var response = await httpClient.GetAsync(url);
        var filePath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, "downloaded_file.zip");
        
        using var stream = await response.Content.ReadAsStreamAsync();
        using var fileStream = new FileStream(filePath, FileMode.Create);
        await stream.CopyToAsync(fileStream);
    }

    public override IBinder OnBind(Intent intent) => null;
}

依赖服务接口与实现
共享项目接口:

public interface IDownloadService
{
    void StartDownload(string url);
}

Android平台实现:

[assembly: Dependency(typeof(DownloadServiceImplementation))]
namespace YourAppName.Droid
{
    public class DownloadServiceImplementation : IDownloadService
    {
        public void StartDownload(string url)
        {
            var intent = new Intent(Android.App.Application.Context, typeof(DownloadService));
            intent.PutExtra("url", url);

            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                Android.App.Application.Context.StartForegroundService(intent);
            }
            else
            {
                Android.App.Application.Context.StartService(intent);
            }
        }
    }
}

AndroidManifest.xml权限添加

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+必需 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- 如需存储到外部存储 -->

iOS 平台:后台会话(Background Session)

iOS不允许应用退出后长时间运行后台服务,但提供了后台会话机制专门处理网络下载/上传任务,系统会接管任务直至完成。

实现步骤:

  1. 使用NSUrlSession创建后台会话
  2. 实现INSUrlSessionDownloadDelegate处理下载回调
  3. 在AppDelegate中处理后台任务完成的系统回调

代码示例:

后台下载服务实现

[assembly: Dependency(typeof(iOSDownloadService))]
namespace YourAppName.iOS
{
    public class iOSDownloadService : IDownloadService, INSUrlSessionDownloadDelegate
    {
        private NSUrlSession _backgroundSession;

        public void StartDownload(string url)
        {
            // 创建后台会话配置,标识符需唯一
            var config = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration("com.yourapp.download.session");
            _backgroundSession = NSUrlSession.FromConfiguration(config, this, NSOperationQueue.MainQueue);
            
            // 创建并启动下载任务
            var downloadTask = _backgroundSession.CreateDownloadTask(new NSUrl(url));
            downloadTask.Resume();
        }

        [Export("URLSession:downloadTask:didFinishDownloadingToURL:")]
        public void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
        {
            // 将临时文件移动到指定目录
            var docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var destPath = Path.Combine(docPath, "downloaded_file.zip");
            var destUrl = NSUrl.FromFilename(destPath);

            try
            {
                NSFileManager.DefaultManager.Move(location, destUrl, out var error);
                if (error != null)
                {
                    // 处理文件移动错误
                }
            }
            catch (Exception ex)
            {
                // 捕获异常
            }
        }

        [Export("URLSession:task:didCompleteWithError:")]
        public void DidComplete(NSUrlSession session, NSUrlSessionTask task, NSError error)
        {
            if (error != null)
            {
                // 处理下载错误
            }
        }
    }
}

AppDelegate中添加后台回调处理

public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
    // 关联后台会话,确保回调被正确处理
    var session = NSUrlSession.FromBackgroundSessionIdentifier(sessionIdentifier);
    if (session != null)
    {
        // 若需要,可在此更新会话委托
    }
    // 必须调用completionHandler告知系统任务处理完成
    completionHandler();
}

Xamarin.Forms共享项目调用

在共享代码中通过依赖服务启动下载:

var downloadService = DependencyService.Get<IDownloadService>();
downloadService.StartDownload("https://example.com/your-large-file.zip");

注意事项

  • Android:需引导用户将应用加入电池优化白名单,否则系统可能在低电量时终止前台服务;前台通知无法隐藏,必须保持到任务完成。
  • iOS:后台会话仅支持HTTP/HTTPS下载,且任务必须是可恢复的;系统会限制后台任务的资源,无法执行计算密集型操作。

内容的提问来源于stack exchange,提问作者Jaafar Dhainy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 08:10:24