安卓应用开发学习:获取经纬度及地理位置描述信息

前段时间,我在学习鸿蒙应用开发的过程中,在鸿蒙系统的手机上实现了获取经纬度及地理位置描述信息(鸿蒙应用开发学习:手机位置信息进阶,从经纬度数据获取地理位置描述信息)。反而学习时间更长的安卓应用开发还未实现获取经纬度及地理位置描述。这几天,我正在看《Android App 开发进阶与项目实战》一书,正好看到了第9章是讲定位导航的。这一章里正好有获取经纬度和详细地址的内容,随书还附带有源码。我照着做,很轻松的实现了用安卓手机获取经纬度和详细地址的功能。特此记录以备忘。



(我的安卓手机上实现了获取经纬度和详细地址)
稍微有点不足的就是,我的手机上显示的定位类型为 null,而书中显示的是卫星定位。这边书是几年前的,基于安卓11的而我的手机系统已经是安卓13,可能操作系统的不同,使得同样的代码运行效果有所不同吧。
我的这个应用中与获取经纬度及详细地址有关的代码如下:
1.获取经纬度及详细地址的Activity文件
src\main\java\......\LocationPageActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;import android.Manifest;
import android.annotation.SuppressLint;
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.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;import com.bahamutjapp.task.GetAddressTask;
import com.bahamutjapp.util.DateUtil;
import com.bahamutjapp.util.SwitchUtil;import java.util.HashMap;
import java.util.Locale;
import java.util.Map;@SuppressLint(value={"DefaultLocale","SetTextI18n"})
public class LocationPageActivity extends AppCompatActivity {private final static String TAG = "myDebug";private Map<String,String> providerMap = new HashMap<>();private TextView tv_location; // 声明一个文本视图对象private String mLocationDesc = ""; // 定位说明private LocationManager mLocationMgr; // 声明一个定位管理器对象private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象private boolean isLocationEnable = false; // 定位服务是否可用@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_location_page);tv_location = findViewById(R.id.tv_location);providerMap.put("gps", "卫星定位");providerMap.put("network", "网络定位");SwitchUtil.checkLocationIsOpen(this, "需要打开定位功能才能查看定位信息");}@Overrideprotected void onResume() {super.onResume();mHandler.removeCallbacks(mRefresh); // 移除定位刷新任务initLocation(); // 初始化定位服务mHandler.postDelayed(mRefresh, 100); // 延迟100毫秒启动定位刷新任务}// 初始化定位服务private void initLocation() {// 从系统服务中获取定位管理器mLocationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);Criteria criteria = new Criteria(); // 创建一个定位准则对象// 设置定位精确度。Criteria.ACCURACY_COARSE表示粗略,Criteria.ACCURACY_FIN表示精细criteria.setAccuracy(Criteria.ACCURACY_FINE);criteria.setAltitudeRequired(true); // 设置是否需要海拔信息criteria.setBearingRequired(true); // 设置是否需要方位信息criteria.setCostAllowed(true); // 设置是否允许运营商收费criteria.setPowerRequirement(Criteria.POWER_LOW); // 设置对电源的需求// Log.d(TAG, "初始化定位服务, 准备获取定位管理器的最佳定位提供者");// 获取定位管理器的最佳定位提供者String bestProvider = mLocationMgr.getBestProvider(criteria, true);if (mLocationMgr.isProviderEnabled(bestProvider)) { // 定位提供者当前可用tv_location.setText("正在获取" + providerMap.get(bestProvider) + "对象");mLocationDesc = String.format("【定位信息】\n定位类型为%s", providerMap.get(bestProvider));beginLocation(bestProvider); // 开始定位isLocationEnable = true;} else { // 定位提供者暂不可用tv_location.setText(providerMap.get(bestProvider) + "不可用");isLocationEnable = false;}}// 显示定位结果文本private void showLocation(Location location) {if (location != null) {// 创建一个根据经纬度查询详细地址的任务GetAddressTask task = new GetAddressTask(this, location, address -> {String desc = String.format(Locale.CHINESE,"%s" +"\n\t定位时间为%s," + "\n\t经度为%f,纬度为%f," +"\n\t高度为%d米,精度为%d米," +"\n\t详细地址为%s。",mLocationDesc, DateUtil.formatDate(location.getTime()),location.getLongitude(), location.getLatitude(),Math.round(location.getAltitude()), Math.round(location.getAccuracy()),address);tv_location.setText(desc);});task.start(); // 启动地址查询任务} else {tv_location.setText(mLocationDesc + "\n暂未获取到定位对象");}}// 开始定位private void beginLocation(String method) {// 检查当前设备是否已经开启了定位功能if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {Toast.makeText(this, "请授予定位权限并开启定位功能", Toast.LENGTH_SHORT).show();return;}// 设置定位管理器的位置变更监听器mLocationMgr.requestLocationUpdates(method, 300, 0, mLocationListener);// 获取最后一次成功定位的位置信息Location location = mLocationMgr.getLastKnownLocation(method);showLocation(location); // 显示定位结果文本}// 定义一个位置变更监听器private LocationListener mLocationListener = new LocationListener() {@Overridepublic void onLocationChanged(Location location) {showLocation(location); // 显示定位结果文本}@Overridepublic void onProviderDisabled(String arg0) {}@Overridepublic void onProviderEnabled(String arg0) {}@Overridepublic void onStatusChanged(String arg0, int arg1, Bundle arg2) {}};// 定义一个刷新任务,若无法定位则每隔一秒就尝试定位private Runnable mRefresh = new Runnable() {@Overridepublic void run() {if (!isLocationEnable) {initLocation(); // 初始化定位服务mHandler.postDelayed(this, 1000);}}};@Overrideprotected void onDestroy() {super.onDestroy();mLocationMgr.removeUpdates(mLocationListener); // 移除定位管理器的位置变更监听器}}
2.Activity文件对应的xml文件
src\main\res\layout\activity_location_page.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".LocationPageActivity"><TextViewandroid:id="@+id/tv_locationTitle"android:layout_width="wrap_content"android:layout_height="30dp"android:text="定位导航"android:textSize="24sp"android:textStyle="bold"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_location"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="10dp"android:layout_marginTop="50dp"android:layout_marginEnd="10dp"android:paddingStart="10dp"android:paddingEnd="10dp"android:text="【定位信息】"android:textSize="16sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_locationTitle" /></androidx.constraintlayout.widget.ConstraintLayout>
3.GetAddressTask.java文件(此文件根据经纬度数据获取详细地址信息)
src\main\java\......\task\GetAddressTask.java
import android.app.Activity;
import android.location.Location;
import android.util.Log;
import android.widget.Toast;import androidx.annotation.NonNull;import org.json.JSONException;
import org.json.JSONObject;import java.io.IOException;
import java.util.Objects;import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;// 根据经纬度获取详细地址的线程
public class GetAddressTask extends Thread {private static final String TAG = "GetAddressTask";private String mQueryUrl = "https://api.tianditu.gov.cn/geocoder?postStr={'lon':%f,'lat':%f,'ver':1}&type=geocode&tk=253b3bd69713d4bdfdc116255f379841";private Activity mAct; // 声明一个活动实例private OnAddressListener mListener; // 声明一个获取地址的监听器对象private Location mLocation; // 声明一个定位对象public GetAddressTask(Activity act, Location location, OnAddressListener listener) {mAct = act;mListener = listener;mLocation = location;}@Overridepublic void run() {String url = String.format(mQueryUrl, mLocation.getLongitude(), mLocation.getLatitude());Log.d(TAG, "url="+url);OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象// 创建一个GET方式的请求结构Request request = new Request.Builder().url(url).build();Call call = client.newCall(request); // 根据请求结构创建调用对象// 加入HTTP请求队列。异步调用,并设置接口应答的回调方法call.enqueue(new Callback() {@Overridepublic void onFailure(@NonNull Call call, @NonNull IOException e) { // 请求失败// 回到主线程操纵界面mAct.runOnUiThread(() -> Toast.makeText(mAct,"查询详细地址出错:"+e.getMessage(), Toast.LENGTH_SHORT).show());}@Overridepublic void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException { // 请求成功String resp = Objects.requireNonNull(response.body()).string();Log.d(TAG, "resp="+resp);// 下面从json串中逐级解析formatted_address字段获得详细地址描述try {JSONObject obj = new JSONObject(resp);JSONObject result = obj.getJSONObject("result");String address = result.getString("formatted_address");// 回到主线程操纵界面mAct.runOnUiThread(() -> mListener.onFindAddress(address));} catch (JSONException e) {e.printStackTrace();}}});}// 定义一个查询详细地址的监听器接口public interface OnAddressListener {void onFindAddress(String address);}}
4.DateUtil.java文件(对日期数据进行格式化)
src\main\java\......\util\DateUtil.java
import android.annotation.SuppressLint;import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;@SuppressLint("SimpleDateFormat")
public class DateUtil {// 获取当前的日期时间public static String getNowDateTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");return sdf.format(new Date());}// 将长整型的时间数值格式化为日期时间字符串public static String formatDate(long time) {Date date = new Date(time);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(date);}}
5.SwitchUtil.java文件(获取定位功能开关状态)
src\main\java\......\util\SwitchUtil.java
import android.content.Context;
import android.content.Intent;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;import java.lang.reflect.Method;public class SwitchUtil {private static final String TAG = "SwitchUtil";// 获取定位功能的开关状态public static boolean getLocationStatus(Context ctx) {// 从系统服务中获取定位管理器LocationManager lm = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}// 检查定位功能是否打开,若未打开则跳到系统的定位功能设置页面public static void checkLocationIsOpen(Context ctx, String hint) {if (!getLocationStatus(ctx)) {Toast.makeText(ctx, hint, Toast.LENGTH_SHORT).show();Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);ctx.startActivity(intent);}}}
6月25日补充:
今天代码进一步研究发现导致页面上定位类型显示为null的原因是在LocationPageActivity.java文件中的源代码“ String bestProvider = mLocationMgr.getBestProvider(criteria, true); ” 的返回值是“fused”。通过搜寻资料,才知道这是一种定位类型(融合定位),介绍资料见下面的链接:
Fused定位
再对代码进行仔细研究,发现上面的代码的返回值赋值给“bestProvider”后,通过执行以下语句获取在页面上显示定位类型的字符串:
mLocationDesc = String.format("【定位信息】\n\t定位类型为%s", providerMap.get(bestProvider));
这个语句是将“bestProvider”作为参数从providerMap对象中获取对应的值。而providerMap对象是在onCreate方法中赋值的:
providerMap.put("gps", "卫星定位");
providerMap.put("network", "网络定位");
因为只put了两个键值对,没有fused的键值对,因此得到的结果是null。解决方法就是再加入下面这条即可。
providerMap.put("fused", "融合");
相关文章:
安卓应用开发学习:获取经纬度及地理位置描述信息
前段时间,我在学习鸿蒙应用开发的过程中,在鸿蒙系统的手机上实现了获取经纬度及地理位置描述信息(鸿蒙应用开发学习:手机位置信息进阶,从经纬度数据获取地理位置描述信息)。反而学习时间更长的安卓应用开发…...
各类排序方法 手撕快排 回顾经典快排 优化版快排
快排的主要思想是分而治之 第一步,确定分界点,a 第二步,调整区间,利用分界点a,把小于分界点a的数放在左边,大于的放在右边,相等的放在哪都可以 第三步,递归处理左右两段 实现(暴…...
独一无二的设计模式——单例模式(Java实现)
1. 引言 亲爱的读者们,欢迎来到我们的设计模式专题,今天的讲解的设计模式,还是单例模式哦!上次讲解的单例模式是基于Python实现(独一无二的设计模式——单例模式(python实现))的&am…...
使用MoA(Mixture of Agents)混合智能体技术,结合多个开源大语言模型如Llama3、phi-3和Mistral,实现一个强大的AI智能体
1.简介 论文简介: 论文提出了一种称为混合智能体(Mixture-of-Agents,MoA)的方法,利用多个大语言模型(LLM)的集体智慧来提高自然语言理解和生成任务的性能。 MoA采用了分层结构,每一层包含多个LLM智能体。每个智能体都将前一层所有智能体的输出作为辅助信息来生成自己的回答。通…...
前端面试题_Css
一、说一下Css的盒子模型? HTML中所有元素都可以看成是一个盒子 盒子的组成:content、padding、border、margin 盒子的类型: 标准盒模型:marginborderpaddingcontent -- box-sizing:content-box(默认&a…...
AI在线免费视频工具3:声音生视频
1、声音生视频 Noisee:通过声音生成对应视频,可以增加prompt指定生成内容相关视频 https://noisee.ai/create...
final、const、readonly关键字在不同语言中代表着什么
一、Java 1.被final修饰的类不能被继承。 2.被final修饰的方法不能被重写。 被 final 修饰的类中所有的成员方法都会隐式的定义为 final 方法。 若父类中 final 方法的访问权限为 private ,则子类中不能直接继承该方法。此时可以在子类中定义相同方法名的函数&…...
HarmonyOS ArkUi Tabs+TabContent+List实现tab吸顶功能
Demo效果 Entry Component struct StickyNestedScroll {State message: string Hello WorldState arr: number[] []scroller new Scroller()StyleslistCard() {.backgroundColor(Color.White).height(72).width("100%").borderRadius(12)}build() {Scroll(this.sc…...
Hugging Face Accelerate 两个后端的故事:FSDP 与 DeepSpeed
社区中有两个流行的零冗余优化器 (Zero Redundancy Optimizer,ZeRO)算法实现,一个来自DeepSpeed,另一个来自PyTorch。Hugging FaceAccelerate对这两者都进行了集成并通过接口暴露出来,以供最终用户在训练/微调模型时自主选择其中之…...
TextField是用于在用户界面中输入文本的控件。它广泛应用于表单、搜索框、评论区等需要用户输入文字的场景
TextField是用于在用户界面中输入文本的控件。它广泛应用于表单、搜索框、评论区等需要用户输入文字的场景。以下是对TextField的详细解释,涵盖其各个方面的功能和属性。 基本属性 text 描述:TextField中当前显示的文本。用法:text: "示…...
MYSQL 四、mysql进阶 5(InnoDB数据存储结构)
一、数据库的存储结构:页 索引结构给我们提供了高效的索引方式,不过索引信息以及数据记录都是保存在文件上的,确切说时存储在页结构中,另一方面,索引是在存储引擎中实现的,Mysql服务器上的存储引擎负责对表…...
Spring企业开发核心框架-下
五、Spring AOP面向切面编程 1、场景设定和问题复现 ①准备AOP项目 项目名:Spring-aop-annotation ②声明接口 /*** - * / 运算的标准接口!*/ public interface Calculator { int add(int i, int j); int sub(int i, int j); int mul(int i, in…...
X射线底片焊缝缺陷检测
实现四种焊缝缺陷的检测和分割处理。...
直播的js代码debug解析找到protobuf消息的定义
我们都知道直播的弹幕消息是通过websocket发送的,而且是通过protobuf传输的,那么这里面传输了哪些内容,这个proto文件又要怎么定义?每个消息叫什么,消息里面又包含有哪些字段,每个字段又是什么类型…...
详细学习es6扩展运算符
ES6中的扩展运算符(Spread Operator)是一种非常方便的语法,主要用于将可迭代对象(比如数组、字符串等)展开成多个参数。以下是关于ES6扩展运算符的详细内容: 用法: 在数组字面量中展开数组&am…...
HEC-HMS水文模型教程
原文链接:HEC-HMS水文模型教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247607904&idx5&sn1a210328a3fc8f941b433674d8fe2c85&chksmfa826787cdf5ee91d01b6981ebd89deac3e350d747d0fec45ce2ef75d7cb8009341c6f55114d&token90645021…...
Spring Cloud LoadBalancer基础入门与应用实践
官网地址:https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/loadbalancer.html 【1】概述 Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在SpringCloud-commons中用…...
layui在表格中嵌入上传按钮,并修改上传进度条
当需要在表格中添加上传文件按钮,并不需要弹出填写表单的框的时候,需要在layui中,用按钮触发文件选择 有一点需要说明的是,layui定义table并不是在定义的标签中渲染,而是在紧接着的标签中渲染,所以要获取实…...
14-10 AIGC 项目生命周期——第一阶段
生成式 AI 项目生命周期的整个过程类似于从范围、选择、调整和对齐/协调模型以及应用程序集成开始的顺序依赖过程。流程表明每个步骤都建立在前一步的基础上。有必要了解每个阶段对于项目的成功都至关重要。 下面的流程图重点介绍了生成式 AI 项目生命周期的第一阶段 1 — “范…...
经典小游戏(一)C实现——三子棋
switch(input){case 1:printf("三子棋\n");//这里先测试是否会执行成功break;case 0:printf("退出游戏\n");break;default :printf("选择错误,请重新选择!\n");break;}}while(input);//直到输入的结果为假,循环才会结束} …...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
