数据抓取(二)&定位方案:地址信息的获取

本文介绍如何在安卓应用中利用谷歌API获取经纬度和地址信息,包括检查定位服务、动态请求权限、使用LocationUtils工具类以及处理地址解析的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章目录

总篇:数据抓取:抓取手机设备各种数据
分篇(一):【数据抓取(一)】手机通讯录数据获取
分篇(二):数据抓取(二)&定位方案:地址信息的获取
分篇(二):数据抓取(三):免权限获取所有安装的应用程序信息(系统和非系统)


前言

在安卓应用的使用中,定位是一个不可或缺的功能,当然成熟的公司一般都是使用高德SDK这类成熟,经得起考验的轮子来开发。那我们这些没钱 囊中羞涩,但是又想体验一把定位功能的友友们该怎么办,其实谷歌已经提供了一套成熟的api供我们使用。本文主要编写关于利用谷歌api获取经纬及详细地址的实现步骤,当然最后也会提供一份已经在使用的工具类给大家。


按照惯例,这里应该上展示图,但是怕被盒,就算了吧(狗头)
在总篇有展示demo,这里也可以下载下来看看(密码azdt):
在这里插入图片描述

一、实现步骤

我们要实现的不只是单纯的获取经纬度,而是在未开启服务时进入app后告知用户去开启服务,同时在用户同意定位权限获取后,即使获取定位信息加以保存。
所以可以分为三个步骤:

  • 检查是否开启定位服务
  • 动态获取定位权限
  • 调用工具类获取定位,并在工具类及时保存定位信息

1.检查是否开启定位服务(通常会在启动Activity进行判断)

  • 获取LocationManager服务
  • 根据服务提供API isProviderEnabled()检查是否开启定位服务
  // 判断是否开启定位服务
    public  boolean isLocationServiceEnabled1(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
        boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        return !isGPSEnabled&&!isNetworkEnabled;
    }

2.根据定位服务是否开启分支执行代码

已开启:检查是否获取定位权限(获取则初始化工具类,未获取则进行动态权限申请)
未开启:跳转定位服务页,开启定位服务
注意:本项目使用XXPermissions三方库简化动态权限申请流程,有兴趣可以了解下

// 检查是否开启GPS
    private void checkGPS(){
        if (!isLocationServiceEnabled(this)) {
            if (checkPermission(this)){
                LocationUtils.getInstance(this);
            }else {
                requestPermission(Permission.ACCESS_COARSE_LOCATION);
            }
        }else {
            LocationUtils.getInstance(this);
        }
    }
    // 检查是否申请权限
    private boolean checkPermission(Context context){
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.d("数据抓取:LocationUtils", "并未申请相关权限");
            return false;
        }
        return true;
    }
    private void requestPermission(String...permission){
        XXPermissions.with(this).permission(permission).request(new OnPermissionCallback() {
            @Override
            public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
                if (!allGranted) {
                    Toast.makeText(MainActivity.this,"获取权限成功", Toast.LENGTH_LONG).show();
                    LocationUtils.getInstance(this);
                }
            }

            @Override
            public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
                if (doNotAskAgain) {
                    Toast.makeText(MainActivity.this,"获取权限被禁止且不再询问",Toast.LENGTH_LONG).show();
                    // 如果是被永久拒绝就跳转到应用权限系统设置页面
                    XXPermissions.startPermissionActivity(MainActivity.this, permissions);
                } else {
                    Toast.makeText(MainActivity.this,"获取权限失败",Toast.LENGTH_LONG).show();
                }
            }
        });
    }

3.工具类LocationUtils获取地址信息

在前面的步骤通过判断是否开启GPS定位服务以及是否申请定位动态权限两步骤之后, 我们使用LocationUtils.getInstance(this);进行定位工具类初始化。
那么工具类LocationUtils又是如何获取地址信息?

  • 先获取LocationManager服务
  • 再通过服务的apigetProviders(true)获取位置提供器
  • 最后可以根据位置提供器获取Location

