2016-06-16

【Android Studio】取得自己的 Android 裝置的定位

參考資料 ----
Getting the Last Known Location
GoogleApiClient
android-play-location - samples
Table 1. Individual APIs and corresponding build.gradle descriptions.

要取得定位,必須用到 Google Play Services,所以先檢查 SDK Manager,是否已安裝 Google Play Services。



並且使用的 Android 實體裝置必須在 2.3.3(API 10) 以上,並且有安裝 Google Play;
模擬器則須在 4.2.2(API 17) 以上(在模擬器測試操作頗麻煩,得先執行一次 Google 地圖,再執行自己的 app,才會顯示定位數字,所以建議在實體 Android 裝置測試、練習)。


要引用 Google Play Services API,編輯 "程式" 層級的 build.gradle,加入

compile 'com.google.android.gms:play-services-location:8.1.0'

存檔,並按一下 工具列 的 Sync Project with Gradle Files

如下圖:




插花小提示:
Google Play Services API 的版本數字不用太新
有時太新反而會因模擬器內建的版本跟不上,造成 app 無法正常執行 Google Play Services API,以這次練習而言,上圖就是我原本設定的版本數 9.0.2,在模擬器執行時,app 本身表面看起來是正常運行的,但 Android Monitor 會顯示如下圖的訊息,所以我就降成 8.1.0。



至於版本數字要設什麼,請看一下

\sdk 安裝目錄\extras\google\m2repository\com\google\android\gms\play-services-location
(各人安裝設定不同,如果您的目錄位置和我的不一樣,那可能得自己找找了...)

這裡存放著您有 Google Play Services API 的各個版本,每個版本一個目錄

 



注意:
儘量不要加入所有的 Google Play Services API,也就是說,不要寫下面這種形式 ----

compile 'com.google.android.gms:play-services:8.1.0'

這樣很容易超出 65K 限制,在您按下 Sync Project with Gradle Files 時會回應錯誤,建議只加入您所需要的 Google Play Services。


開放權限
 
<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android"
    ... >

    ...
    ...

    <!-- 精確定位,就是 GPS -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- 約略定位,就是 WI-FI -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    ...
    ...
 

設定 activity_main.xml 版面
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- 經度  -->
    <TextView
        android:id="@+id/longitude_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:textIsSelectable="true" />

    <!-- 緯度  -->
    <TextView
        android:id="@+id/latitude_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:textIsSelectable="true" />

</LinearLayout>
 

GoogleApiClient 提供了 Builder 和 ConnectionCallbacks、OnConnectionFailedListener 兩個 Interface,所以我們要以 Builder 建立 GoogleApiClient 這個 instance 和 實做 ConnectionCallbacks、OnConnectionFailedListener。


MainActivity.java
 
...
...

// 記得 import 下面這幾個
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationServices;

public class LocationMainActivity extends AppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener
{
    protected static final String TAG = "MainActivity";

    protected GoogleApiClient mGoogleApiClient;
    protected Location mLastLocation;

    protected String mLatitudeLabel;
    protected String mLongitudeLabel;
    protected TextView mLatitudeText;
    protected TextView mLongitudeText;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_location_main);

        mLatitudeLabel = "緯度";
        mLongitudeLabel = "經度";
        mLatitudeText = (TextView) findViewById((R.id.latitude_text));
        mLongitudeText = (TextView) findViewById((R.id.longitude_text));

        buildGoogleApiClient();
    }

    protected synchronized void buildGoogleApiClient()
    {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    protected void onStart()
    {
        super.onStart();
        mGoogleApiClient.connect();
    }

    @Override
    protected void onStop() 
    {
        super.onStop();
        if (mGoogleApiClient.isConnected()) 
        {
            mGoogleApiClient.disconnect();
        }
    }


    // 當 GoogleApiClient 連上 Google Play Service 後要執行的動作
    @Override
    public void onConnected(Bundle connectionHint)
    {
        // 這行指令在 IDE 會出現紅線,不過仍可正常執行,可不予理會
        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (mLastLocation != null)
            {
                mLatitudeText.setText(String.format("%s: %f", mLatitudeLabel, mLastLocation.getLatitude()));
                mLongitudeText.setText(String.format("%s: %f", mLongitudeLabel, mLastLocation.getLongitude()));
            }
        else
            {
                Toast.makeText(this, "偵測不到定位,請確認定位功能已開啟。", Toast.LENGTH_LONG).show();
            }
    }

    @Override
    public void onConnectionFailed(ConnectionResult result)
    {
        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
    }


    @Override
    public void onConnectionSuspended(int cause)
    {
        Log.i(TAG, "Connection suspended");
        mGoogleApiClient.connect();
    }
}

 

這個小程式只能在程式啟動時取得定位,不會隨時更新。


相關筆記 ----
在模擬器取得定位值的操作
Google 地圖 app 速成篇