首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当NSUrlSession充电器电缆拔出在iOS 14.4上时,BGTaskScheduler内部的iOS照片上传任务不会执行

当NSUrlSession充电器电缆拔出在iOS 14.4上时,BGTaskScheduler内部的iOS照片上传任务不会执行
EN

Stack Overflow用户
提问于 2021-03-18 11:28:43
回答 1查看 236关注 0票数 1

我们正在开发一个Xamarin Forms应用程序,它应该在后台将照片上传到API。该应用程序是根据客户的要求为客户定制的,因此他们会将手机设置为任何需要设置的权限。

如果充电电缆插上电源,下面就可以正常工作了。

我正在使用BGTaskScheduler (iOS13+)并对这两种类型的任务(BGProcessingTaskRequestBGAppRefreshTaskRequest)进行排队,这样如果插入电缆就会触发BGProcessingTaskRequest,如果没有,则等待BGAppRefreshTaskRequest获得处理时间。

我将RefreshTaskIdUploadTaskId添加到Info.plist

AppDelegate.cs in iOS项目如下

代码语言:javascript
复制
  public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        global::Xamarin.Forms.Forms.Init();
        LoadApplication(new App());

        BGTaskScheduler.Shared.Register(UploadTaskId, null, task => HandleUpload(task as BGProcessingTask));
        BGTaskScheduler.Shared.Register(RefreshTaskId, null, task => HandleAppRefresh(task as BGAppRefreshTask));

        return base.FinishedLaunching(app, options);
    }

    public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
    {
        Console.WriteLine("HandleEventsForBackgroundUrl");
        BackgroundSessionCompletionHandler = completionHandler;
    }
    public override void OnActivated(UIApplication application)
    {
        Console.WriteLine("OnActivated");
    }

    public override void OnResignActivation(UIApplication application)
    {
        Console.WriteLine("OnResignActivation");
    }

    private void HandleAppRefresh(BGAppRefreshTask task)
    {
        HandleUpload(task);
    }

    public override void DidEnterBackground(UIApplication application)
    {
        ScheduleUpload();
    }

    private void HandleUpload(BGTask task)
    {
        var uploadService = new UploadService();
        uploadService.EnqueueUpload();
        task.SetTaskCompleted(true);
    }

    private void ScheduleUpload()
    {
        var upload = new BGProcessingTaskRequest(UploadTaskId)
        {
            RequiresNetworkConnectivity = true,
            RequiresExternalPower = false
        };

        BGTaskScheduler.Shared.Submit(upload, out NSError error);

        var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);

        BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);

        if (error != null)
            Console.WriteLine($"Could not schedule BGProcessingTask: {error}");
        if (refreshError != null)
            Console.WriteLine($"Could not schedule BGAppRefreshTask: {refreshError}");
    }

上传UploadService的机制是使用NSUrlSession,它还编写了一个临时文件来使用应该在后台工作的CreateUploadTask(request, NSUrl.FromFilename(tempFileName)),整个机制如下:

