iOS 和 Android 的后台定时上传任务

[复制链接]
回帖奖励 6 金钱      回复本帖可获得 2 金钱奖励! 每人限 1 次
查看313 | 回复2 | 4 天前 | 显示全部楼层 |阅读模式

本帖最后由 qq243559086 于 2024-10-18 13:50 编辑

本帖最后由 qq243559086 于 2024-10-18 13:48 编辑

本帖最后由 qq243559086 于 2024-10-18 13:45 编辑

爱星物联APP在使用的过程中会把一些性能和崩溃日志存储在本地,并在手机空闲的时候上传到平台。那么该如何定时后台上传日志呢。

在iOS平台上实现起来比较简单:

// 这个是后台id,注意要把后台id配置到plist文件中,Key为: BGTaskSchedulerPermittedIdentifiers
static NSString *const kBackgroundUploadId = @"your.bundle.id.LogBackgroundUpload";
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kBackgroundUploadId];
conf.HTTPMaximumConnectionsPerHost = 1;
conf.discretionary = YES;
conf.sessionSendsLaunchEvents = YES;
conf.waitsForConnectivity = YES;
// 先创建一个后台Session
NSURLSession *bgSession = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:nil];

// 创建一个请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[API_BASE stringByAppendingString:@"/v1/log/upload"]] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30];
request.allowsCellularAccess = NO;
if (@available(iOS 13.0, *)) {
    request.allowsExpensiveNetworkAccess = NO;
}
[request setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
request.HTTPMethod = @"POST";
// 创建定时任务
// 在 iOS 中,为 background session 创建 upload task 时,系统会将文件复制到临时目录,然后从临时目录上传。
NSURLSessionUploadTask *task = [bgSession uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:path]];
// 这里是定时,必须是后台session才有效,在任务完成的回调中重新创建一个新的定时任务,就能形成一个循环了
task.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:time];
// 启动任务
[task resume];

// UIApplicationDelegate方法 , app被杀掉,后台任务完成会来这里后台启动app
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
{
handleEventsCompletion = completionHandler;
// 这里重建上面的bgSession 即可
}

// NSURLSessionDelegate方法,所有的后台任务都完成了会来这里。-application:handleEventsForBackgroundURLSession:completionHandler: 方法被触发才会有此回调
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (handleEventsCompletion) {
            handleEventsCompletion();
            handleEventsCompletion = nil;
        }
    });
}

// NSURLSessionDelegate方法,这里可以判断任务是否完成,创建下一个定时任务
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{}

iOS使用的是系统后台Session API,这种是最保险的,如果使用的是第三方库,也要看其是否最终使用的系统后台Session API,否则很难在后台持续运行。

Android引入 androidx.work:work-runtime 框架实现也不复杂:

// 定义一个后台定时任务类,Worker来自 androidx.work:work-runtime库
public class UploadWorker extends Worker {
    public UploadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        // 这里上传文件,成功了返回,Result.success(),失败了返回 Result.failure(),或者 Result.retry()重试
        // 这里的同步上传文件我就省略了,最好不要异步上传
        return Result.success();
    }

    @Override
    public void onStopped() {
        super.onStopped();
// 自己中断任务
    }

    String LOG_UPLOAD_WORKER; // 任务id
    // 启动定时任务
    public static void startWork(Context context) {
        LOG_UPLOAD_WORKER = context.getPackageName() + ".LogUploadWorker";
        // 获取任务管理器
        WorkManager wm = WorkManager.getInstance(context);
        // ListenableFuture<List<WorkInfo>> workInfos = wm.getWorkInfosForUniqueWork(LOG_UPLOAD_WORKER);
        // List<WorkInfo> list = null;
        // try {
        //     list = workInfos.get(); // 这里是获取还未完成的任务,有相同的就不要创建新任务了
        // } catch (ExecutionException | InterruptedException ignored) {}

        // 工作条件
        Constraints constraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.UNMETERED) // 仅WiFi下工作
                .setRequiresBatteryNotLow(true) // 低电量不工作
                .setRequiresDeviceIdle(true) // 在待机状态(空闲状态)下执行,需要 API 23,国内最好不用,有可能无法唤醒任务
                .build();

//        OneTimeWorkRequest.from(UploadWorker.class); // 一次性任务
        long interval = 15; // 定时的时间间隔,androidx.work:work-runtime 库规定必须大于等于15分钟
        // 定时任务
        PeriodicWorkRequest uploadWorkRequest = new PeriodicWorkRequest.Builder(UploadWorker.class, interval, TimeUnit.MINUTES)
                .setConstraints(constraints)
//                .setInitialDelay(10, TimeUnit.MINUTES) // 第一次延迟
//                .addTag(LOG_UPLOAD_WORKER)
//                .setId(UUID.fromString(""))
//                .setInputData()
                .build();

        // 启动一次性任务
//        wm.enqueueUniqueWork(LOG_UPLOAD_WORKER, ExistingWorkPolicy.KEEP, uploadWorkRequest);
        // 启动定时任务,上面的 doWork 会被定时调用
        wm.enqueueUniquePeriodicWork(LOG_UPLOAD_WORKER, ExistingPeriodicWorkPolicy.UPDATE, uploadWorkRequest);
    }
}

由于国内的Android系统对后台任务杀的比较严重,此方案也得多测试衡量使用。若是各位大神有更好的方法,还请不吝赐教。

回复

使用道具 举报

lazy | 4 天前 | 显示全部楼层

回帖奖励 +2 金钱

不错呢
回复

使用道具 举报

回帖奖励 +2 金钱

不错不错
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则