Android位置服务和应用权限

news2024/11/26 0:47:25

Github:https://github.com/MADMAX110/Odometer

一、使用位置服务

之前的Odometer应用是显示一个随机数,现在要使用Android的位置服务返回走过的距离。
修改getDiatance方法使其返回走过的距离,为此要用Android的位置服务。这些服务允许你得到用户的当前位置,请求定期更新,并在用户进入一个特定位置指定半径范围内时请求触发一个意图。
步骤:
1、声明需要又使用位置服务的权限
2、创建服务时建立一个位置监听器
3、请求位置更新
4、计算走过的距离
5、在撤销服务之前删除位置更新

1、声明你需要的权限

Android默认地允许你完成很多动作,不过有些动作需要的得到用户的许可才能完成。这可能是因为它们使用了用户的个人信息,或者可能会影响存储的数据,也可能会影响其他应用的工作。
位置服务就是需要得到用户授予应用权限才能使用的服务之一。
修改AndroidManifest.xml增加以下声明:
分别是请求精确位置和模糊位置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <application>
        .......
    </application>

</manifest>

2、为OdometerService增加位置监听器

要实现LocationListener接口来创建位置监听器。
这个接口有4个方法需要你定义:
onLocationChanged, onPreviderEnabled, onProviderDisabled, onStatusChanged。
要在第一次创建OdometerService时建立一个位置监听器,所以要在OdometerService的onCreate方法中实现这个接口。
将下列代码加入OdometerService

    private LocationListener listener;

    @Override
    public void onCreate() {
        super.onCreate();
        listener = new LocationListener() {
            @Override
            public void onLocationChanged(@NonNull Location location) {

            }

        };
    }

3、请求位置更新

要得到位置更新需要做三件事:
创建一个位置管理器来访问Android的位置服务;
指定一个位置提供器;
请求这个位置提供者将用户当前位置的定期更新发送到我们在上一页增加的位置监听器。

创建位置管理器

要使用getSystemService方法来创建

locManager = (LocationManager) getSystemService(Context.LOCALE_SERVICE);
指定位置提供者

指定位置提供者用来确定用户的位置。有两个主要选项GPS和network。
GPS选项使用设备的GPS传感器确定用户的位置,网络选项会利用Wifi、蓝牙或移动网络。
并不是所有设备都同时又这两类位置提供者,所以可以使用位置管理器的getBestProvider方法得到设备上最准确的位置提供者,这个方法有两个参数:一个Criteria对象(可以用来指定电量需求之类的标准),以及一个标志(指示当前是否应当在设备上启用)。

String provider = locManager.getBestProvider(new Criteria(), true);
请求位置更新

要用位置管理器的requestLocationUpdates方法让位置提供者将更新发送到位置监听器。
这个方法有4个参数:位置提供者、更新的最小时间间隔,位置更新之间的最小距离,你希望接收这些更新的位置监听器。

//检查是否有权限
if (ContextCompat.checkSelfPermission(this, PERMISSION_STRIMG)
                        == PackageManager.PERMISSION_GRANTED){
            String provider = locManager.getBestProvider(new Criteria(), true);
            if (provider != null)
                locManager.requestLocationUpdates(provider, 1000, 1, listener);
        }
更新的OdometerService代码
package com.hafd.odometer;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.IBinder;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.content.PackageManagerCompat;

import java.util.Random;

public class OdometerService extends Service {

    private final IBinder binder = new OdometerBinder();
    private final Random random = new Random();

    //创建一个绑定式服务时,需要提供一个Binder实现
    public class OdometerBinder extends Binder {
        //活动将使用这个方法得到OdometerService的一个引用
        OdometerService getOdometer() {
            return OdometerService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public double getDistance() {
        return random.nextDouble();
    }

    private LocationListener listener;
    private LocationManager locManager;
    public static final String PERMISSION_STRIMG =
            Manifest.permission.ACCESS_FINE_LOCATION;
    @SuppressLint("ServiceCast")
    @Override
    public void onCreate() {
        super.onCreate();
        listener = new LocationListener() {
            @Override
            public void onLocationChanged(@NonNull Location location) {

            }

        };
        locManager = (LocationManager) getSystemService(Context.LOCALE_SERVICE);

        if (ContextCompat.checkSelfPermission(this, PERMISSION_STRIMG)
                        == PackageManager.PERMISSION_GRANTED){
            String provider = locManager.getBestProvider(new Criteria(), true);
            if (provider != null)
                locManager.requestLocationUpdates(provider, 1000, 1, listener);
        }

    }
}

4、计算走过的距离

可以使用Location对象的diatanceTo方法来得到两个位置之间的距离

