場所に深く浸る

この投稿は、 android-developersブログのトピックの翻訳です。 ストーリーの残りの部分は、 Professional Android 2 Application Developmentの著者であるReto Mayerによるものです。 彼は、結果をキャッシュする、速度などの意味で、場所を使用するアプリケーションを改善する方法について書いています。



食事をする場所を探しているか、 Boris Bikeバイクに最も近い場所を探しているかは関係ありません。GPSから位置データを受信し、結果の抽象的なリストを空欄に記入するのは常に遅れます。 コンテキスト情報を受け取りたい場所にいると、データ接続が不足することがよくあります。



空で拳を脅かすのではなく、アプリケーションを開いてから近くの場所に関する最新情報を表示するまでの時間を短縮するためのヒントとコツを含むオープンソースアプリケーションを作成しました。 バッテリー使用量を可能な限り低く抑えながら、これらすべてを行います。



コードを見せてください!



Androidプロジェクトの場所に関するプロジェクトのヒントをご覧ください。 Readme.txtを読んで、アプリケーションを正常にコンパイルおよび実行してください



それは本当に何をしますか?



Google Places APIを使用して、場所を使用して近くのアトラクション(地図上のポイント)のリストを決定し、詳細を表示し、確認または評価できるアプリケーションの基本機能を実装します。



このコードは、Google I / O 2011でのセッションで詳しく説明したベストプラクティスの多くを実装しています。AndroidProtips:Advanced Topics for Expert Android Developersvideo )。 パッシブロケーションプロバイダーを使用してロケーション更新を受信するためのインテントの使用、デバイスのステータスの監視による更新頻度の変更、アプリケーション実行中のレシーバーの切り替え、カーソルローダーの使用など。



このアプリケーションはHoneycomb向けに書かれていますが、バージョン1.6以降でも動作します。



これでコードができました。詳しく見てみましょう。



私の主な優先事項は鮮度です。アプリケーションを開いてから適切な場所を確認するまでの遅延を最小限に抑えながら、バッテリーの使用を最小限に抑えます。



要件:



新鮮さは決して待たないことを意味する



アプリケーションがアクティブモードになるたびにロケーションマネージャーから最後に認識されたものを選択することにより、ロケーションの最初の近似までの待ち時間を大幅に短縮できます。



この例では、 GingerbreadLastLocationFinderから、現在利用できないものも含め、デバイス上のすべてのロケーションプロバイダーを反復処理して、最も正確な最終ロケーションを見つけます。



List<String> matchingProviders = locationManager.getAllProviders(); for (String provider: matchingProviders) { Location location = locationManager.getLastKnownLocation(provider); if (location != null) { float accuracy = location.getAccuracy(); long time = location.getTime(); if ((time > minTime && accuracy < bestAccuracy)) { bestResult = location; bestAccuracy = accuracy; bestTime = time; } else if (time < minTime && bestAccuracy == Float.MAX_VALUE && time > bestTime){ bestResult = location; bestTime = time; } } }
      
      





1つ以上の場所の座標がある場合、最も正確な場所が選択されます。 それ以外の場合、最新の結果が返されます。



2番目の場合(最新の場所の更新が十分ではないと判断される場合)、値は何らかの方法で返されますが、最速のプロバイダーを使用して1つの場所の更新を要求します。



 if (locationListener != null && (bestTime < maxTime || bestAccuracy > maxDistance)) { IntentFilter locIntentFilter = new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION); context.registerReceiver(singleUpdateReceiver, locIntentFilter); locationManager.requestSingleUpdate(criteria, singleUpatePI); }
      
      





残念ながら、最速のプロバイダーを特定することはできませんが、実際には、ネットワークを使用して場所を特定すると結果が早く返されることは明らかです。



また、このコードはGingerbreadLastLocationFinderを示していることに注意してください。これはrequestSingleUpdateメソッドを使用して、1回限りの位置更新を受信します。 この機能はGingerbreadまで利用できませんでした-LegacyLastLocationFinderをチェックして、以前のバージョンのAndroidに同じ機能を実装した方法を確認してください。



singleUpdateReceiverは、受信した更新をLocation Listenerリスナーを介して呼び出し元のクラスに返します。

 protected BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { context.unregisterReceiver(singleUpdateReceiver); String key = LocationManager.KEY_LOCATION_CHANGED; Location location = (Location)intent.getExtras().get(key); if (locationListener != null && location != null) locationListener.onLocationChanged(location); locationManager.removeUpdates(singleUpatePI); } };
      
      





インテントを使用して場所の更新を取得する



現在位置の最も正確でタイムリーな評価を受け取ったので、その更新も受け取りたいと思います。



PlacesConstantsクラスには、場所の更新頻度を決定する一連の値が含まれています。 必要な頻度で更新が到着するように設定します。



 // The default search radius when searching for places nearby. public static int DEFAULT_RADIUS = 150; // The maximum distance the user should travel between location updates. public static int MAX_DISTANCE = DEFAULT_RADIUS/2; // The maximum time that should pass before the user gets a location update. public static long MAX_TIME = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
      
      





