安卓开发后台应用周期循环获取位置信息上报服务器

问题背景

最近有需求,在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工具类,输出日志到文件,方便定位

/**
 * 日志工具,输出日志到文件
 */
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在后台,周期循环获取位置信息并进行上报,有兴趣的同学可以进一步深入研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/45407.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

随笔--更改已经启动中的容器的配置文件

文章目录 docker 容器的配置信息地址修改文件映射 docker 容器的配置信息地址 # 一般在 sudo su cd /cd /var/lib/docker/containers/{容器id}/ # 查看容器的id,CONTAINER ID就是容器id的前部分 docker ps修改文件映射 进入容器的配置文件位置一般包含这些文件 # 先stop容器…

Docker--harbor私有仓库部署与管理

目录 一、构建私有库 1.下载 registry 镜像 2.在 daemon.json 文件中添加私有镜像仓库地址 3.运行 registry 容器 4.为镜像打标签 5.上传到私有仓库 6.列出私有仓库的所有镜像 7.列出私有仓库的centos镜像有哪些tag 8.测试私有仓库下载 二、Harbor 简介 1.什么是Harb…

iOS--属性关键字

定义 chat&#xff1a; 在iOS开发中&#xff0c;属性关键字是用于声明类的属性&#xff08;实例变量&#xff09;的修饰符。属性关键字可以影响属性的访问权限、内存管理和生成相关的getter和setter方法。 属性关键字有哪些&#xff1f; 分类属性关键字原子性atomic、nonato…

Spring MVC-基础概念(定义+创建和连接+@RequestMappring的描述)

目录 1.什么是Spring MVC&#xff1f; 2. MVC 和 Spring MVC 的关系 3.Spring MVC 项目创建 4. RequestMappring实现用户和程序的映射 4.1 RequestMappring 注解解释 4.2 方法1: RequestMapping(“/xxx”) 4.4 RequestMapping(method xxxx, value “xxx”) 是POST/GET…

协作实现时序数据高效流转链路 | 7.20 IoTDB X RocketMQ 技术沙龙线上直播回顾

7 月 20 日&#xff0c;IoTDB X RocketMQ 技术沙龙线上直播圆满结束。工业物联网时序数据库研发商天谋科技、云原生事件流平台 Apache RocketMQ 社区的四位技术专家&#xff0c;针对实时数据接入、多样数据处理与系统的高扩展、高可靠特性的数据流转处理平台实现难点&#xff0…

Java日志框架JUL、Log4j、logback、log4j2使用

随着软件系统的发展系统业务越来越多、逻辑越来越复杂、代码量越来越多&#xff0c;伴随着容易出现的bug也会越来越多&#xff0c;不论是开发测试阶段还是生产阶段都需要将这些错误及时的捕捉记录下来&#xff0c;方便解决这些问题&#xff0c;否则针对出现的异常无从下手&…

【三维点云处理】顶点、面片、邻接矩阵、邻接距离矩阵以及稀疏存储概念

文章目录 vts和faces基础知识vertices-节点&#xff08;3是点的三维坐标&#xff09;faces-面片&#xff08;3是构成三角形面片的3个点&#xff09; 邻接矩阵邻接距离矩阵&#xff08;NN500&#xff09;稀疏矩阵 vts和faces基础知识 vertices-节点&#xff08;3是点的三维坐标…

【JavaEE初阶】HTTP协议

文章目录 1. HTTP概述和fiddler的使用1.1 HTTP是什么1.2 抓包工具fiddler的使用1.2.1 注意事项1.2.2 fiddler的使用 2. HTTP协议格式2.1 HTTP请求格式2.1.1 基本格式2.1.2 认识URL2.1.3 方法 2.2 请求报头关键字段2.3 HTTP响应格式2.3.1 基本格式2.3.2状态码 1. HTTP概述和fidd…

【博客682】k8s apiserver bookmarks机制以更高效检测变更

k8s apiserver bookmarks机制以更高效检测变更 list-watch背景&#xff1a; List-Watch 是kubernetes中server和client通信的最核心的机制&#xff0c; 比如说api-server监听etcd&#xff0c; kubelet监听api-server&#xff0c; scheduler监听api-server等等&#xff0c;其实…

任务的创建与删除