    private static double distanceInMeters;
    private static Location lastLocation = null;
    public void onCreate() {
        super.onCreate();
        listener = new LocationListener() {
            @Override
            public void onLocationChanged(@NonNull Location location) {
                if (location == null)lastLocation = location;
                distanceInMeters += location.distanceTo(lastLocation);
                lastLocation = location;
            }

        };
    }
    
    public double getDistance() {
        return this.distanceInMeters;
    }

5、停止监听器继续获取位置更新

    public void onDestroy() {
        super.onDestroy();
        if (locManager != null && listener != null) {
            if (ContextCompat.checkSelfPermission(this, PERMISSION_STRIMG) 
                    == PackageManager.PERMISSION_GRANTED){
                locManager.removeUpdates(listener);
            }
            locManager = null;
            listener = null;
        }
    }

完整的OdometerService.java

package com.hafd.odometer;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.IBinder;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

public class OdometerService extends Service {

    private static double distanceInMeters;
    private static Location lastLocation = null;
    private final IBinder binder = new OdometerBinder();
    private LocationListener listener;
    private LocationManager locManager;
    public static final String PERMISSION_STRIMG =
            Manifest.permission.ACCESS_FINE_LOCATION;

    //创建一个绑定式服务时,需要提供一个Binder实现
    public class OdometerBinder extends Binder {
        //活动将使用这个方法得到OdometerService的一个引用
        OdometerService getOdometer() {
            return OdometerService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public double getDistance() {
        return this.distanceInMeters;
    }

    @SuppressLint("ServiceCast")
    @Override
    public void onCreate() {
        super.onCreate();
        listener = new LocationListener() {
            @Override
            public void onLocationChanged(@NonNull Location location) {
                if (location == null)lastLocation = location;
                distanceInMeters += location.distanceTo(lastLocation);
                lastLocation = location;
            }

        };
        locManager = (LocationManager) getSystemService(Context.LOCALE_SERVICE);

        if (ContextCompat.checkSelfPermission(this, PERMISSION_STRIMG)
                        == PackageManager.PERMISSION_GRANTED){
            String provider = locManager.getBestProvider(new Criteria(), true);
            if (provider != null)
                locManager.requestLocationUpdates(provider, 1000, 1, listener);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (locManager != null && listener != null) {
            if (ContextCompat.checkSelfPermission(this, PERMISSION_STRIMG)
                    == PackageManager.PERMISSION_GRANTED){
                locManager.removeUpdates(listener);
            }
            locManager = null;
            listener = null;
        }
    }
}

二、让应用请求权限

现在的应用必须在系统设置里面手动为这个应用开启位置权限,如何要让应用自己请求权限呢?

检查并请求权限

修改代码使得只有当用户授予了必要的权限时MainActivity才绑定到这个服务,这个权限由OdometerService中定义的PERMISSION_STRING常量指定。如果没有授予权限就请求这个权限。
在MainActivity更新onStart方法。

    @Override
    protected void onStart() {
        super.onStart();
        //如果没有授权就请求权限
        if (ContextCompat.checkSelfPermission(this, OdometerService.PERMISSION_STRIMG) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,
                    new String[]{OdometerService.PERMISSION_STRIMG}, PERMISSION_REQUEST_CODE);
        }else {
            Intent intent = new Intent(this, OdometerService.class);
            bindService(intent, connection, Context.BIND_AUTO_CREATE); 
        }
        
    }

检查用户对权限请求的响应

使用requestPermissions方法请求用户授予一个权限时,不能通过检查它的返回值来确定是否授予了权限。这是因为权限请求是异步发生的,当你等待用户响应时并不会阻塞当前线程。
正确的做法是要检查用户的响应,需要覆盖活动的onRequestPermissionsResult方法。这个方法有3个参数:
一个标识权限请求的int请求代码、
一个权限String数组、
以及一个对应请求结果的int数组。
使用这个方法时,首先检查int请求代码是否与requestPermissions方法中使用的代码一致。如果一致则检查是否已经授予这个权限。

