Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách đơn giản nhất lấy vị trí hiện tại và tên thành phố trên Android

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ỉ"
}