项目需求讨论 - 定位功能小结

前言:

我们知道我们的APP有可能需要获取一些地理位置信息。比如定位用户当前的位置,自动选定城市或者区域等。所以这次做个关于定位的一些总结。

正文

我们按照二大块来进行分析:一块是相关权限,一块是具体获取地理信息的相关代码。(而实际开发代码中,代码这二块是写在一起的,单纯是为了文章分析从而分开。)


1.相关权限

这里的权限我特指了二块:

  1. 一个是本身我们平常开发的app需要获取各种权限,比如相机等,这时候我们既然要获取当前手机的地理信息,肯定也要有一个Location相关的权限。
  2. 本身手机需要打开相应的定位功能,不然app有权限获取,但是手机关闭了整个的定位功能,就还是获取不到。

1.1 app获取手机权限

emmm......这块我觉得应该不需要花更多的时间来说明了吧,主要就是:

  1. 检查权限 (checkSelfPermission)
  2. 请求权限(requestPermissions)
  3. 回调事件处理(onRequestPermissionsResult)
    而我们要申请的权限无非就是Location相关的权限。
android.permission.ACCESS_COARSE_LOCATION 
允许一个程序访问CellID或WiFi热点来获取粗略的位置
android.permission.ACCESS_FINE_LOCATION 
允许一个程序访问精良位置(如GPS)

我们可以看到第一个权限中的英文单词COARSE粗略的意思,所以在想要粗略的获取一个地理位置的时候,比如我们通过网络来获取,我们只需要申请这个权限即可;第二个权限中的英文单词FINE说明是精确度高的,比如我们需要通过GPS来获取权限的时候,我们就需要申请这个权限。

一般来说我们的app这二个权限都会申请,因为会需要GPS配合网络一起来确定地理位置信息。

1.2 手机的定位开关

在确定我们的app本身已经具有了定位权限后,我们需要知道本身的手机是否已经打开了定位功能。

public static boolean isLocServiceEnable(Context context) {
    LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    if (gps || network) {
       return true;
    }
    return false;
}

我们可以看到上面我们提过一般来获取定位是靠GPS和NetWork二种(为啥是一般呢,因为还有一种 PASSIVE,后面会讲到)。所以我们需要判定这二个功能是否可用。(如果用户把定位功能给关了,那肯定二个都返回false。)


那这时候假如我们发现用户把定位功能关了。我们肯定需要提示用户,然后协助用户跳到该设置界面,从而让用户把定位功能打开 (毕竟一般的普通用户,可能还真的让他去设置界面找,一时半会还真找不到,毕竟安卓机型太多,每个地方都不同 )。

比如我们弹出一个弹框,提示用户,按确定按钮的时候跳转到设置的定位界面:

AlertDialog.Builder builder  = new AlertDialog.Builder(activity);
builder.setMessage("尚未开启位置定位服务");
builder.setPositiveButton("开启", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
            //启动定位Activity
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
           //比如我们这里设定requestCode为 1 
            activity.startActivityForResult(intent , 1);
      }
});

builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
       @Override
       public void onClick(DialogInterface dialog, int which) {
       }
});

我们可以看到通过隐式启动Action为Settings.ACTION_LOCATION_SOURCE_SETTINGS即可,但是这里记得用使用startActivityForResult而不是startActivity,我看很多网上的的写法是用startActivity,单纯跳转过去是没问题,但是我们需要知道返回的结果,万一用户跳转过去后没有打开呢。

既然我们用了statActivityForResult来启动,当我们返回回到自己的app界面的时候,在onActivityForResult中需要来判断,本来因为习惯性思维,所以以为自动在onActivityForResult的返回参数resultCode可以用来判断,后来发现不管开启不开启,都是返回RESULT_CANCELED,也就是0,毕竟在那个设置界面我们并没有设定setResult(xxx);所以当判断了requestCode之后,我们需要重新判断一次定位是否可用了。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
      
    if (requestCode == 1) {
       //我们通过上面提过的方法,再次去判断是否gps和network的provider都无效。
       if (!isLocServiceEnable(MainActivity.this)) {
           Toast.makeText(this, "未开启定位功能,请手动选择地址位置", Toast.LENGTH_LONG).show();
       } else {
           //去获取具体的地理位置信息...
       }
    } 
}