    @SuppressLint("MissingSuperCall")
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults){
        switch (requestCode) {
            case PERMISSION_REQUEST_CODE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Intent intent = new Intent(this, OdometerService.class);
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);
                }else {
                    
                }
            }
        }
    }

如果拒绝授予就发出一个通知

如果用户不授予权限,不允许使用他们当前位置,OdometerService就无法得出他们走了多远。这里使用通知告知用户。
先增加两个字符串:

    <string name="app_name">Odometer</string>
    <string name="permission_denied">Location permission required</string>

再将下列代码增加到上一节空白的else语句中:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                            .setSmallIcon(android.R.drawable.ic_menu_compass)
                            .setContentTitle(getResources().getString(R.string.app_name))
                            .setContentText(getResources().getString(R.string.permission_denied))
                            .setPriority(NotificationCompat.PRIORITY_HIGH)
                            .setVibrate(new long[] {1000, 1000})
                            .setAutoCancel(true);
                    Intent actionIntent = new Intent(this, MainActivity.class);
                    PendingIntent actionPendingIntent = PendingIntent.getActivity(
                            this,
                            0,
                            actionIntent,
                            PendingIntent.FLAG_UPDATE_CURRENT);
                    builder.setContentIntent(actionPendingIntent);

                    NotificationManager notificationManager =
                            (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    notificationManager.notify(NOTIFICATION_ID, builder.build());

完整的MainActivity

package com.hafd.odometer;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;

import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.widget.TextView;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private OdometerService odometer;
    private boolean bound = false;
    private final int PERMISSION_REQUEST_CODE = 698;
    private final int NOTIFICATION_ID = 423;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            OdometerService.OdometerBinder odometerBinder = (OdometerService.OdometerBinder) iBinder;
            odometer = odometerBinder.getOdometer(); //使用IBinder得到服务的一个引用
            bound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            bound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        displayDistance();
    }

    @SuppressLint({"MissingSuperCall", "NotificationPermission"})
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults){
        switch (requestCode) {
            case PERMISSION_REQUEST_CODE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Intent intent = new Intent(this, OdometerService.class);
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);
                }else {
                    NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                            .setSmallIcon(android.R.drawable.ic_menu_compass)
                            .setContentTitle(getResources().getString(R.string.app_name))
                            .setContentText(getResources().getString(R.string.permission_denied))
                            .setPriority(NotificationCompat.PRIORITY_HIGH)
                            .setVibrate(new long[] {1000, 1000})
                            .setAutoCancel(true);
                    Intent actionIntent = new Intent(this, MainActivity.class);
                    PendingIntent actionPendingIntent = PendingIntent.getActivity(
                            this,
                            0,
                            actionIntent,
                            PendingIntent.FLAG_UPDATE_CURRENT);
                    builder.setContentIntent(actionPendingIntent);

                    NotificationManager notificationManager =
                            (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    notificationManager.notify(NOTIFICATION_ID, builder.build());
                }
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        //如果没有授权就请求权限
        if (ContextCompat.checkSelfPermission(this, OdometerService.PERMISSION_STRIMG) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,
                    new String[]{OdometerService.PERMISSION_STRIMG}, PERMISSION_REQUEST_CODE);
        }else {
            Intent intent = new Intent(this, OdometerService.class);
            bindService(intent, connection, Context.BIND_AUTO_CREATE);
        }

    }

    protected void onStop() {
        super.onStop();
        if (bound) {
            unbindService(connection);
            bound = false;
        }
    }

    private void displayDistance() {
        final TextView distanceView = (TextView) findViewById(R.id.distance);
        final Handler handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                double distance = 0.0;
                //如果得到OdometerService的一个引用,而且绑定到这个服务,则调用getDistance。
                if (bound && odometer != null)distance = odometer.getDistance();
                String distanceStr = String.format(Locale.getDefault(), "%1$,.2f miles", distance);
                distanceView.setText(distanceStr);
                //每秒运行一次
                handler.postDelayed(this, 1000);
            }
        });
    }
}