代码语言:javascript
复制
 public NSUrlSession uploadSession;

    public async void EnqueueUpload()
    {
        var accountsTask = await App.PCA.GetAccountsAsync();
        var authResult = await App.PCA.AcquireTokenSilent(App.Scopes, accountsTask.First())
                                      .ExecuteAsync();

        if (uploadSession == null)
            uploadSession = InitBackgroundSession(authResult.AccessToken);

        var datastore = DependencyService.Get<IDataStore<Upload>>();
        var uploads = await datastore.GetUnuploaded();

        foreach (var unUploaded in uploads)
        {
            try
            {
                string folder = unUploaded.Description;
                string subfolder = unUploaded.Category;

                if (string.IsNullOrEmpty(folder) || string.IsNullOrEmpty(subfolder))
                    continue;

                var uploadDto = new Dtos.Upload
                {
                    FolderName = folder,
                    SubFolderName = subfolder,
                    Image = GetImageAsBase64(unUploaded.ImagePath)
                };
                var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                var fileName = Path.GetFileName(unUploaded.ImagePath);
                var tempFileName = Path.Combine(documents, $"{fileName}.txt");
                string stringContent = await new StringContent(JsonConvert.SerializeObject(uploadDto), Encoding.UTF8, "application/json").ReadAsStringAsync();
                await File.WriteAllTextAsync(tempFileName, stringContent);

                using (var url = NSUrl.FromString(UploadUrlString))
                using (var request = new NSMutableUrlRequest(url)
                {
                    HttpMethod = "POST",

                })
                {
                    request.Headers.SetValueForKey(NSObject.FromObject("application/json"), new NSString("Content-type"));
                    try
                    {
                        uploadSession.CreateUploadTask(request, NSUrl.FromFilename(tempFileName));

                    }
                    catch (Exception e)
                    {
                        Console.WriteLine($"NSMutableUrlRequest failed {e.Message}");
                    }
                }
            }
            catch (Exception e)
            {
                if (e.Message.Contains("Could not find a part of the path"))
                {
                    await datastore.DeleteItemAsync(unUploaded.Id);
                    Console.WriteLine($"deleted");
                }

                Console.WriteLine($"uploadStore failed {e.Message}");
            }
        }
    }
    private string GetImageAsBase64(string path)
    {
        using (var reader = new StreamReader(path))
        using (MemoryStream ms = new MemoryStream())
        {
            reader.BaseStream.CopyTo(ms);
            return Convert.ToBase64String(ms.ToArray());
        }
    }

    public NSUrlSession InitBackgroundSession(string authToken = null, IDataStore<Upload> dataStore = null)
    {
        Console.WriteLine("InitBackgroundSession");
        using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier))
        {
            configuration.AllowsCellularAccess = true;
            configuration.Discretionary = false;
            configuration.AllowsConstrainedNetworkAccess = true;
            configuration.AllowsExpensiveNetworkAccess = true;
            if (string.IsNullOrWhiteSpace(authToken) == false)
            {
                configuration.HttpAdditionalHeaders = NSDictionary.FromObjectsAndKeys(new string[] { $"Bearer {authToken}" }, new string[] { "Authorization" });
            }

            return NSUrlSession.FromConfiguration(configuration, new UploadDelegate(dataStore), null);
        }
    }
}

public class UploadDelegate : NSUrlSessionTaskDelegate, INSUrlSessionDelegate
{
    public IDataStore<Upload> Datastore { get; }