2 获取地理位置

我们上面提到了我们想要获取地理位置的时候,需要具备上面的基本权限,然后才能正常使用我们的相关api去获取信息。

主要是通过```LocationManager``这个类。


但是android.location包下的并不是谷歌推荐的:


翻译过来就是:此API不是访问Android位置的推荐方法。
Google位置服务API是Google Play服务的一部分,是向您的应用添加位置感知功能的首选方式。 它提供了更简单的API,更高的精度,低功耗的地理围栏等等。 如果您当前正在使用android.location API,强烈建议您尽快切换到Google Location Services API。

而是推荐the Google Location Services API ,然后你懂得....emmm........

2.1 直接获取地理信息

使用getLastKnownLocation方法获取:

//获取LocationManager的实例对象
locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
//获取支持的provider列表
List<String> providers = locationManager.getProviders(true);
Location bestLocation = null;
//遍历provider列表
for (String provider : providers) {
    //通过getLastKnowLocation方法来获取
    Location l = locationManager.getLastKnownLocation(provider);
    if (l == null) {
         continue;
    }
    if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
         // Found best last known location: %s", l);
         bestLocation = l;
     }
}

这里需要注意的是,为啥通过循环provider来获取,比如有些人会问,我开启了GPS,我想通过GPS来定位,我不是直接getLastKnowLocation(LocationManager.GPS_PROVIDER)就可以了吗?理论上是没问题的,但是大部分时候获取到的都是null , 毕竟GPS本身定位时间也会很久,而且如果在室内就更加GG了。

所以网上经常看到有提问:


当然解决方式也有很多,有些人直接通过while循环,比如一直请求:

while(xxx = null){
  xxx = getLastKnowLocation(LocationManager.GPS_PROVIDER);
}

这还不算坑爹,我用了华为和小米手机,小米手机使用这个GPS来获取Location,一下子就获取了。华为我写了while循环,等了很久很久,也还是一直是null。(居然还跟不同牌子手机都有关系)

所以最终我是遍历了provider来获取最佳的地址来解决的,如果获取不到GPS定位,也会有network辅助。

也可以参考相关的链接了解一下:Android 成功 使用GPS获取当前地理位置(解决getLastKnownLocation 返回 null),不过貌似也没有找到百分百直接获取GPS定位获取信息的方式。


2.2 监视位置变化

使用requestLocationUpdates方法来获取。

public void requestLocationUpdates(
String provider, 
long minTime, 
float minDistance,
LocationListener listener) 
{

}

我们可以看到传入provider,最小更新时间,最小的更新距离,然后就是回调listener。

所以我们重点在于LocationListener:

mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
             //比如判断location是否为null,然后根据Location来转换成相关的地址位置信息。
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
                  
        }

        @Override
        public void onProviderEnabled(String provider) {
            
        }

        @Override
        public void onProviderDisabled(String provider) {
                    //比如在provider失效了就取消监听
                    locationManager.removeUpdates(this);
        }
};

切记,在某个你需要的条件下,通过removeUpdates()去取消监听,比如你可能在onPause中去取消等。

我们在onLocationChanged方法中获取到了Location对象,就可以去获取相关信息了。

  1. 通过Location来获取相关的经纬度:


double latitude = location.getLatitude();
double longitude = location.getLongitude();
  1. 通过Geocoder来把经纬度转换成相应的Address集合:


Geocoder geocoder = new Geocoder(context);
List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);
  1. 最后通过Address对象中的相关属性,拼接出自己想要的相关信息。
address.getCountryName() //国家
address.getPostalCode() //邮编
address.getCountryCode() //国家编码
address.getAdminArea() //省份
address.getSubAdminArea() //二级省份
address.getThoroughfare() //道路
address.getSubLocality() //二级城市
.......
.......


结语:

emm.......大家轻喷即可。。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容