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

681 阅读8分钟

系列文章目录

总篇:(数据抓取:抓取手机设备各种数据 - 掘金 (juejin.cn))

分篇(一):【数据抓取(一)】手机通讯录数据获取 - 掘金 (juejin.cn)

分篇(二):数据抓取(二)&安卓定位方案:地址信息的获取 - 掘金 (juejin.cn)

分篇(三):数据抓取(三):安卓-免权限获取所有安装的应用程序信息(系统和非系统) - 掘金 (juejin.cn)

前言

一、实现步骤

  • 1.检查是否开启定位服务(通常会在启动Activity进行判断)
  • 2.根据定位服务是否开启分支执行代码
  • 3.工具类LocationUtils获取地址信息
  • 4.工具类LocationUtils通过SP存取经纬度

二、地址信息类AddressInfo

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

  • 总结

前言

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

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

image.png


一、实现步骤

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

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

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

  • 获取LocationManager服务
  • 根据服务提供API isProviderEnabled()检查是否开启定位服务
  // 判断是否开启定位服务
    public  boolean isLocationServiceEnabled(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,给我一点信心去完善这个开源库

总结

提示:这里对文章进行总结: 例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。