问题背景
最近有需求,在APP启动后,退到后台,还要能实现周期获取位置信息上报服务器,研究了一下实现方案。
问题分析
一、APP退到后台后网络请求实现
APP退到后台后,实现周期循环发送网络请求。目前尝试了两种方案是OK,如下:
(1)AlarmManager + 前台服务 +广播的方案,可以正常实现,大体思路是,启动一个前台服务,使用AlarmManager发起一个定时广播,然后广播接收器接收到广播后,循环去执行service的操作。
(2)使用jetpeck库提供的worker实现,基于PeriodicWorkRequest实现一个周期执行的任务,比如周期设置为15分钟,可以在后台稳定执行。
二、APP退到后台后获取地理位置实现
APP申请位置时,用户选择了列表中的始终允许后,APP在后台是可以正常获取到位置信息的。不过这里有个坑,因为安卓11版本后退位置信息管控策略进行了更新,如果APP的target sdk版本大于29时,需要分别申请前台位置权限和后台位置权限,本APPtargetsdk是小于等于29的,可以同时申请前台位置权限和后台位置权限。(compileSdkVersion小于29时,会没有这个后台位置权限,需要最好升级到29)
问题解决
下面展示大概的代码,可以参考实现。
(1)引入依赖
api('androidx.work:work-runtime:2.0.1')
(2)manifest文件中增加申请权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 这个权限用于访问GPS定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
(3)权限申请(同时申请位置权限和后台位置权限)
RxPermissionHelper helper = new RxPermissionHelper(this);
helper.requestEach(new RxPermissionHelper.PermissionCallback() {
@Override
public void granted(String permissionName) {
LogUtil.writerLog("ACCESS_FINE_LOCATION granted");
}
@Override
public void denied(String permissionName, boolean forever) {
}
@Override
public void result(boolean allGranted) {
}
}, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION);
(4)work类:去执行前台服务
/**
* work类执行定时任务
*/
public class SimpleWorker extends Worker {
private CurPosUtil curPosUtil;
public SimpleWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Log.d("baorant", "执行调度任务");
LogUtil.writerLog("执行调度任务");
startService();
return Result.success();
}
private void startService() {
// curPosUtil = new CurPosUtil(getApplicationContext());
Intent intent = new Intent(getApplicationContext(), RecordService.class);
getApplicationContext().startService(intent);
}
}
(5)RecordService前台服务类(需要在manifest文件中配置)
/**
* 一个定时任务
*
* 方案:使用前台服务去执行网络请求,定时发送广播,然后在广播接收器中重新启动服务,实现循环后台服务。
*/
public class RecordService extends Service {
private CurPosUtil curPosUtil;
/**
* 每30秒更新一次数据
*/
private static final int ONE_Miniute= 30 * 1000;
private static final int PENDING_REQUEST=0;
int count = 0;
public RecordService() {
}
@Override
public void onCreate() {
super.onCreate();
curPosUtil = new CurPosUtil(getApplicationContext());
LogUtil.writerLog("RecordService onCreate");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String NOTIFICATION_CHANNEL_ID = "package_name";
String channelName = "My Background Service";
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,channelName, NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this,NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_dial_icon) // the status icon
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentText("定时服务正在运行") // the contents of the entry
.build();
startForeground(2, notification);
}
}
/**
* 调用Service都会执行到该方法
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.writerLog("RecordService: onStartCommand");
// 这里模拟后台操作
initPos();
return super.onStartCommand(intent, flags, startId);
}
private void initPos() {
curPosUtil = new CurPosUtil(this);
curPosUtil.getCurPos(new CurPosUtil.CurPosCallback() {
@Override
public void getCurPos(double s, double s1, String s2) {
LogUtil.writerLog(DateUtil.timeToDate(String.valueOf(System.currentTimeMillis())));
LogUtil.writerLog("getCurPos: " + s + " " + s1 + " " + s2);
commonLogin(s + " " + s1 + " " + s2);
}
});
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
public void commonLogin(String position) {
RetrofitHelper.getInstance().login(position, "", "", "",
"", "", "", "")
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean aBoolean) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
(6)activity中启动周期任务,周期15分钟循环执行
PeriodicWorkRequest.Builder request =
new PeriodicWorkRequest.Builder(SimpleWorker.class, 15
, TimeUnit.MINUTES).addTag("simpleTask");
LogUtil.writerLog(DateUtil.timeToDate(String.valueOf(System.currentTimeMillis())));
LogUtil.writerLog("点击执行task");
WorkManager.getInstance().enqueue(request.build() );
(7)LogUtil工具类,输出日志到文件,方便定位文章来源:https://www.toymoban.com/news/detail-603555.html
/**
* 日志工具,输出日志到文件
*/
public class LogUtil {
/**
* 路径 "/data/data/com包名/files/backLogTest"
*
* @param msg 需要打印的内容
*/
public static void writerLog(String msg) {
Log.d("baorant", msg);
// 保存到的文件路径
final String filePath = App.getContext().getFilesDir().getPath();
FileWriter fileWriter;
BufferedWriter bufferedWriter = null;
try {
// 创建文件夹
File dir = new File(filePath, "backLogTest");
if (!dir.exists()) {
dir.mkdir();
}
// 创建文件
File file = new File(dir, "lowTemperature.txt");
if (!file.exists()) {
file.createNewFile();
}
// 写入日志文件
fileWriter = new FileWriter(file, true);
bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write( msg + "=======时间 :"+ getCurrentTime()+ "\n");
bufferedWriter.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static String getCurrentTime() {
Calendar calendar = Calendar.getInstance();
@SuppressLint("SimpleDateFormat")
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(calendar.getTime());
}
}
问题总结
运行结果如下:
如结果所示,基于该方案可以实现APP在后台,周期循环获取位置信息并进行上报,有兴趣的同学可以进一步深入研究。文章来源地址https://www.toymoban.com/news/detail-603555.html
到了这里,关于安卓开发后台应用周期循环获取位置信息上报服务器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!