Chào mừng các bạn quay trở lại với series “Android Developer Roadmap – Lộ trình học Lập trình viên Android 2025“! Chúng ta đã cùng nhau đi qua nhiều kiến thức nền tảng quan trọng, từ việc thiết lập môi trường, học Kotlin, OOP, cấu trúc dữ liệu và giải thuật, cho đến tạo ứng dụng đầu tiên, hiểu về vòng đời Activity, và xây dựng giao diện người dùng với các Layout và View khác nhau, đặc biệt là RecyclerView hay Fragments. Hôm nay, chúng ta sẽ bước vào một chủ đề cực kỳ phổ biến và hữu ích: tích hợp Google Maps và các dịch vụ liên quan từ Google Play Services vào ứng dụng Android của mình.
Trong thế giới di động hiện đại, khả năng hiển thị bản đồ, xác định vị trí người dùng, tìm kiếm địa điểm hay tính toán quãng đường là những tính năng cốt lõi mà rất nhiều ứng dụng yêu cầu, từ ứng dụng gọi xe, giao hàng, du lịch, bất động sản cho đến các ứng dụng mạng xã hội. Google Maps Platform, kết hợp với sức mạnh của Google Play Services, cung cấp bộ API mạnh mẽ để bạn dễ dàng triển khai những tính năng này.
Mục lục
Google Play Services là gì và Tại sao lại quan trọng?
Trước khi đi sâu vào Maps, chúng ta cần hiểu về Google Play Services. Đây không phải là một ứng dụng thông thường mà là một tập hợp các dịch vụ nền và API do Google cung cấp, chạy ngầm trên hầu hết các thiết bị Android có cài đặt Google Play Store. Google Play Services cung cấp các chức năng cốt lõi mà ứng dụng có thể tận dụng, bao gồm:
- Xác thực người dùng (Google Sign-In).
- Đồng bộ hóa dữ liệu.
- Truy cập các API của Google (Maps, Drive, Fit, v.v.).
- Dịch vụ định vị (Fused Location Provider).
- Nhắn tin đám mây (Firebase Cloud Messaging – FCM).
- Quảng cáo (AdMob).
Tích hợp với Google Play Services mang lại nhiều lợi ích:
- Cập nhật tự động: Các dịch vụ được cập nhật qua Google Play Store độc lập với việc cập nhật hệ điều hành, giúp ứng dụng của bạn luôn sử dụng phiên bản API mới nhất mà không cần người dùng phải nâng cấp Android.
- Hiệu năng tốt hơn: Cung cấp các API được tối ưu hóa cho hiệu năng và sử dụng năng lượng.
- Đồng bộ hóa: Giúp các ứng dụng trên cùng thiết bị hoặc trên các thiết bị khác nhau có thể hoạt động liền mạch hơn.
- Đơn giản hóa phát triển: Cung cấp các API dễ sử dụng, trừu tượng hóa nhiều chi tiết phức tạp của hệ thống.
Tóm lại, Google Play Services là nền tảng thiết yếu để truy cập nhiều tính năng mạnh mẽ của Google trên thiết bị Android, và Google Maps API là một trong những dịch vụ quan trọng nhất mà nó cung cấp.
Thiết lập Dự án và Nhận API Key
Để bắt đầu tích hợp Google Maps, bạn cần thực hiện vài bước thiết lập cơ bản:
1. Thêm Dependency
Mở file build.gradle
(Module level) của bạn và thêm dependency cho Google Maps SDK và Google Play Services location library:
dependencies {
// Các dependencies khác của bạn
implementation("com.google.android.gms:play-services-maps:18.2.0") // Phiên bản có thể thay đổi
implementation("com.google.android.gms:play-services-location:21.0.1") // Phiên bản có thể thay đổi
}
Sau khi thêm dependency, hãy Sync Project với Gradle bằng cách nhấp vào “Sync Now” ở góc trên bên phải Android Studio. Nếu bạn muốn tìm hiểu sâu hơn về Gradle, hãy xem lại bài viết “Gradle là gì? Cách sử dụng trong Phát triển Android“.
2. Nhận Google Maps API Key
Bạn cần có một API key để sử dụng Google Maps Platform. Đây là các bước cơ bản:
- Truy cập Google Cloud Console.
- Đăng nhập bằng tài khoản Google của bạn.
- Tạo một dự án mới hoặc chọn một dự án hiện có.
- Trong menu điều hướng, chọn “APIs & Services” -> “Library”.
- Tìm kiếm “Maps SDK for Android” và “Places API” (nếu cần) và “Geolocation API” (nếu cần) và Enable chúng.
- Trong menu điều hướng, chọn “APIs & Services” -> “Credentials”.
- Nhấp vào “Create Credentials” -> “API Key”.
- Google sẽ tạo một API key cho bạn. Hãy sao chép key này.
- Rất quan trọng: Hạn chế sử dụng API key của bạn để chỉ cho phép các ứng dụng Android sử dụng với SHA-1 fingerprint của signing certificate. Điều này giúp ngăn chặn việc người khác sử dụng key của bạn. Bạn có thể tìm SHA-1 fingerprint trong Android Studio bằng cách mở Gradle view (View -> Tool Windows -> Gradle), điều hướng đến tasks -> android -> signingReport, và chạy task này. SHA-1 sẽ hiển thị trong Run window.
3. Thêm API Key vào Dự án Android
Thêm API key vào file AndroidManifest.xml
của bạn. Đặt nó bên trong thẻ <application>
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
<application
...
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY" /> <!-- Thay YOUR_API_KEY bằng API key của bạn -->
...
</application>
</manifest>
Thay "YOUR_API_KEY"
bằng key bạn đã nhận được từ Google Cloud Console.
4. Thêm Permissions
Bạn cần khai báo các quyền cần thiết trong AndroidManifest.xml
, bên ngoài thẻ <application>
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Quyền truy cập vị trí -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Quyền truy cập Internet là cần thiết cho Google Maps -->
<uses-permission android:name="android.permission.INTERNET" />
... <!-- Thẻ application và các thành phần khác -->
</manifest>
ACCESS_FINE_LOCATION
cung cấp vị trí chính xác nhất (sử dụng GPS, Wi-Fi, và mạng di động), trong khi ACCESS_COARSE_LOCATION
cung cấp vị trí kém chính xác hơn (chỉ sử dụng Wi-Fi và mạng di động). Tùy thuộc vào yêu cầu của ứng dụng mà bạn chọn quyền phù hợp. Đối với Android 6.0 (API 23) trở lên, bạn cũng cần yêu cầu các quyền này lúc chạy ứng dụng (runtime permissions).
Hiển thị Bản đồ Cơ bản
Sau khi thiết lập, chúng ta có thể hiển thị bản đồ.
1. Thêm Fragment cho Bản đồ
Cách phổ biến nhất để hiển thị bản đồ là sử dụng SupportMapFragment
hoặc MapFragment
(nếu target API < 21). SupportMapFragment
tương thích với các phiên bản Android cũ hơn thông qua AndroidX. Bạn thêm nó vào file layout XML của Activity hoặc Fragment của mình:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MapsActivity">
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Nếu bạn chưa quen với việc xây dựng giao diện bằng XML, hãy xem lại bài viết về Layouts và Views cơ bản.
2. Khởi tạo Bản đồ trong Activity/Fragment
Trong Activity hoặc Fragment của bạn, bạn cần lấy tham chiếu đến SupportMapFragment
và đăng ký một callback để được thông báo khi bản đồ sẵn sàng.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var googleMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps) // layout chứa fragment bản đồ
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this) // Đăng ký callback
}
override fun onMapReady(map: GoogleMap) {
googleMap = map
// Ví dụ: Di chuyển camera đến một vị trí cụ thể và thêm marker
val sydney = LatLng(-34.0, 151.0)
googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}
}
Trong ví dụ trên, chúng ta implement interface OnMapReadyCallback
và override phương thức onMapReady()
. Phương thức này được gọi khi instance của GoogleMap
đã sẵn sàng để sử dụng. Tại đây, bạn có thể thực hiện các thao tác với bản đồ như di chuyển camera, thêm marker, cấu hình các tùy chọn bản đồ, v.v.
Việc sử dụng getMapAsync()
và xử lý trong callback onMapReady()
là quan trọng vì đối tượng GoogleMap
không có sẵn ngay lập tức khi Activity/Fragment được tạo. Cách tiếp cận bất đồng bộ này tương tự như cách bạn xử lý các tác vụ khác cần thời gian để hoàn thành, một chủ đề mà chúng ta đã thảo luận trong bài viết về Lập trình Bất đồng bộ trong Android.
Làm việc với Vị trí của Người dùng
Hiển thị bản đồ là bước đầu tiên. Tiếp theo, bạn thường muốn hiển thị vị trí hiện tại của người dùng hoặc theo dõi sự thay đổi vị trí.
1. Fused Location Provider Client
Google Play Services cung cấp Fused Location Provider API, một cách hiệu quả và tiết kiệm năng lượng để lấy thông tin vị trí. Bạn sẽ sử dụng FusedLocationProviderClient
.
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var googleMap: GoogleMap
private lateinit var fusedLocationClient: FusedLocationProviderClient // Khai báo client
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// Khởi tạo FusedLocationProviderClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
override fun onMapReady(map: GoogleMap) {
googleMap = map
// Bật lớp "My Location" trên bản đồ (hiển thị chấm xanh vị trí người dùng)
// Cần kiểm tra quyền trước khi gọi phương thức này
if (checkLocationPermission()) { // Hàm kiểm tra quyền tùy chỉnh
googleMap.isMyLocationEnabled = true
}
// Lấy vị trí cuối cùng biết được
getLastLocation()
}
private fun checkLocationPermission(): Boolean {
// Implement logic kiểm tra runtime permission ACCESS_FINE_LOCATION
// Trả về true nếu có quyền, false nếu không.
// Nếu không có quyền, cần yêu cầu người dùng cấp quyền.
// Đây là một bước quan trọng với các API level từ 23 trở lên.
// Xem thêm về Runtime Permissions trong tài liệu Android.
return true // Placeholder, cần implement đúng
}
private fun getLastLocation() {
if (checkLocationPermission()) { // Kiểm tra quyền lần nữa trước khi lấy vị trí
fusedLocationClient.lastLocation
.addOnSuccessListener { location ->
if (location != null) {
// Xử lý vị trí nhận được (ví dụ: di chuyển camera đến vị trí này)
val userLatLng = LatLng(location.latitude, location.longitude)
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(userLatLng, 15f))
} else {
// Vị trí cuối cùng không có, có thể yêu cầu update vị trí mới
// handleLocationUpdates() // Cần implement phương thức này
}
}
.addOnFailureListener { exception ->
// Xử lý khi có lỗi
}
}
}
// Hàm yêu cầu cập nhật vị trí (nếu cần)
// private fun handleLocationUpdates() { ... }
}
Lưu ý về việc kiểm tra và yêu cầu quyền (checkLocationPermission()
): Đây là bước bắt buộc đối với các quyền nguy hiểm như truy cập vị trí kể từ Android 6.0. Bạn cần implement logic này một cách cẩn thận, bao gồm việc hiển thị giải thích cho người dùng nếu họ từ chối quyền lần đầu và xử lý kết quả sau khi yêu cầu quyền. Điều này liên quan đến vòng đời của Activity và xử lý kết quả từ các Intent, những kiến thức bạn đã học trong các bài trước (Vòng đời Activity, Intents).
2. Yêu cầu Cập nhật Vị trí
Nếu bạn cần theo dõi vị trí của người dùng theo thời gian (ví dụ: cho ứng dụng theo dõi chuyến đi), bạn cần yêu cầu cập nhật vị trí liên tục:
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationResult
// ... trong class MapsActivity
private lateinit var locationRequest: LocationRequest
private lateinit var locationCallback: LocationCallback
// ... trong onCreate() hoặc sau khi có quyền
locationRequest = LocationRequest.create().apply {
interval = 10000 // khoảng thời gian giữa các lần cập nhật (ms)
fastestInterval = 5000 // khoảng thời gian cập nhật nhanh nhất có thể
priority = LocationRequest.PRIORITY_HIGH_ACCURACY // Độ chính xác cao
}
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
for (location in locationResult.locations){
// Xử lý từng vị trí nhận được ở đây
// Ví dụ: Cập nhật UI, thêm marker mới, vẽ đường đi
val currentLocation = LatLng(location.latitude, location.longitude)
// googleMap.addMarker(MarkerOptions().position(currentLocation).title("Vị trí hiện tại"))
// googleMap.animateCamera(CameraUpdateFactory.newLatLng(currentLocation))
}
}
}
// ... Thêm phương thức để bắt đầu và dừng cập nhật
private fun startLocationUpdates() {
if (checkLocationPermission()) { // Kiểm tra quyền trước khi bắt đầu
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper() // Sử dụng Main Looper
)
}
}
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
// Quan trọng: Bắt đầu/dừng cập nhật vị trí theo vòng đời Activity/Fragment
override fun onResume() {
super.onResume()
startLocationUpdates()
}
override fun onPause() {
super.onPause()
stopLocationUpdates() // Ngừng cập nhật khi ứng dụng ở chế độ nền để tiết kiệm pin
}
Việc quản lý cập nhật vị trí theo vòng đời Activity (onResume()
và onPause()
) là một ví dụ điển hình về việc tuân thủ vòng đời của các thành phần Android để tối ưu hiệu suất và tài nguyên hệ thống. Đối với các trường hợp cần cập nhật vị trí ngay cả khi ứng dụng bị đóng, bạn có thể cần sử dụng Services.
Thêm Marker và Vẽ Đường
Google Maps SDK cho phép bạn thêm các yếu tố tương tác lên bản đồ như marker (đánh dấu), polyline (đường), polygon (đa giác), v.v.
1. Thêm Marker
Bạn đã thấy ví dụ thêm marker ở trên. Bạn có thể tùy chỉnh marker bằng cách sử dụng MarkerOptions
:
val hanoi = LatLng(21.028511, 105.801868)
googleMap.addMarker(
MarkerOptions()
.position(hanoi)
.title("Hà Nội")
.snippet("Thủ đô Việt Nam") // Thông tin thêm khi nhấn vào marker
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)) // Đổi màu marker
// .icon(BitmapDescriptorFactory.fromResource(R.drawable.custom_marker_icon)) // Sử dụng icon tùy chỉnh
.alpha(0.7f) // Độ trong suốt
)
2. Vẽ Polyline (Ví dụ: vẽ đường đi)
import com.google.android.gms.maps.model.PolylineOptions
import com.google.android.gms.maps.model.JointType
import com.google.android.gms.maps.model.Cap
import com.google.android.gms.maps.model.RoundCap
import android.graphics.Color
// ... trong onMapReady() hoặc sau khi có các điểm tọa độ
val point1 = LatLng(21.0, 105.8)
val point2 = LatLng(20.9, 105.9)
val point3 = LatLng(20.8, 106.0)
googleMap.addPolyline(
PolylineOptions()
.add(point1, point2, point3) // Thêm các điểm vào đường
.width(10f) // Độ dày đường
.color(Color.RED) // Màu đường
.jointType(JointType.ROUND) // Kiểu nối các đoạn thẳng
.startCap(RoundCap()) // Kiểu đầu đường
.endCap(RoundCap()) // Kiểu cuối đường
.geodesic(true) // Đường cong theo bề mặt Trái Đất
)
Để vẽ đường đi thực tế giữa hai địa điểm, bạn thường cần sử dụng Directions API của Google Maps Platform (đòi hỏi thêm cấu hình và có thể phát sinh chi phí), hoặc các API tương tự từ nhà cung cấp khác để lấy danh sách các tọa độ tạo nên tuyến đường, sau đó vẽ các tọa độ đó bằng Polyline.
Xử lý Tương tác của Người dùng
Bạn có thể lắng nghe các sự kiện tương tác trên bản đồ.
1. Bắt Sự kiện Click trên Bản đồ
// Trong onMapReady()
googleMap.setOnMapClickListener { latLng ->
// Xử lý khi người dùng click vào một điểm trên bản đồ
// latLng chứa tọa độ của điểm click
Log.d("MapClick", "Clicked at: ${latLng.latitude}, ${latLng.longitude}")
// Có thể thêm marker tại điểm này, hoặc thực hiện hành động khác
}
2. Bắt Sự kiện Click trên Marker
import com.google.android.gms.maps.model.Marker
// Trong onMapReady()
googleMap.setOnMarkerClickListener { marker ->
// Xử lý khi người dùng click vào một marker
Log.d("MarkerClick", "Clicked on marker: ${marker.title}")
// Trả về true nếu bạn đã xử lý sự kiện (ví dụ: hiển thị info window tùy chỉnh),
// false để sử dụng hành vi mặc định (hiển thị info window mặc định)
false
}
Tóm tắt các Thành phần Chính
Dưới đây là bảng tóm tắt các thành phần chính mà bạn sẽ làm việc khi tích hợp Google Maps và Location Services:
Thành phần | Mục đích | Sử dụng | Ghi chú |
---|---|---|---|
Google Play Services | Cung cấp bộ API cốt lõi của Google trên thiết bị Android. | Thêm dependency play-services-* vào Gradle. |
Cần có trên thiết bị của người dùng. |
Maps SDK for Android | Bộ công cụ để nhúng và tương tác với bản đồ Google trong ứng dụng. | Thêm dependency play-services-maps . Sử dụng SupportMapFragment /MapFragment và đối tượng GoogleMap . |
Yêu cầu API key. |
Location Services API | Cung cấp các công cụ để lấy thông tin vị trí. | Thêm dependency play-services-location . Sử dụng FusedLocationProviderClient . |
Yêu cầu runtime permissions cho vị trí. |
SupportMapFragment |
Một Fragment chứa bản đồ Google. | Thêm vào layout XML. Lấy tham chiếu trong Activity/Fragment. | Cách phổ biến để hiển thị bản đồ, tương thích ngược. |
GoogleMap object |
Đối tượng chính để tương tác với bản đồ (thêm marker, di chuyển camera, cấu hình bản đồ, lắng nghe sự kiện). | Nhận được trong callback onMapReady() . |
Không có sẵn ngay lập tức khi Activity/Fragment được tạo. |
FusedLocationProviderClient |
API chính để truy cập dịch vụ định vị hợp nhất. | Khởi tạo bằng LocationServices.getFusedLocationProviderClient(this) . |
Cách được khuyến nghị để lấy vị trí. |
LocationRequest |
Định nghĩa các thông số cho yêu cầu cập nhật vị trí (khoảng thời gian, độ chính xác, v.v.). | Cấu hình trước khi yêu cầu cập nhật. | Tùy chỉnh hành vi cập nhật vị trí. |
LocationCallback |
Callback để nhận kết quả cập nhật vị trí. | Override phương thức onLocationResult() . |
Xử lý dữ liệu vị trí nhận được. |
Các Vấn đề Thường Gặp và Khắc phục
- Bản đồ chỉ hiển thị lưới ô vuông xám: Thường là do API key sai, thiếu quyền Internet, hoặc Maps SDK chưa được Enable trên Google Cloud Console. Kiểm tra lại các bước thiết lập và API key có bị hạn chế sai hay không.
- Ứng dụng crash khi yêu cầu vị trí: Có thể do thiếu runtime permission
ACCESS_FINE_LOCATION
/ACCESS_COARSE_LOCATION
hoặc thiếu quyền khai báo trong Manifest. Đảm bảo bạn đã khai báo quyền trong Manifest VÀ yêu cầu quyền từ người dùng lúc chạy ứng dụng. - Không lấy được vị trí cuối cùng: Vị trí cuối cùng có thể không tồn tại nếu thiết bị vừa khởi động hoặc dịch vụ vị trí bị tắt. Bạn có thể cần yêu cầu cập nhật vị trí mới thay thế.
- Thiết bị không có Google Play Services: Ứng dụng sẽ không thể sử dụng các API này. Bạn nên kiểm tra sự khả dụng của Google Play Services lúc khởi động ứng dụng và thông báo cho người dùng nếu cần.
Kết luận
Tích hợp Google Maps và Google Play Services là một kỹ năng quan trọng đối với bất kỳ nhà phát triển Android nào. Với bộ API mạnh mẽ này, bạn có thể thêm các tính năng bản đồ và vị trí phức tạp vào ứng dụng của mình một cách hiệu quả. Chúng ta đã đi qua các bước cơ bản từ thiết lập dự án, lấy API key, hiển thị bản đồ, làm việc với dữ liệu vị trí, thêm marker và đường vẽ, cũng như xử lý tương tác người dùng.
Chủ đề này mở ra cánh cửa cho rất nhiều khả năng phát triển ứng dụng thú vị. Hãy dành thời gian thực hành, thử nghiệm với các tùy chọn cấu hình bản đồ khác nhau, và khám phá các API nâng cao hơn của Google Maps Platform như Geocoding (chuyển đổi tọa độ sang địa chỉ và ngược lại), Places API (tìm kiếm địa điểm), v.v.
Đây là một bước tiến quan trọng trong lộ trình của bạn để trở thành một lập trình viên Android chuyên nghiệp. Đừng ngần ngại tham khảo tài liệu chính thức của Google Maps Platform và Google Play Services để tìm hiểu sâu hơn về từng API và các tính năng nâng cao.
Hẹn gặp lại các bạn trong bài viết tiếp theo của series “Android Developer Roadmap”!