Androidでジオフェンスを操作する



こんにちは、ハブロフスクの住民。 今日は、Google I / 0 2013( ビデオプレゼンテーション )で発表されたLocation API全般と特にジオフェンスについてお話したいと思います 。 このイベントは6か月以上前に行われたという事実にもかかわらず、ハブにはこれに関する健全な情報はまだありません( 1つの言及のみ)。 私は状況を少し修正しようとします。



Location APIとは何ですか?



ロケーションAPIは、デバイスのロケーションと連携するアプリケーションを作成するように設計されたGoogle Play開発者サービスの一部です。 LocationManagerの同様の機能とは異なり、これらのAPIはエネルギー効率を改善しました。 現在、次の機能を使用できます。デバイスの場所の特定、ジオフェンスの操作、ユーザーアクティビティの認識。 ポジショニングを使用すると、精度とエネルギー消費のバランスを取ることができ、最も一般的な場所へのアクセスも可能になります。 アクティビティを認識することで、デバイスのユーザーが何をしているのかを知ることができます。車に乗る、自転車に乗る、歩く、一箇所にいる、などです。 実際、ジオフェンスを使用すると、デバイスユーザーが特定のゾーンに入ったとき、ゾーンを離れたとき、または一定の時間そのゾーンにいるときにメッセージを送信できます。

私の意見では、 公式の例は非常に複雑で分かりにくいものです。 これは、その中にあるという事実によるものです。



これに基づいて、この記事ではジオフェンスのみに焦点を当て、いくつかの例外処理を省略します。



:デバイスでGoogle Playサービスが無効になっている場合があります。 これにより、多くのアプリケーションの動作が混乱する可能性があり、システムはユーザーに無効にする前に正直に警告します。 それでも、 GooglePlayServicesUtil.isGooglePlayServicesAvailableを使用してアプリケーションでこれを確認し、何らかの方法でユーザーに警告することをお勧めします。



挑戦する



そのため、例として、ジオフェンスの座標と半径を明示的に指定できるアプリケーションを作成します。 入場/退場すると、ジオフェンスIDと移動タイプの通知がステータスバーに追加されます。 ジオフェンスを離れた後、それを削除します。



せっかちな人のためのソース




アルゴリズム



一般に、プロセスは次のとおりです。

  1. アクティビティから、ジオフェンスに関するデータを転送するサービスを作成します。
  2. サービスはLocationClientを初期化します。
  3. LocationClientが初期化されたら、 ジオフェンスとそれに対応するPendingIntentを追加します
  4. ジオフェンスが追加されると、 LocationClientから切断し、サービスを停止します。
  5. さらに、ゾーンへの出入り時にIntentServiceを起動するPendingIntentに期待しています。 サービスは通知をステータスバーに追加し、ワークアウトされたジオフェンスを削除するサービスを作成します。
  6. 作成されたサービスは、 LocationClientを再度初期化します。
  7. LocationClientが初期化されると、使用済みのジオフェンスが削除されます。
  8. ジオフェンスが削除されたら、LocationClientから切断し、サービスを停止します。
  9. 利益!


ご覧のとおり、メインキャラクターはLocationClientです。 彼は、ジオフェンスの検索と操作のためにAPIにアクセスする責任があります。



ビジネスに!



まず、Google Play開発者サービスを接続する必要があります。 これを行う方法については、 ここで説明します

次に、アクティビティで、表示要素を初期化します。 この領域から、ボタンクリックの処理時にサービスを呼び出すことに興味があります。



int transitionType = Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT; MyGeofence myGeofence = new MyGeofence(mId, latitude, longitude, radius, transitionType); Intent geofencingService = new Intent(activity, GeofencingService.class); geofencingService.putExtra(GeofencingService.EXTRA_ACTION, GeofencingService.Action.ADD); geofencingService.putExtra(GeofencingService.EXTRA_GEOFENCE, myGeofence); activity.startService(geofencingService);
      
      