试一试

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当10年程序员是什么体验?存款几位数?

最近网上一个话题吸引了许多人的讨论&#xff0c;当10年程序员&#xff0c;是一种什么体验&#xff1f; 都说程序员的高收入和工作年限应该成正比&#xff0c;真的是这样吗&#xff1f;工作10年的程序员&#xff0c;工资应该是什么水平&#xff1f;不少网友纷纷“现身说法”..…

qt中json类

目录 QJsonValue QJsonObject QJsonArray QJsonDocument 案例&#xff1a; Qt 5.0开始提供了对Json的支持&#xff0c;我们可以直接使用Qt提供的Json类进行数据的组织和解析&#xff0c;下面介绍4个常用的类。 QJsonValue 该类封装了JSON支持的数据类型。 布尔类型&#xf…

希尔贝壳受邀参加《人工智能开发平台通用能力要求 第4部分:大模型技术要求》标准第一次研讨会

随着大模型技术与经验的不断累积&#xff0c;该方向也逐渐从聚焦技术突破&#xff0c;到关注开发、部署、应用的全流程工程化落地。为完善人工智能平台标准体系建设&#xff0c;满足产业多样化需求&#xff0c;2023年9月7日&#xff0c;中国信通院云大所在线上召开《人工智能开…

C# Thread.Sleep(0)有什么用?

一、理论分析 回答这个要先从线程时间精度&#xff08;时间片&#xff09;开始说起。很多参考书说&#xff0c;默认情况下&#xff0c;时间片为15ms 左右&#xff0c;但是这是已经过时的知识。在老的 Windows 操作系统里&#xff0c;应用程序模式时时间片 15ms 左右&#xff0…

Jwt简介+工具类应用+Jwt集成spa项目

目录 一、Jwt简介 1.1 Jwt是什么 1.2 为什么使用Jwt 1.3 Jwt的工作原理 1.4 Jwt的组成 1.5 Jwt的验证过程 1.6 JWT令牌刷新思路 二、Jwt工具类 2.1 Jwt工具类是什么 2.2 Jwt工具类的使用 2.2.1 生成Jwt 2.2.2 解析Jwt 2.2.3 复制JWT并延时30分钟 2.2.4 测试JWT的有…

修复画笔工具组

修复画笔工具组 修复画笔工具组包括污点修复画笔工具、修复画笔工具、修补工具、内容感知移动工具和红眼工具&#xff0c;主要用于图像的修复或修补。 一、污点修复画笔工具 污点修复画笔工具可以去除图像中的污点、裂痕等不理想的部分&#xff0c;将其用与周围相似的图形来填充…

pc端使用微信扫码登录(思路篇)

我们在PC端网页中登录的时候有些需要微信扫码登录 例如CSDN网站登录 扫描之后 需要关注公众号 关注公众号就登录成功了 如何实现 流程图 步骤 1.pc端点击登录向业务服务器请求要登录二维码 2.业务服务器拿到用户端唯一参数或socketId&#xff08;使用websocket连接&#x…

10款精选的后台管理系统

1.vue2-manage 此项目是 vue element-ui 构建的后台管理系统&#xff0c;是后台项目node-elm 的管理系统&#xff0c;所有的数据都是从服务器实时获取的真实数据&#xff0c;具有真实的注册、登陆、管理数据、权限验证等功能。 项目地址&#xff1a;https://github.com/baili…

NSDT孪生编辑器助力智慧城市

技术有能力改变城市的运作方式&#xff0c;提高效率&#xff0c;为游客和居民提供更好的体验&#xff0c;实现更可持续的运营和更好的决策。 当今城市面临的主要挑战是什么&#xff0c;成为智慧城市如何帮助克服这些挑战&#xff1f; 我们生活在一个日益城市化的世界&#xf…

【Java 进阶篇】JavaScript Array数组详解

当我们编写JavaScript代码时&#xff0c;经常需要处理一组数据。JavaScript中的数组&#xff08;Array&#xff09;是一种用于存储多个值的数据结构&#xff0c;它提供了许多方法和功能&#xff0c;使我们能够方便地操作这些数据。在本篇博客中&#xff0c;我们将详细探讨JavaS…