Q: 什么是任务&#xff1f; A: 任务可以理解为进程/线程&#xff0c;创建一个任务&#xff0c;就会在内存开辟一个空间。 比如&#xff1a; 玩游戏&#xff0c;打篮球&#xff0c;开车&#xff0c;都可以视为任务。 Windows 系统中的 MarkText 、谷歌浏览器、记事本&#xff0…

SpringBoot2为什么默认使用CGLib不再使用JDK动态代理

SpringBoot2为什么默认使用CGLib不再使用JDK动态代理 CGLib和JDK动态代理对比1. 不需要实现接口2. 性能3. 代理对象的创建4. 调用方法 2. CGLib使用 CGLib和JDK动态代理对比 1. 不需要实现接口 JDK动态代理类要求目标类必须实现接口,而CGLib动态代理可以直接代理普通类(非接口…

C语言中指针(简略复习)

一、指针 1. 指针运算 设p为指针&#xff0c;T为类型&#xff0c;n为整型数字&#xff0c;则pn表示&#xff0c;指针从当前位置向后移动n个数据单位&#xff0c;而不是n个字节。 数据类型不同&#xff0c;移动的数据单位字节数也不同&#xff0c;实际上移动的字节数为n*sizeo…

【windows】连接共享打印机提示:0x0000011B

【问题现象】 添加共享打印机的时候&#xff0c; 提示错误&#xff1a;0x0000011B。 【解决方法】 按winr键&#xff0c;在运行输入regedit 然后在注册表中找到路径&#xff1a; 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print 打开后&#xff0c;在右侧…

【Nodejs】Puppeteer\爬虫实践

puppeteer 文档:puppeteer.js中文文档|puppeteerjs中文网|puppeteer爬虫教程 Puppeteer本身依赖6.4以上的Node&#xff0c;但是为了异步超级好用的async/await&#xff0c;推荐使用7.6版本以上的Node。另外headless Chrome本身对服务器依赖的库的版本要求比较高&#xff0c;c…

[Angular] Import TranslateModule in Angular 16

1.Background Angular 更新至V16版后&#xff0c;支援 standalone&#xff0c;故移除了 NgModule&#xff0c;而TranslateModule 又要在AppModule中 import&#xff0c;那该如何做呢&#xff1f; 2.NPM packages installation npm install ngx-translate/core npm install n…

Pytorch个人学习记录总结 08

目录 神经网络-搭建小实战和Sequential的使用 版本1——未用Sequential 版本2——用Sequential 神经网络-搭建小实战和Sequential的使用 torch.nn.Sequential的官方文档地址&#xff0c;模块将按照它们在构造函数中传递的顺序添加。代码实现的是下图&#xff1a; 版本1—…

Vue入门项目——WebApi

Vue入门——WebApi vue3项目搭建组合式API响应式APIreactive()ref() 生命周期钩子computed计算属性函数watch监听函数父子通信模板引用组合选项 vue3项目搭建 简单看下Vue3的优势吧 下载安装npm及node.js16.0以上版本&#xff08;确保安装成功可用如下代码检查版本&#xff0…

【Docker-compose】基于Docker-compose创建LNMP环境并运行Wordpress网站平台

基于Docker compose创建LNMP环境并运行Wordpress网站平台 1.Docker-Compose概述2. YAML文件格式及编写注意事项3. Docker-Compose配置常用字段4.Docker Compose常用命令5.使用Docker-compose创建LNMP环境&#xff0c;并运行Wordpress网站平台5.1 Docker Compose环境安装5.2 使用…

使用spark进行hbase的bulkload

使用spark进行hbase的bulkload 一、 背景 HBase 是一个面向列&#xff0c;schemaless&#xff0c;高吞吐&#xff0c;高可靠可水平扩展的 NoSQL 数据库&#xff0c;用户可以通过 HBase client 提供的 put get 等 api 实现在数据的实时读写。在过去的几年里&#xff0c;HBase …

主机漏洞利用演示MS17-010(永恒之蓝)

ms17-010危害&#xff1a;对被攻击方的电脑造成蓝屏&#xff01; 申明&#xff1a;本篇文章的用意仅做学习使用 网络搭建环境&#xff1a; 软件&#xff1a;Vmware Workstation 17 攻击机&#xff1a;Kali 靶机环境&#xff1a;Windows 7 Nmap软件的基本功能&#xff1a; …