ここで、サービス( GeofencingService )のインテントを作成し、必要なデータをそこに転送します。 GeofencingServiceジオフェンスの追加と削除を担当するため(この例では、これらのアクションを異なるサービスに分割しないことにしました)、サービスで実行する必要がある操作のタイプを転送する必要があります。 この場合、このアドオン( GeofencingService.Action.ADD )。 このサービスにはジオフェンスデータも必要です。 基本的にGeofence.BuilderのラッパーであるMyGeofenceクラスのオブジェクトとしてそれらを渡します(これについては後で説明します)。

したがって、中心の座標とゾーンの半径、および移動のタイプを送信します。 後者には、GEOFENCE_TRANSITION_ENTER、GEOFENCE_TRANSITION_EXIT、GEOFENCE_TRANSITION_DWELLの3つのタイプがあります。 最初の2つですべてが明確な場合は、3つ目の説明が必要です。 GEOFENCE_TRANSITION_DWELLは、ユーザーがゾーンに入って、しばらく時間を過ごしたことを示します。 この信号を使用するには、ジオフェンスを構築するときにsetLoiteringDelayを設定する必要があります。 この例では、GEOFENCE_TRANSITION_DWELLは使用されていません。



サービスに移りましょう。 このサービスは、 GooglePlayServicesClient.ConnectionCallbacksGooglePlayServicesClient.OnConnectionFailedListenerLocationClient.OnAddGeofencesResultListenerLocationClient.OnRemoveGeofencesResultListenerインターフェースを実装します。 これにより、彼はLocationClientの操作に完全に責任を持つことができます。

onStartCommandでは 、操作の種類(ADDまたはREMOVE)を取得し、このアクションを実行するために必要なデータを取得します。 その後、 LocationClientを初期化して実行します。



  mAction = (Action) intent.getSerializableExtra(EXTRA_ACTION); switch (mAction) { case ADD: MyGeofence newGeofence = (MyGeofence) intent.getSerializableExtra(EXTRA_GEOFENCE); mGeofenceListsToAdd.add(newGeofence.toGeofence()); break; case REMOVE: mGeofenceListsToRemove = Arrays.asList(intent.getStringArrayExtra(EXTRA_REQUEST_IDS)); break; } mLocationClient = new LocationClient(this, this, this); mLocationClient.connect();
      
      





mGeofenceListsToAddジオフェンスを追加する前に、 MyGeofenceクラスオブジェクトの toGeofence()メソッドを呼び出しましたMyGeofenceGeofence.Builderのラッパーであると既に述べました



  public MyGeofence(int id, double latitude, double longitude, float radius, int transitionType) { this.id = id; this.latitude = latitude; this.longitude = longitude; this.radius = radius; this.transitionType = transitionType; } public Geofence toGeofence() { return new Geofence.Builder() .setRequestId(String.valueOf(id)) .setTransitionTypes(transitionType) .setCircularRegion(latitude, longitude, radius) .setExpirationDuration(ONE_MINUTE) .build(); }
      
      





Geofence.Builderは、 Geofenceを作成するためのユーティリティクラスです。 必要なパラメーターを設定し、 build()メソッドを呼び出してオブジェクトを作成します。 上記は必須の最小パラメーターです。 ここでは、 setExpirationDurationに注意する価値があります。 事実、登録されたジオフェンスは、指定された時間の後、または明示的な削除の2つの場合にのみ削除できます。 したがって、パラメータとしてNEVER_EXPIREを渡す場合は、オブジェクトを自分で削除する必要があります。 ロケーションAPIには制限があります :一度にアプリケーションごとに最大100ジオフェンス。



