Android 創(chuàng)建和監(jiān)視地理圍欄

2018-08-02 17:47 更新

編寫:penkzhou - 原文:http://developer.android.com/training/location/geofencing.html

地理圍欄將用戶當(dāng)前位置感知和附件地點(diǎn)特征感知相結(jié)合。為了標(biāo)示一個感興趣的位置,我們需要指定這個位置的經(jīng)緯度。為了調(diào)整位置的鄰近度,需要添加一個半徑。經(jīng)緯度和半徑定義一個地理圍欄,即在感興趣的位置創(chuàng)建一個圓形區(qū)域或者圍欄。

我們可以有多個活動的地理圍欄(限制是一個設(shè)備用戶100個)。對于每個地理圍欄,我們可以讓 Location Services 發(fā)出進(jìn)入和離開事件,或者我們可以在觸發(fā)一個事件之前,指定在某個地理圍欄區(qū)域等待一段時間或者停留。通過指定一個以毫秒為單位的截止時間,我們可以限制任何一個地理圍欄的持續(xù)時間。當(dāng)?shù)乩韲鷻谑Ш螅琇ocation Services 會自動刪除這個地理圍欄。

geofence

這節(jié)課介紹如何添加和刪除地理圍欄,和用 IntentService 監(jiān)聽地理位置變化。

設(shè)置地理圍欄監(jiān)視

請求地理圍欄監(jiān)視的第一步就是設(shè)置必要的權(quán)限。在使用地理圍欄時,我們必須設(shè)置 ACCESS_FINE_LOCATION 權(quán)限。在應(yīng)用的 manifest 文件中添加如下子節(jié)點(diǎn)即可:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

如果想要用 IntentService 監(jiān)聽地理位置變化,那么還需要添加一個節(jié)點(diǎn)來指定服務(wù)名字。這個節(jié)點(diǎn)必須是 的子節(jié)點(diǎn):

<application
   android:allowBackup="true">
   ...
   <service android:name=".GeofenceTransitionsIntentService"/>
<application/>

為了訪問位置 API,我們需要創(chuàng)建一個 Google Play services API client 的實(shí)例。想要學(xué)習(xí)如何連接 client,請見連接Google Play Services。

創(chuàng)建和添加地理圍欄

我們的應(yīng)用需要用位置 API 的 builder 類來創(chuàng)建地理圍欄,用 convenience 類來添加地理圍欄。另外,我們可以定義一個 PendingIntent(將在這節(jié)課介紹)來處理當(dāng)?shù)乩砦恢冒l(fā)生遷移時,Location Services 發(fā)出的 intent。

創(chuàng)建地理圍欄對象

首先,用 Geofence.Builder 創(chuàng)建一個地理圍欄,設(shè)置想要的半徑,持續(xù)時間,和地理圍欄遷移的類型。例如,填充一個叫做 mGeofenceList 的 list 對象:

mGeofenceList.add(new Geofence.Builder()
    // Set the request ID of the geofence. This is a string to identify this
    // geofence.
    .setRequestId(entry.getKey())

    .setCircularRegion(
            entry.getValue().latitude,
            entry.getValue().longitude,
            Constants.GEOFENCE_RADIUS_IN_METERS
    )
    .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
            Geofence.GEOFENCE_TRANSITION_EXIT)
    .build());

這個例子從一個固定的文件中獲取數(shù)據(jù)。在實(shí)際情況下,應(yīng)用可能會根據(jù)用戶的位置動態(tài)地創(chuàng)建地理圍欄。

指定地理圍欄和初始化觸發(fā)器

下面的代碼用到 GeofencingRequest 類。該類嵌套了 GeofencingRequestBuilder 類來需要監(jiān)視的地理圍欄和設(shè)置如何觸發(fā)地理圍欄事件:

private GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(mGeofenceList);
    return builder.build();
}

這個例子介紹了兩個地理圍欄觸發(fā)器。當(dāng)設(shè)備進(jìn)入一個地理圍欄時, GEOFENCE_TRANSITION_ENTER 轉(zhuǎn)移會觸發(fā)。當(dāng)設(shè)備離開一個地理圍欄時, GEOFENCE_TRANSITION_EXIT 轉(zhuǎn)移會觸發(fā)。如果設(shè)備已經(jīng)在地理圍欄里面,那么指定 INITIAL_TRIGGER_ENTER 來通知位置服務(wù)觸發(fā) GEOFENCE_TRANSITION_ENTER。

在很多情況下,使用 INITIAL_TRIGGER_DWELL 可能會更好。僅僅當(dāng)由于到達(dá)地理圍欄中已定義好的持續(xù)時間,而導(dǎo)致用戶停止時,INITIAL_TRIGGER_DWELL 才會觸發(fā)事件。這個方法可以減少當(dāng)設(shè)備短暫地進(jìn)入和離開地理圍欄時,由大量的通知造成的“垃圾警告信息”。另一種獲取最好的地理圍欄結(jié)果的策略是設(shè)置最小半徑為100米。這有助于估計典型的 Wifi 網(wǎng)絡(luò)的位置精確度,也有利于降低設(shè)備的功耗。