次のステップは、ロケーションマネージャーからロケーションの更新を要求することです。 GingerbreadLocationUpdateRequesterから取得した次のコードフラグメントでは、 requestLocationUpdatesメソッド呼び出しで、どのLocation Managerが更新を直接要求するかを決定するための基準を渡すことができます



 public void requestLocationUpdates(long minTime, long minDistance, Criteria criteria, PendingIntent pendingIntent) { locationManager.requestLocationUpdates(minTime, minDistance, criteria, pendingIntent); }
      
      





Location Listenerではなく、Pending Intentを渡していることに注意してください。



 Intent activeIntent = new Intent(this, LocationChangedReceiver.class); locationListenerPendingIntent = PendingIntent.getBroadcast(this, 0, activeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
      
      





一般に、ロケーションリスナを使用することを好みます。複数のアクティビティまたはサービス、またはマニフェストで直接レシーバを登録する柔軟性を提供するためです。



このアプリケーションでは、新しい場所は近くの場所の更新されたリストを意味します。 これは、サーバーにリクエストを送信し、場所のリストを埋めるコンテンツプロバイダーを更新するサービスを通じて発生します。



場所を変更してもUIは直接更新されないため、アクティビティではなく、マニフェストで関連するLocationChangedReceiverを作成して登録することは理にかなっています。



 <receiver android:name=".receivers.LocationChangedReceiver"/>
      
      





LocationChangedReceiverは、各更新から位置を抽出し、 PlaceUpdateServiceサービスを開始して、近くの場所のデータベースを更新します。



 if (intent.hasExtra(locationKey)) { Location location = (Location)intent.getExtras().get(locationKey); Log.d(TAG, "Actively Updating place list"); Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class); updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location); updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS); updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, true); context.startService(updateServiceIntent); }
      
      





バッテリー寿命中にデータを受信



オフラインサポートを追加するには、 PlacesContentProviderおよびPlaceDetailsContentProviderの検索結果をキャッシュすることから始めます。



また、特定の状況では、位置情報を選択する必要があります。 PlacesUpdateServiceサービスのこのコードは、限られた数の場所でプリフェッチがどのように有効化されるかを示しています。



バッテリーが少なくなると、データのプリフェッチがオフになる可能性があることに注意してください。



 if ((prefetchCount < PlacesConstants.PREFETCH_LIMIT) && (!PlacesConstants.PREFETCH_ON_WIFI_ONLY || !mobileData) && (!PlacesConstants.DISABLE_PREFETCH_ON_LOW_BATTERY || !lowBattery)) { prefetchCount++; // Start the PlaceDetailsUpdateService to prefetch the details for this place. }
      
      





同様の手法がオフラインマークの実装に使用されます。



バッテリー寿命の最適化:スマートサービスおよびデバイスステータスを使用した受信機の切り替え



デバイスがオフラインのときに更新サービスを開始しても意味がありません。 PlaceUpdateServiceサービスは、更新を取得する前に接続を確認します。



 NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
      
      





接続がない場合、ActiveLocationChangedReceiverとPassiveLocationChangedReceiverはオフになり、 ConnectivityChangedReceiverはオンになります。



 ComponentName connectivityReceiver = new ComponentName(this, ConnectivityChangedReceiver.class); ComponentName locationReceiver = new ComponentName(this, LocationChangedReceiver.class); ComponentName passiveLocationReceiver = new ComponentName(this, PassiveLocationChangedReceiver.class); pm.setComponentEnabledSetting(connectivityReceiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); pm.setComponentEnabledSetting(locationReceiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); pm.setComponentEnabledSetting(passiveLocationReceiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
      
      





ConnectivityChangedReceiverは、すべてのネットワーク接続の変更をリッスンします。 新しい接続が作成されると、それ自体がオフになり、位置リスナーがオンになります。



バッテリーの状態を監視して機能を減らし、電力を節約します



電話が最後の15%にあるとき、ほとんどのアプリケーションは料金を節約するために機能しません。 マニフェストにレシーバーを登録して、デバイスが低バッテリー状態に入ったり出たりしたときに警告を出すことができます。



 <receiver android:name=".receivers.PowerStateChangedReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_BATTERY_LOW"/> <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/> </intent-filter> </receiver>
      
      





PowerStateChangedReceiverからのこのスニペットは、デバイスが低バッテリー状態になるたびにPassiveLocationChangedReceiverをオフにし、 充電が正常になるとオンにします。



 boolean batteryLow = intent.getAction().equals(Intent.ACTION_BATTERY_LOW); pm.setComponentEnabledSetting(passiveLocationReceiver, batteryLow ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);
      
      





すべてのプリフェッチサンプルを無効にするか、低バッテリ状態でリフレッシュレートを下げることにより、このロジックを拡張できます。



次は?



投稿はすでに素晴らしいことが判明しているので、私はここにとどまります。 私のブログThe Radioactive Yakを読んでください



All Articles