Giới thiệu
Ví dụ này minh họa cách lấy vị trí hiện tại (latitude, longitude) và chuyển đổi thành địa chỉ đọc được (tên đường, quận, thành phố, quốc gia) sử dụng FusedLocationProviderClient và Geocoder trong IntentService (background thread).
Bước 1: Tạo dự án mới
Tạo project Android Studio: File → New Project.
Bước 2: Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_marginTop="20dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Lấy vị trí hiện tại và tên thành phố"
android:textAlignment="center"
android:layout_centerHorizontal="true"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:layout_centerInParent="true"
android:textSize="16sp"
android:textStyle="bold"/>
</RelativeLayout>
Bước 3: Dependency (build.gradle)
dependencies {
implementation 'com.google.android.gms:play-services-location:17.0.0'
// Hoặc phiên bản mới hơn: 21.0.1+
}
Bước 4: MainActivity - Yêu cầu vị trí và nhận callback
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
public class MainActivity extends AppCompatActivity {
private FusedLocationProviderClient fusedLocationClient;
private static final int LOCATION_PERMISSION_REQUEST_CODE = 2;
private LocationAddressResultReceiver addressResultReceiver;
private TextView currentAddTv;
private Location currentLocation;
private LocationCallback locationCallback;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addressResultReceiver = new LocationAddressResultReceiver(new Handler());
currentAddTv = findViewById(R.id.textView);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
currentLocation = locationResult.getLocations().get(0);
getAddress();
}
};
startLocationUpdates();
}
@SuppressWarnings("MissingPermission")
private void startLocationUpdates() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
} else {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setInterval(2000);
locationRequest.setFastestInterval(1000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
}
@SuppressWarnings("MissingPermission")
private void getAddress() {
if (!Geocoder.isPresent()) {
Toast.makeText(MainActivity.this, "Không tìm thấy địa chỉ", Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent(this, GetAddressIntentService.class);
intent.putExtra("add_receiver", addressResultReceiver);
intent.putExtra("add_location", currentLocation);
startService(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLocationUpdates();
} else {
Toast.makeText(this, "Quyền vị trí không được cấp", Toast.LENGTH_SHORT).show();
}
}
}
private class LocationAddressResultReceiver extends ResultReceiver {
LocationAddressResultReceiver(Handler handler) { super(handler); }
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 0) {
Log.d("Address", "Vị trí null, thử lại");
getAddress();
} else if (resultCode == 1) {
Toast.makeText(MainActivity.this, "Không tìm thấy địa chỉ", Toast.LENGTH_SHORT).show();
}
String currentAdd = resultData.getString("address_result");
showResults(currentAdd);
}
}
private void showResults(String currentAdd) {
currentAddTv.setText(currentAdd);
}
@Override
protected void onResume() {
super.onResume();
startLocationUpdates();
}
@Override
protected void onPause() {
super.onPause();
fusedLocationClient.removeLocationUpdates(locationCallback);
}
}
Bước 5: GetAddressIntentService - Geocoding ngầm (background)
Dùng IntentService (deprecated API 30, thay bằng WorkManager hoặc Thread/Executor) để chạy Geocoder không block UI thread.
package app.com.sample;
import android.app.IntentService;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.util.Log;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import androidx.annotation.Nullable;
public class GetAddressIntentService extends IntentService {
private static final String IDENTIFIER = "GetAddressIntentService";
private ResultReceiver addressResultReceiver;
public GetAddressIntentService() { super(IDENTIFIER); }
@Override
protected void onHandleIntent(@Nullable Intent intent) {
addressResultReceiver = Objects.requireNonNull(intent).getParcelableExtra("add_receiver");
if (addressResultReceiver == null) {
Log.e("GetAddressIntentService", "Không có receiver");
return;
}
Location location = intent.getParcelableExtra("add_location");
if (location == null) {
sendResultsToReceiver(0, "Không có vị trí");
return;
}
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
List addresses = null;
try {
addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
} catch (Exception ioException) {
Log.e("", "Lỗi lấy địa chỉ cho vị trí");
}
if (addresses == null || addresses.size() == 0) {
sendResultsToReceiver(1, "Không tìm thấy địa chỉ cho vị trí này");
} else {
Address address = addresses.get(0);
String addressDetails = address.getFeatureName() + "\n" +
address.getThoroughfare() + "\n" +
"Phường/Xã: " + address.getLocality() + "\n" +
"Quận/Huyện: " + address.getSubAdminArea() + "\n" +
"Tỉnh/Thành phố: " + address.getAdminArea() + "\n" +
"Quốc gia: " + address.getCountryName() + "\n" +
"Mã bưu chính: " + address.getPostalCode() + "\n";
sendResultsToReceiver(2, addressDetails);
}
}
private void sendResultsToReceiver(int resultCode, String message) {
Bundle bundle = new Bundle();
bundle.putString("address_result", message);
addressResultReceiver.send(resultCode, bundle);
}
}
Bước 6: Manifest - Service và quyền
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="app.com.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".GetAddressIntentService" />
</application>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
Chạy ứng dụng
Chạy trên thiết bị thật (cần GPS và Internet). TextView sẽ hiển thị địa chỉ đầy đủ: tên đường, phường, quận, thành phố, quốc gia.
Cách hiện đại (API 30+): Thay IntentService bằng Coroutine/Flow
// Trong ViewModel hoặc Repository
suspend fun getAddressFromLocation(location: Location): String = withContext(Dispatchers.IO) {
val geocoder = Geocoder(context, Locale.getDefault())
val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)
addresses?.firstOrNull()?.let { address ->
// format address...
} ?: "Không tìm thấy địa chỉ"
}