為地理圍欄轉(zhuǎn)移定義Intent

從 Location Services 發(fā)送來的Intent能夠觸發(fā)各種應(yīng)用內(nèi)的動作,但是不能用它來打開一個 Activity 或者 Fragment,這是因為應(yīng)用內(nèi)的組件只能在響應(yīng)用戶動作時才可見。大多數(shù)情況下,處理這一類 Intent 最好使用 IntentService。一個 IntentService 可以推送一個通知,可以進(jìn)行長時間的后臺作業(yè),可以將 intent 發(fā)送給其他的 services ,還可以發(fā)送一個廣播 intent。下面的代碼展示了如何定義一個 PendingIntent 來啟動一個 IntentService:

public class MainActivity extends FragmentActivity {
    ...
    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (mGeofencePendingIntent != null) {
            return mGeofencePendingIntent;
        }
        Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        return PendingIntent.getService(this, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
    }

添加地理圍欄

使用 GeoencingApi.addGeofences() 方法來添加地理圍欄。為該方法提供 Google API client,GeofencingRequest 對象和 PendingIntent。下面的代碼,在 onResult()) 中處理結(jié)果,假設(shè)主 activity 實(shí)現(xiàn) ResultCallback。

public class MainActivity extends FragmentActivity {
    ...
    LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                getGeofencingRequest(),
                getGeofencePendingIntent()
        ).setResultCallback(this);

處理地理圍欄轉(zhuǎn)移

當(dāng) Location Services 探測到用戶進(jìn)入或者離開一個地理圍欄,它會發(fā)送一個包含在 PendingIntent 的 Intent,這個 PendingIntent 就是在添加地理圍欄時被我們包括在請求當(dāng)中。這個 Intent 被一個類似 GeofenceTransitionsIntentService 的 service 接收,這個 service 從 intent 得到地理圍欄事件,決定地理圍欄轉(zhuǎn)移的類型,和決定觸發(fā)哪個已定義的地理圍欄。然后它會發(fā)出一個通知。

下面的代碼介紹了如何定義一個 IntentService。這個 IntentService 在地理圍欄轉(zhuǎn)移出現(xiàn)時,會推送一個通知。當(dāng)用戶點(diǎn)擊這個通知,那么應(yīng)用的主 activity 會出現(xiàn):

public class GeofenceTransitionsIntentService extends IntentService {
   ...
    protected void onHandleIntent(Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            String errorMessage = GeofenceErrorMessages.getErrorString(this,
                    geofencingEvent.getErrorCode());
            Log.e(TAG, errorMessage);
            return;
        }

        // Get the transition type.
        int geofenceTransition = geofencingEvent.getGeofenceTransition();

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            List triggeringGeofences = geofencingEvent.getTriggeringGeofences();

            // Get the transition details as a String.
            String geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            );

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails);
            Log.i(TAG, geofenceTransitionDetails);
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition));
        }
    }

在通過 PendingIntent 檢測轉(zhuǎn)移事件之后,這個 IntentService 獲取地理圍欄轉(zhuǎn)移類型和測試一個事件是不是應(yīng)用用來觸發(fā)通知的 —— 要么是 GEOFENCE_TRANSITION_ENTER,要么是 GEOFENCE_TRANSITION_EXIT。然后,這個 service 會發(fā)出一個通知并且記錄轉(zhuǎn)移的詳細(xì)信息。

停止地理圍欄監(jiān)視

當(dāng)不再需要監(jiān)視地理圍欄或者想要節(jié)省設(shè)備的電池電量和 CPU 周期時,需要停止地理圍欄監(jiān)視。我們可以在用于添加和刪除地理圍欄的主 activity 里停止地理圍欄監(jiān)視;刪除地理圍欄會導(dǎo)致它馬上停止。API 要么通過 request IDs,要么通過刪除與指定 PendingIntent 相關(guān)的地理圍欄來刪除地理圍欄。

下面的代碼通過 PendingIntent 刪除地理圍欄,當(dāng)設(shè)備進(jìn)入或者離開之前已經(jīng)添加的地理圍欄時,停止所有通知:

LocationServices.GeofencingApi.removeGeofences(
            mGoogleApiClient,
            // This is the same pending intent that was used in addGeofences().
            getGeofencePendingIntent()
    ).setResultCallback(this); // Result processed in onResult().
}

你可以將地理圍欄同其他位置感知的特性結(jié)合起來,比如周期性的位置更新。像要了解更多的信息,請看本章的其它課程。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號