Android location API using Google Play services

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.

android sdk google play services.png

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

Screenshot_20161227-000108.png

<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