其中getLastKnownLocation()方法获取上一次定位服务获取的地址,如果一开始就开启GPS定位服务,此方法能够快速获取Location;
反之如果没有开启GPS定位服务,此处便设置requestLocationUpdates()监听器对定位进行监听。

    //获取经纬度location
    private void getLocation(Context context) {
        //1.获取位置管理器
        LocationManager locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
        //2.获取位置提供器,GPS或是NetWork
        List<String> providers = locationManager.getProviders(true);
        Log.d("TAG", "打印位置提供器: ");
        if (providers!=null&&providers.size()!=0){
            for (String proviceder:providers){
                if (locationManager.isProviderEnabled(proviceder)) {
                    Log.d("TAG", "longitude:gps_toOpen" );
                    // 需要检查权限,否则编译报错,想抽取成方法都不行,还是会报错。只能这样重复 code 了。
                    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                        Log.d("数据抓取:LocationUtils", "并未申请相关权限");
                        return;
                    }
                    Location location = locationManager.getLastKnownLocation(proviceder);
                    Log.d("TAG", "getLocation: "+proviceder);
                    if (location!=null){
                        setLocation(location);
                        getAddress(context,location);
                    }
                    locationManager.requestLocationUpdates(proviceder, 5000, 3, new LocationListener() {
                        @Override
                        public void onLocationChanged(@NonNull Location location) {
                            if (location!=null){
                                setLocation(location);
                                getAddress(context,location);
                                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                                    locationManager.removeUpdates(this);
                                }
                            }
                        }

                    });
                }
            }
        }
    }

4.工具类LocationUtils通过SP存取经纬度

该工具类实现单例模式,在第一次初始化时就初始化了SharedPreferences.Editor和SharedPreferences对象。

 @SuppressLint("StaticFieldLeak")
    private volatile static LocationUtils uniqueInstance;
    private Location location;
    private final Context mContext;
    private String tag = "";
    private SharedPreferences preferences;
    private SharedPreferences.Editor edit;
    private LocationUtils(Context context) {
        mContext = context;
        preferences = mContext.getSharedPreferences("locaiton",MODE_PRIVATE);
        edit = preferences.edit();
        getLocation(context);
    }

    //实现单例
    public static LocationUtils getInstance(Context context) {
        if (uniqueInstance == null) {
            synchronized (LocationUtils.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new LocationUtils(context);
                }
            }
        }
        return uniqueInstance;
    }

sp存方法已经在第三步骤调用,接下来贴上取方法

    private void setLocation(Location location) {
        this.location = location;
        if(location!=null){
            setData();
        }
    }
    private void setData(){
        edit.putString("longitude",String.valueOf(location.getLongitude()));
        edit.putString("Latitude",String.valueOf(location.getLatitude()));
        edit.apply();
    }
    public String getLongitude(){
        Log.d("TAG", "splocation_get"+preferences.getString("longitude",""));
        return preferences.getString("longitude","");


    }
    public String getLatitude(){
        Log.d("TAG", "splocation_get"+preferences.getString("Latitude",""));
        return preferences.getString("Latitude","");
    }

二.地址信息类AddressInfo

在上述步骤我们便已经实行了位置获取,但是地址可用信息不止经纬度,我们数据抓取获取的信息也自然还有多个字段,下图展示:

字段名描述
gps_longitude经度
gps_latitude维度
gps_address_street街道
gps_address_province省份
gps_address_city城市
gps_address_country国家
gps_address_countryCode国家代码

AddressInfo代码如下:

public static class AddressInfo{
        private String gps_longitude;// 经度
        private String gps_latitude;// 维度
        private String gps_address_street;// 街道
        private String gps_address_province;// 省份
        private String gps_address_city;// 城市
        private String gps_address_country;// 国家
        private String gps_address_countryCode;// 国家代码

        public AddressInfo() {}

        public String getGps_address_country() {
            return gps_address_country;
        }

        public String getGps_address_countryCode() {
            return gps_address_countryCode;
        }