LocationClientの接続後、 GooglePlayServicesClient.ConnectionCallbacksインターフェースコールバックはonConnectedで機能します。 その中で、現在のアクションのタイプに応じて追加または削除します。



  @Override public void onConnected(Bundle bundle) { Log.d("GEO", "Location client connected"); switch (mAction) { case ADD: Log.d("GEO", "Location client adds geofence"); mLocationClient.addGeofences(mGeofenceListsToAdd, getPendingIntent(), this); break; case REMOVE: Log.d("GEO", "Location client removes geofence"); mLocationClient.removeGeofences(mGeofenceListsToRemove, this); break; } }
      
      





ご覧のとおり、 addGeofencesパラメーターの1 つにはPendingIntentが必要です。これは移動時に機能します。 この場合、 PendingIntentIntentServiceを開始します



  private PendingIntent getPendingIntent() { Intent transitionService = new Intent(this, ReceiveTransitionsIntentService.class); return PendingIntent.getService(this, 0, transitionService, PendingIntent.FLAG_UPDATE_CURRENT); }
      
      





アクションが完了すると、 OnAddGeofencesResultListenerまたはonRemoveGeofencesByRequestIdsResultがトリガーされ、 LocationClientから切断してサービスを停止します。



  @Override public void onAddGeofencesResult(int i, String[] strings) { if (LocationStatusCodes.SUCCESS == i) { Log.d("GEO", "Geofences added " + strings); for (String geofenceId : strings) Toast.makeText(this, "Geofences added: " + geofenceId, Toast.LENGTH_SHORT).show(); mLocationClient.disconnect(); stopSelf(); } else { Log.e("GEO", "Error while adding geofence: " + strings); } } @Override public void onRemoveGeofencesByRequestIdsResult(int i, String[] strings) { if (LocationStatusCodes.SUCCESS == i) { Log.d("GEO", "Geofences removed" + strings); mLocationClient.disconnect(); stopSelf(); } else { Log.e("GEO", "Error while removing geofence: " + strings); } }
      
      





アプリケーションの最後の部分は、デバイスユーザーがジオフェンスの境界を越えると開始するIntentServiceです。 すべてのアクションはonHandleIntentで実行されます



  @Override protected void onHandleIntent(Intent intent) { if (LocationClient.hasError(intent)) { Log.e(TRANSITION_INTENT_SERVICE, "Location Services error: " + LocationClient.getErrorCode(intent)); return; } int transitionType = LocationClient.getGeofenceTransition(intent); List<Geofence> triggeredGeofences = LocationClient.getTriggeringGeofences(intent); List<String> triggeredIds = new ArrayList<String>(); for (Geofence geofence : triggeredGeofences) { Log.d("GEO", "onHandle:" + geofence.getRequestId()); processGeofence(geofence, transitionType); triggeredIds.add(geofence.getRequestId()); } if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) removeGeofences(triggeredIds); }
      
      





ここでは、主にLocationClientの静的メソッドを使用します。 まず、 hasErrorを使用してエラーをチェックします。 次にそれぞれgetGeofenceTransitiongetTriggeringGeofencesを使用して、動きのタイプとトリガーされたジオフェンスのリストを取得します。 各ジオフェンスの処理を呼び出し、そのIDを保存します。 そして最後に、この動きがジオフェンスの出口であった場合、ジオフェンスを削除します。

ジオフェンスを削除するには、操作タイプ(REMOVE)と削除用のIDリストを転送するサービスを再度作成します。



  private void removeGeofences(List<String> requestIds) { Intent intent = new Intent(getApplicationContext(), GeofencingService.class); String[] ids = new String[0]; intent.putExtra(GeofencingService.EXTRA_REQUEST_IDS, requestIds.toArray(ids)); intent.putExtra(GeofencingService.EXTRA_ACTION, GeofencingService.Action.REMOVE); startService(intent); }
      
      







それだけです!



この例が明確で興味深いものであることを願っています。 すべての良いアプリケーションをお祈りします!



更新:

この記事とコードは2年で非常に古くなっています。

リポジトリ内のコードを更新してくれたVilkamanに感謝します。

更新されたLocation APIの操作について詳しくは、彼の記事をご覧ください。



All Articles