在安卓系统中使用Firebase和谷歌地图创建一个位置跟踪应用程序
在本教程中,你将学习如何创建一个使用谷歌地图API来确定另一个用户的精确位置的应用程序。我们将通过在Firebase实时数据库中保存用户的位置,然后从另一个设备中检索数据来实现这一目标。
位置服务最近在我们日常使用的社交应用程序中变得很普遍。此外,这些服务还可以用来追踪丢失或被盗的设备。因此,了解如何实现位置服务可以让你创造出更多富有成效的应用程序。
目标
通过本教程,你将能够想出两个简单的应用程序来帮助进行位置跟踪。这些项目使用谷歌地图API和Firebase的实时数据库。
前提条件
- 对Android开发中的Kotlin有一个很好的理解。
- Android Studio。
在本教程中,我们将使用真实的Android设备进行测试。然而,你可以通过模拟位置数据来使用模拟器。
第1步 - 创建一个新项目
在Android Studio上创建一个新项目。在选择Project template 页面,选择Google Maps Activity 模板。
![]()
等待Android Studio构建你的项目。
此时运行应用程序后,你会看到一个空白屏幕,因为你还没有为地图设置API key 。
有几件事需要注意
一旦你打开AndroidManifest.xml ,你会看到这些自动填充的细节。
ACCESS_FINE_LOCATION权限。这可以访问用户的精确位置。大多数情况下,当你需要最精确的位置时使用。另一种类型的位置精度是 ,它的精度较低。因此,我们将使用 ,以获得准确的读数。ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATIONcom.google.android.geo.API_KEY指定了API密钥。
implementation 'com.google.android.gms:play-services-maps:17.0.0'
第2步 - 创建一个API密钥
在使用Google Maps API 之前,你需要创建一个API key ,并在developer console 中启用它。使用你的谷歌账户来注册。
打开res/values/google_maps_api.xml 。这是你将看到的。
![]()
这个文件将包含你的API key 。点击下划线的链接,按create a project ,然后继续。
![]()
在下一个屏幕中,你将需要创建一个API key 来调用API。
![]()
API key 是在purple 。
![]()
一旦你有了API key ,把它复制并粘贴到XML文件中的google_maps_key 的值中。
MapsActivity
这是对默认的MapsActivity 的概述。
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
// This method is called when we need to initialize the map and as you can see, it creates a marker with coordinates near Sydney and adds it to the map.
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
// Add a marker in Sydney and move the camera
val sydney = LatLng(-34.0, 151.0)
mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
//call moveCamera() on mMap to update the camera
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}
}
在这一点上,一旦你运行这个应用程序,你会注意到标记是在悉尼。
第3步 - 创建一个Firebase项目
我们将使用Firebase来存储用户的位置。再一次,你将需要一个谷歌账户。
![]()
第4步 - 将Firebase项目连接到应用程序上
你现在需要把这个项目连接到你的应用程序。
- 转到工具>Firebase
![]()
确保你将Realtime Database 到你的应用程序。
第5步 - 添加权限
- 添加互联网权限。
这个权限允许应用程序连接到互联网并保存数据。
<uses-permission android:name="android.permission.INTERNET"/>
- 添加谷歌地图的位置依赖。
implementation 'com.google.android.gms:play-services-location:17.0.0'
第6步 - MapsActivity
导航到MapsActivity.kt ,添加以下代码。
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var map: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
setupLocClient()
}
private lateinit var fusedLocClient: FusedLocationProviderClient
// use it to request location updates and get the latest location
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap //initialise map
getCurrentLocation()
}
private fun setupLocClient() {
fusedLocClient =
LocationServices.getFusedLocationProviderClient(this)
}
// prompt the user to grant/deny access
private fun requestLocPermissions() {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), //permission in the manifest
REQUEST_LOCATION)
}
companion object {
private const val REQUEST_LOCATION = 1 //request code to identify specific permission request
private const val TAG = "MapsActivity" // for debugging
}
private fun getCurrentLocation() {
// Check if the ACCESS_FINE_LOCATION permission was granted before requesting a location
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
// call requestLocPermissions() if permission isn't granted
requestLocPermissions()
} else {
fusedLocClient.lastLocation.addOnCompleteListener {
// lastLocation is a task running in the background
val location = it.result //obtain location
//reference to the database
val database: FirebaseDatabase = FirebaseDatabase.getInstance()
val ref: DatabaseReference = database.getReference("test")
if (location != null) {
val latLng = LatLng(location.latitude, location.longitude)
// create a marker at the exact location
map.addMarker(MarkerOptions().position(latLng)
.title("You are currently here!"))
// create an object that will specify how the camera will be updated
val update = CameraUpdateFactory.newLatLngZoom(latLng, 16.0f)
map.moveCamera(update)
//Save the location data to the database
ref.setValue(location)
} else {
// if location is null , log an error message
Log.e(TAG, "No location found")
}
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
//check if the request code matches the REQUEST_LOCATION
if (requestCode == REQUEST_LOCATION)
{
//check if grantResults contains PERMISSION_GRANTED.If it does, call getCurrentLocation()
if (grantResults.size == 1 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
getCurrentLocation()
} else {
//if it doesn`t log an error message
Log.e(TAG, "Location permission has been denied")
}
}
}
}
第7步 - 运行该应用程序
运行该应用程序。这就是你要实现的目标(位置可能有所不同)。给予该应用程序的位置权限。
![]()
用户的位置将被看到,如下图所示。
![]()
从上面的代码来看,你已经成功地将用户的位置保存在数据库中。导航到Firebase控制台,点击你创建的项目。
你应该看到与此类似的东西。
![]()
LocationChecker应用程序
这第二个应用程序允许你从数据库中检索用户的位置。
第1步:创建一个新的项目
按照上面讨论的过程来创建一个新的项目。确保你选择了Google Maps 模板,并为其适当地命名。
第2步:向现有的密钥添加证书
由于我们已经有一个API key ,我们可以直接在控制台中包含它。打开你的开发者控制台,点击edit icon 。
![]()
导航到google_maps_api.xml 文件,复制软件包名称和SHA-1证书指纹,将这些细节粘贴到add item 部分。然后,保存更改。
第3步:添加一个按钮
我们需要添加一个button ,它将触发对数据库中当前位置的读取。
这里是activity_maps.xml 。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
tools:context=".MapsActivity"
xmlns:tools="http://schemas.android.com/tools">
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:layout_constraintLeft_toLeftOf="parent"
map:layout_constraintRight_toRightOf="parent"
map:layout_constraintTop_toTopOf="parent"
map:layout_constraintBottom_toBottomOf="parent" />
<Button
android:layout_width="wrap_content"
map:layout_constraintLeft_toLeftOf="parent"
map:layout_constraintRight_toRightOf="parent"
map:layout_constraintBottom_toBottomOf="parent"
android:padding="20dp"
android:id="@+id/btn_find_location"
android:text="@string/find_user_s_location"
android:layout_height="wrap_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
第4步:添加权限
默认情况下,GoogleMaps 活动模板在AndroidManifest.xml 文件中添加了ACCESS_FINE_LOCATION 的权限。因为我们需要互联网来读取数据库,所以要添加互联网的权限,如下图所示。
<uses-permission android:name="android.permission.INTERNET"/>
第5步:模型类
因为我们要从数据库中读取数据,所以我们需要一个类来添加attributes,latitude, 和longitude 来处理数据。
import com.google.firebase.database.IgnoreExtraProperties
@IgnoreExtraProperties
data class LocationInfo(
var latitude: Double? = 0.0,
var longitude: Double? = 0.0
)
第6步:MapsActivity
添加以下代码。
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var map: GoogleMap
private var database: FirebaseDatabase = FirebaseDatabase.getInstance()
private var dbReference: DatabaseReference = database.getReference("test")
private lateinit var find_location_btn: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
find_location_btn = findViewById(R.id.btn_find_location)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
// Get a reference from the database so that the app can read and write operations
dbReference = Firebase.database.reference
dbReference.addValueEventListener(locListener)
}
val locListener = object : ValueEventListener {
// @SuppressLint("LongLogTag")
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()){
//get the exact longitude and latitude from the database "test"
val location = snapshot.child("test").getValue(LocationInfo::class.java)
val locationLat = location?.latitude
val locationLong = location?.longitude
//trigger reading of location from database using the button
find_location_btn.setOnClickListener {
// check if the latitude and longitude is not null
if (locationLat != null && locationLong!= null) {
// create a LatLng object from location
val latLng = LatLng(locationLat, locationLong)
//create a marker at the read location and display it on the map
map.addMarker(MarkerOptions().position(latLng)
.title("The user is currently here"))
//specify how the map camera is updated
val update = CameraUpdateFactory.newLatLngZoom(latLng, 16.0f)
//update the camera with the CameraUpdate object
map.moveCamera(update)
}
else {
// if location is null , log an error message
Log.e(TAG, "user location cannot be found")
}
}
}
}
// show this toast if there is an error while reading from the database
override fun onCancelled(error: DatabaseError) {
Toast.makeText(applicationContext, "Could not read from database", Toast.LENGTH_LONG).show()
}
}
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap //initialize map when the map is ready
}
companion object {
// TAG is passed into the Log.e methods used above to print information to the Logcat window
private const val TAG = "MapsActivity" // for debugging
}
}
当你运行该应用程序时,你应该能够得到一个类似的视图(位置会有所不同)。
![]()
测试应用程序
在你的手机上安装两个应用程序。确保网络连接良好。检查数据库中的保存位置。在第二个应用程序LocationChecker上,检查是否检索到了位置。
结论
你可以使用地图API来创建令人敬畏的应用程序,或者为现有的应用程序添加更多的功能。你还可以添加你可能感兴趣的其他功能。例如,你可以通知用户他们正在被追踪。