Android location API using Google Play services
Bài đăng này đã không được cập nhật trong 3 năm
1. Giới thiệu về Android Location
Một trong những tiện ích của điện thoại là tính năng vị trí. Để có thể lấy được vị trí người dùng trên Android thì có 2 cách để thực hiện:
- Cách thứ nhất là sử dụng Android Location API
- Cách thứ hai là sử dụng Google Play services API
Android Location API sử dụng 3 nguồn cung cấp để lấy vị trí là:
- LocationManager.GPS_PROVIDER -- nguồn này xác định vị trí dựa trên vệ tinh. Tùy thuộc vào điều kiện trong nhà hay ngoài trời mà nguồn này cần một khoảng thời gian để có thể trả về vị trí hiện tại với độ chính xác phụ thuộc.
- LocationManager.NETWORK_PROVIDER -- nguồn này xác định vị trí dựa trên cột thu phát sóng của mạng di động và các điểm truy cập WIFI. Két quả vị trí thu được nhờ tìm kiếm trong mạng.
- LocationManager.PASSIVE_PROVIDER -- nguồn này trả về vị trí sinh ra bởi các nguồn khác. Ví dụ bạn nhận được vị trí cập nhật một cách thụ động khi mà các ứng dụng khác hoặc dịch vụ khác yêu cầu mà ko phải bạn thực sự yêu cầu điều này.
Google Play services location API là cách mới mà Google giới thiệu để có thể lấy được vị trí hiện tại của thiết bị. API này dùng "Fused Location Provider" mà tự động chọn nguồn cung cấp vị trí để sử dụng dựa trên độ chính xác và mức tiêu thụ pin. API này cũng được Google khuyến khích sử dụng thay thế cho Android Location API cũ. Cách sử dụng API này để lấy vị trí sẽ được trình bày trong phần 2.
2. Google Play services location API
Vì sử dụng Google Play services để lấy vị trí nên bạn cần có Google Play services.
Với Android Studio bạn cần mở SDK Manager và tải gói Google Play services.
Với thiết bị để test thì bạn có thể sử dụng một trong các cách sau đây:
-
Tải Android Emulator đã tích hợp sẵn Google Play services
-
Cài đặt Google Play services cho máy ảo Genymotion
-
Sử dụng thiết bị thật có Google Play services (hầu như đều có sẵn)
Sau đây tôi xin hướng dẫn một ví dụ sử dụng Google Play services để lấy location
-
Trước tiên các bạn tạo một project mới
-
Thêm Google Play Location services vào dependencies của file Build.gradle
Build.gradle
dependencies {
compile 'com.google.android.gms:play-services-location:10.0.1'
}
Thêm location permission vào Manifest
Manifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- File layout, do chỉ làm với mục đích demo nên tôi để hard code string và dimen, khi các bạn code cần chuyển vào file strings.xml và dimens.xml
activity_maim.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.quanda.googleplayserviceslocaiton.MainActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Current location"
android:textColor="@android:color/black"
android:textSize="30sp"/>
<TextView
android:id="@+id/tv_current_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="HaNoi!"
android:textColor="@android:color/black"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_get_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_current_location"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="get location"/>
<Switch
android:id="@+id/sw_auto_update_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btn_get_location"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:switchPadding="20dp"
android:text="Update location automatically"/>
</RelativeLayout>
Và đây là file class MainActivity
-
Để chạy với Android 6.0 thì bạn cần thêm check runtime permission requestLocationPermissions() và xử lý kết quả trong onRequestPermissionsResult()
-
Trước khi dùng Google Play services thì bạn cũng cần phaỉ check xem thiết bị có hỗ trợ không qua isPlayServicesAvailable() thì mới tiến hành setUpLocationClientIfNeeded() và buildLocationRequest()
-
Với các tác vụ có liên quan đến location thì bạn cần check xem GPS có được bật không trước khi gọi tác vụ.
MainActivity.java
package com.example.quanda.googleplayserviceslocaiton;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.util.Locale;
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener {
public static final String TAG = MainActivity.class.getSimpleName();
private static final long UPDATE_INTERVAL = 5000;
private static final long FASTEST_INTERVAL = 5000;
private static final int REQUEST_LOCATION_PERMISSION = 100;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private Location mLastLocation;
private boolean mIsAutoUpdateLocation;
private TextView mTvCurrentLocation;
private Button mBtnGetLocation;
private Switch mSwAutoUpdateLocation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate");
initViews();
requestLocationPermissions();
if (isPlayServicesAvailable()) {
setUpLocationClientIfNeeded();
buildLocationRequest();
} else {
mTvCurrentLocation.setText("Device does not support Google Play services");
}
mBtnGetLocation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isGpsOn()) {
updateUi();
} else {
Toast.makeText(MainActivity.this, "GPS is OFF", Toast.LENGTH_SHORT).show();
}
}
});
mSwAutoUpdateLocation.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (!isGpsOn()) {
Toast.makeText(MainActivity.this, "GPS is OFF",
Toast.LENGTH_SHORT).show();
mSwAutoUpdateLocation.setChecked(false);
return;
}
mIsAutoUpdateLocation = isChecked;
if (isChecked) {
startLocationUpdates();
} else {
stopLocationUpdates();
}
}
});
}
private void initViews() {
mTvCurrentLocation = (TextView) findViewById(R.id.tv_current_location);
mBtnGetLocation = (Button) findViewById(R.id.btn_get_location);
mSwAutoUpdateLocation = (Switch) findViewById(R.id.sw_auto_update_location);
}
private void updateUi() {
if (mLastLocation != null) {
mTvCurrentLocation.setText(String.format(Locale.getDefault(), "%f, %f",
mLastLocation.getLatitude(), mLastLocation.getLongitude()));
}
}
private void requestLocationPermissions() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_LOCATION_PERMISSION);
}
}
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_LOCATION_PERMISSION:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
requestLocationPermissions();
}
break;
default:
break;
}
}
private boolean isPlayServicesAvailable() {
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this)
== ConnectionResult.SUCCESS;
}
private boolean isGpsOn() {
LocationManager manager = (LocationManager) getSystemService(LOCATION_SERVICE);
return manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
private void setUpLocationClientIfNeeded() {
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
private void buildLocationRequest() {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
}
protected void startLocationUpdates() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
@Override
protected void onStart() {
super.onStart();
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
}
@Override
protected void onStop() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
@Override
public void onConnected(Bundle bundle) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (lastLocation != null) {
mLastLocation = lastLocation;
}
}
@Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
@Override
public void onLocationChanged(Location location) {
Log.d(TAG, String.format(Locale.getDefault(), "onLocationChanged : %f, %f",
location.getLatitude(), location.getLongitude()));
mLastLocation = location;
if (mIsAutoUpdateLocation) {
updateUi();
}
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
@Override
public void onDestroy() {
if (mGoogleApiClient != null
&& mGoogleApiClient.isConnected()) {
stopLocationUpdates();
mGoogleApiClient.disconnect();
mGoogleApiClient = null;
}
Log.d(TAG, "onDestroy LocationService");
super.onDestroy();
}
}
Trong ví dụ trên tôi đã trình bày về một ví dụ sử dụng Google Play services để lấy vị trí
-
Button Get Location dùng để hiển thị vị trí lastLocation lấy được từ device
-
Switch Auto Update Location được bật lên thì sẽ tự động cập nhật vị trí mới và hiển thị ở TextView
Từ việc lấy được vị trí hiện tại của người dùng, ta có thể sử dụng vào một số trường hợp như:
-
Hiển thị vị trí trên bản đồ.
-
Convert tọa dộ sang địa chỉ và hiển thị địa chỉ
-
Cài đặt phần lấy vị trí trong service để chạy background, sử dụng vào việc định vị thiết bị di động và các thiết bị có vị trí khác.
-
...
All rights reserved