    public UploadDelegate(IDataStore<Upload> datastore)
    {
        this.Datastore = datastore;
    }
    public override void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error)
    {
        Console.WriteLine(string.Format("DidCompleteWithError TaskId: {0}{1}", task.TaskIdentifier, (error == null ? "" : " Error: " + error.Description)));

        if (error == null)
        {
            ProcessCompletedTask(task);
        }
    }
    public void ProcessCompletedTask(NSUrlSessionTask sessionTask)
    {
        try
        {
            Console.WriteLine(string.Format("Task ID: {0}, State: {1}, Response: {2}", sessionTask.TaskIdentifier, sessionTask.State, sessionTask.Response));

            if (sessionTask.Response == null || sessionTask.Response.ToString() == "")
            {
                Console.WriteLine("ProcessCompletedTask no response...");
            }
            else
            {
                var resp = (NSHttpUrlResponse)sessionTask.Response;
                Console.WriteLine("ProcessCompletedTask got response...");
                if (sessionTask.State == NSUrlSessionTaskState.Completed && resp.StatusCode == 201)
                {
                    Console.WriteLine("201");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ProcessCompletedTask Ex: {0}", ex.Message);
        }
    }
    public override void DidBecomeInvalid(NSUrlSession session, NSError error)
    {
        Console.WriteLine("DidBecomeInvalid" + (error == null ? "undefined" : error.Description));
    }

    public override void DidFinishEventsForBackgroundSession(NSUrlSession session)
    {
        Console.WriteLine("DidFinishEventsForBackgroundSession");
    }
    public override void DidSendBodyData(NSUrlSession session, NSUrlSessionTask task, long bytesSent, long totalBytesSent, long totalBytesExpectedToSend)
    {
    }
}

但是,如果iOS充电器电缆插上电源,一切都会正常工作。我设置了一个网络调试,大量登录到控制台,我可以看到iPhone上什么也没有发生。

iOS上的“低功耗模式”设置是关闭的。

我看过背景执行神秘性,正在设置configuration.Discretionary = false;会话

NSUrlSession 14.4?上拔出iOS充电器电缆时,如何使上传任务启动

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-22 20:29:11

下列工程无需充电电缆:

代码语言:javascript
复制
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
   
    public Action BackgroundSessionCompletionHandler { get; set; }

    public static string UploadTaskId { get; } = "XXX.upload";
    public static NSString UploadSuccessNotificationName { get; } = new NSString($"{UploadTaskId}.success");
    public static string RefreshTaskId { get; } = "XXX.refresh";
    public static NSString RefreshSuccessNotificationName { get; } = new NSString($"{RefreshTaskId}.success");

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        global::Xamarin.Forms.Forms.Init();
        LoadApplication(new App());

        BGTaskScheduler.Shared.Register(UploadTaskId, null, task => HandleUpload(task as BGProcessingTask));
        BGTaskScheduler.Shared.Register(RefreshTaskId, null, task => HandleAppRefresh(task as BGAppRefreshTask));

        return base.FinishedLaunching(app, options);
    }

    public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
    {
        AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
        return true;
    }

    public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
    {
        Console.WriteLine("HandleEventsForBackgroundUrl");
        BackgroundSessionCompletionHandler = completionHandler;
    }

    public override void OnActivated(UIApplication application)
    {
        Console.WriteLine("OnActivated");
        var uploadService = new UploadService();
        uploadService.EnqueueUpload();
    }

    public override void OnResignActivation(UIApplication application)
    {
        Console.WriteLine("OnResignActivation");
    }

    private void HandleAppRefresh(BGAppRefreshTask task)
    {
        task.ExpirationHandler = () =>
        {
            Console.WriteLine("BGAppRefreshTask ExpirationHandler");

            var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
            BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);

            if (refreshError != null)
                Console.WriteLine($"BGAppRefreshTask ExpirationHandler Could not schedule BGAppRefreshTask: {refreshError}");
        };

        HandleUpload(task);
    }

    public override void DidEnterBackground(UIApplication application) => ScheduleUpload();

    private void HandleUpload(BGTask task)
    {
        Console.WriteLine("HandleUpload");
        var uploadService = new UploadService();
        uploadService.EnqueueUpload();
        task.SetTaskCompleted(true);
    }

    private void ScheduleUpload()
    {
        Console.WriteLine("ScheduleUpload");
        var upload = new BGProcessingTaskRequest(UploadTaskId)
        {
            RequiresNetworkConnectivity = true,
            RequiresExternalPower = false
        };

        BGTaskScheduler.Shared.Submit(upload, out NSError error);

        var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
        BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);

        if (error != null)
            Console.WriteLine($"Could not schedule BGProcessingTask: {error}");
        if (refreshError != null)
            Console.WriteLine($"Could not schedule BGAppRefreshTask: {refreshError}");
    }
}

然后上传服务:

代码语言:javascript
复制
public class UploadService : IUploadService
{
    private const string uploadUrlString = "https://Yadyyadyyada";

    public async void EnqueueUpload()
    {
        var accountsTask = await App.PCA.GetAccountsAsync();
        var authResult = await App.PCA.AcquireTokenSilent(App.Scopes, accountsTask.First())
                                      .ExecuteAsync();

                try
                {
                    var uploadDto = new object();

                    var message = new HttpRequestMessage(HttpMethod.Post, uploadUrlString);
                    message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
                    message.Content = new StringContent(JsonConvert.SerializeObject(uploadDto), Encoding.UTF8, "application/json");

                    var response = await httpClient.SendAsync(message);
                    if (response.IsSuccessStatusCode)
                    {
                        var json = await response.Content.ReadAsStringAsync();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine($"EnqueueUpload {e.Message}");
                }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66690183

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档