【Linux初阶】多线程2 | 分离线程,线程库,线程互斥,可重入VS线程安全,锁的常见概念

文章目录 ☀️一、分离线程&#x1f33b;1.pthread_ self - 获取线程ID&#x1f33b;2.线程分离 ☀️二、用户级线程库&#x1f33b;1.pthread_t&#x1f33b;2.理解用户级线程库 - pthread库&#x1f33b;3.局部存储 ☀️三、线程互斥&#x1f33b;1.线程间的互斥相关概念&…

【Kali】简单记录

文章目录 信息收集DNS记录分析hostdigdnsenum 路由信息tcptraceroutetctrace 搜索引擎 目标识别arpingfping 识别操作系统p0f 服务枚举端口扫描nmap识别VPN服务器 漏洞映射exploitdbmsfconsole 提权arpspoofDsniff 信息收集 DNS记录分析 host host www.example.com host -a …

Windows 多媒体编程库 DirectX 介绍

目录 1、什么是DirectX&#xff1f; 2、使用DirectX的好处 2.1、DirectX为软件开发者提供硬件无关性 2.2、为硬件开发提供策略 3、DirectX的主体构成 3.1、Direct3D 3.2、DirectDraw 3.3、DirectPlay 3.4、DirectSound 3.5、DirectMusic 3.6、DirectInput 4、Dire…

Python数据分析实战-实现T检验(附源码和实现效果)

实现功能 T 检验&#xff08;Students t-test&#xff09;是一种常用的统计方法&#xff0c;用于比较两个样本之间的均值是否存在显著差异。它可以应用于许多场景&#xff0c;其中一些常见的应用场景包括&#xff1a; A/B 测试&#xff1a;在市场营销和用户体验研究中&#xf…

【mmdetection代码解读 3.x版本】FPN层的解读

文章目录 1. forward函数1.1 self.fpn_convs的构建 1. forward函数 def forward(self, inputs: Tuple[Tensor]) -> tuple:assert len(inputs) len(self.in_channels)# build laterals laterals [lateral_conv(inputs[i self.start_level])for i, lateral_conv in enumer…

QMidi Pro for Mac:打造您的专属卡拉OK体验

你是否曾经厌倦于在KTV里与朋友们争夺麦克风&#xff1f;是否想要在家中享受自定义的卡拉OK体验&#xff1f;现在&#xff0c;有了QMidi Pro for Mac&#xff0c;一切变得简单而愉快&#xff01; QMidi Pro是一款功能强大的卡拉OK播放器&#xff0c;专为Mac用户设计。它充分利…

机器学习(二)什么是机器学习

文章目录 什么是机器学习1.4.1确定是否为机器学习问题 1.5基于规则学习和基于模型的学习1.5.1基于规则学习1.5.2基于模型学习1.5.3房价预测问题 1.6机器学习数据的基本概念1.6.1机器学习数据集基本概念强化实践 后记 什么是机器学习 在开始讲解术语概念之前我们首先梳理下之前…

互联网Java工程师面试题·Java 并发编程篇·第三弹

目录 26、什么是线程组&#xff0c;为什么在 Java 中不推荐使用&#xff1f; 27、为什么使用 Executor 框架比使用应用创建和管理线程好&#xff1f; 27.1 为什么要使用 Executor 线程池框架 27.2 使用 Executor 线程池框架的优点 28、java 中有几种方法可以实现一个线程…

天猫用户重复购买预测(速通二)

天猫用户重复购买预测&#xff08;二&#xff09; 模型训练分类相关模型1、逻辑回归分类模型2、K近邻分类模型3、高斯贝叶斯分类模型4、决策树分类模型5、集成学习分类模型 模型验证模型验证指标 特征优化特征选择技巧1、搜索算法2、特征选择方法 模型训练 分类相关模型 1、逻…

基于Springboot实现商务安全邮箱邮件收发系统项目【项目源码+论文说明】

基于Springboot实现商务安全邮箱邮件收发系统演示 摘要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。计算机的优势和普及使得商务安全邮箱的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;采用jsp技术…