本帖最后由 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系统对后台任务杀的比较严重,此方案也得多测试衡量使用。若是各位大神有更好的方法,还请不吝赐教。 |