        public String getGps_longitude() {
            return gps_longitude;
        }

        public String getGps_latitude() {
            return gps_latitude;
        }

        public String getGps_address_street() {
            return gps_address_street;
        }

        public String getGps_address_province() {
            return gps_address_province;
        }

        public String getGps_address_city() {
            return gps_address_city;
        }
    }

那么我们又如何从Location对象中解析出如下数据呢?
通过Geocoder对象的getFromLocation()方法返回一个地址数组,通过对下标数据分析,装入地址信息类。

    public String getAddress(Context context,Location location) {
        List<Address> result;
        try {
            if (location != null) {
                Geocoder gc = new Geocoder(context, Locale.getDefault());
                 // 返回一个地址数组,该数组试图描述给定纬度和经度周围的区域。 
                // 返回的地址应该根据提供给该类构造函数的区域设置进行本地化。
                // 结果可以通过网络查找的方式获得,这个方法可能需要一些时间来返回,因此不应该在主线程上调用。 
                result = gc.getFromLocation(location.getLatitude(),
                        location.getLongitude(), 1);
                for (int i = 0; i < result.size(); i++) {
                    address = result.get(i).toString();
                }
                addressInfo.gps_latitude = String.valueOf(result.get(0).getLatitude());
                addressInfo.gps_longitude = String.valueOf(result.get(0).getLongitude());
                addressInfo.gps_address_street = result.get(0).getAddressLine(2);
                addressInfo.gps_address_province = result.get(0).getAdminArea();
                addressInfo.gps_address_city = result.get(0).getLocality();
                addressInfo.gps_address_country = result.get(0).getAddressLine(0);
                addressInfo.gps_address_countryCode = result.get(0).getCountryCode();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return address;
    }

三、获取地址信息类(在确认定位服务和权限没问题后)

  LocationUtils instance = LocationUtils.getInstance(this);
                LocationUtils.AddressInfo addressInfo = instance.getAddressInfo();

最后附上完整工具类

package com.itaem.datacapture.Utils;// 2023/4/15

import static android.content.Context.LOCATION_SERVICE;
import static android.content.Context.MODE_PRIVATE;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;


import java.util.List;
import java.util.Locale;

/**
 * 获取经纬度、位置工具类
 */
public class LocationUtils {

    @SuppressLint("StaticFieldLeak")
    private volatile static LocationUtils uniqueInstance;
    private Location location;
    private final Context mContext;
    private String tag = "";
    private SharedPreferences preferences;
    private SharedPreferences.Editor edit;
    private LocationUtils(Context context) {
        mContext = context;
        preferences = mContext.getSharedPreferences("locaiton",MODE_PRIVATE);
        edit = preferences.edit();
        getLocation(context);
    }

    //实现单例
    public static LocationUtils getInstance(Context context) {
        if (uniqueInstance == null) {
            synchronized (LocationUtils.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new LocationUtils(context);
                }
            }
        }
        return uniqueInstance;
    }

    //获取经纬度location
    private void getLocation(Context context) {
        //1.获取位置管理器
        LocationManager locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
        //2.获取位置提供器,GPS或是NetWork
        List<String> providers = locationManager.getProviders(true);
        Log.d("TAG", "打印位置提供器: ");
        if (providers!=null&&providers.size()!=0){
            for (String proviceder:providers){
                if (locationManager.isProviderEnabled(proviceder)) {
                    Log.d("TAG", "longitude:gps_toOpen" );
                    // 需要检查权限,否则编译报错,想抽取成方法都不行,还是会报错。只能这样重复 code 了。
                    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                        Log.d("数据抓取:LocationUtils", "并未申请相关权限");
                        return;
                    }
                    Location location = locationManager.getLastKnownLocation(proviceder);
                    Log.d("TAG", "getLocation: "+proviceder);
                    if (location!=null){
                        setLocation(location);
                        getAddress(context,location);
                    }
                    locationManager.requestLocationUpdates(proviceder, 5000, 3, new LocationListener() {
                        @Override
                        public void onLocationChanged(@NonNull Location location) {
                            if (location!=null){
                                setLocation(location);
                                getAddress(context,location);
                                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                                    locationManager.removeUpdates(this);
                                }
                            }
                        }

                    });
                }
            }
        }
    }




    private void setLocation(Location location) {
        this.location = location;
        if(location!=null){
            setData();
        }
    }
    private void setData(){
        edit.putString("longitude",String.valueOf(location.getLongitude()));
        edit.putString("Latitude",String.valueOf(location.getLatitude()));
        edit.apply();
    }
    public String getLongitude(){
        Log.d("TAG", "splocation_get"+preferences.getString("longitude",""));
        return preferences.getString("longitude","");


    }
    public String getLatitude(){
        Log.d("TAG", "splocation_get"+preferences.getString("Latitude",""));
        return preferences.getString("Latitude","");
    }

    //获取经纬度
    public Location showLocation() {
        if (location!=null){
            Log.d("TAG", "经纬度" + location.getLongitude());
            Log.d("TAG", "经纬度" + location.getLatitude());
        }
        Log.d("TAG", "location_tag" + tag);
        return location;
    }
    private AddressInfo addressInfo = new AddressInfo();
    private String address = "";
    public AddressInfo getAddressInfo() {
        return addressInfo;
    }

    public String getAddress() {
        return address;
    }

    //获取地址信息:城市、街道等信息
    public String getAddress(Context context,Location location) {
        List<Address> result;
        try {
            if (location != null) {
                Geocoder gc = new Geocoder(context, Locale.getDefault());
                // 返回一个地址数组,该数组试图描述给定纬度和经度周围的区域。
                // 返回的地址应该根据提供给该类构造函数的区域设置进行本地化。
                // 结果可以通过网络查找的方式获得,这个方法可能需要一些时间来返回,因此不应该在主线程上调用。
                result = gc.getFromLocation(location.getLatitude(),
                        location.getLongitude(), 1);
                for (int i = 0; i < result.size(); i++) {
                    address = result.get(i).toString();
                }
                addressInfo.gps_latitude = String.valueOf(result.get(0).getLatitude());
                addressInfo.gps_longitude = String.valueOf(result.get(0).getLongitude());
                addressInfo.gps_address_street = result.get(0).getAddressLine(2);
                addressInfo.gps_address_province = result.get(0).getAdminArea();
                addressInfo.gps_address_city = result.get(0).getLocality();
                addressInfo.gps_address_country = result.get(0).getAddressLine(0);
                addressInfo.gps_address_countryCode = result.get(0).getCountryCode();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return address;
    }

    public static class AddressInfo{
        private String gps_longitude;// 经度
        private String gps_latitude;// 维度
        private String gps_address_street;// 街道
        private String gps_address_province;// 省份
        private String gps_address_city;// 城市
        private String gps_address_country;// 国家
        private String gps_address_countryCode;// 国家代码

        public AddressInfo() {}

        public String getGps_address_country() {
            return gps_address_country;
        }

        public String getGps_address_countryCode() {
            return gps_address_countryCode;
        }

        public String getGps_longitude() {
            return gps_longitude;
        }

        public String getGps_latitude() {
            return gps_latitude;
        }

        public String getGps_address_street() {
            return gps_address_street;
        }

        public String getGps_address_province() {
            return gps_address_province;
        }

        public String getGps_address_city() {
            return gps_address_city;
        }
    }
}

最后附上相关源码以及我手搓的开源库:
数据抓取:https://github.com/Android5730/DataCapture
如果有帮助到各位,可以给个star,给我一点信心去完善这个开源库

总结

本工具类已经步入正式使用了,基本上不会有问题,当然如果能够获取精细级别的定位权限,那么获取数据就更加完善,本项目只获取了大致位置权限,可能有部分机型无法获取全部的信息数据。
当然,如果有可以修改的建议和更好的方法,欢迎在评论区提出。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薪火_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值