Location Updates
Demonstrates how to use the Fused Location Provider API to get updates about a device's location. The Fused Location Provider is part of the Google Play services Location APIs.
Introduction
This sample builds on the BasicLocationSample sample included in this repo, and allows the user to request periodic location updates. In response, the API updates the app periodically with the best available location, based on the currently-available location providers such as WiFi and GPS (Global Positioning System). The accuracy of the location is also determined by the location permissions you've requested (we use the ACCESS_FINE_LOCATION here) and the options you set in the location request.
This sample uses Google Play services (GoogleApiClient) and the FusedLocationApi.
To run this sample, location must be enabled.
Prerequisites
- Android API Level >v9
- Android Build Tools >v21
- Google Support Repository
Getting Started
This sample uses the Gradle build system. To build this project, use the "gradlew build" command or use "Import Project" in Android Studio.
Support
- Stack Overflow: http://stackoverflow.com/questions/tagged/google-play-services
If you've found an error in this sample, please file an issue: https://github.com/googlesamples/android-play-location/issues
Patches are encouraged, and may be submitted according to the instructions in CONTRIBUTING.md.
License
Copyright 2014 Google, Inc.
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Changing Location Settings
If your app needs to request location or receive permission updates, the device needs to enable the appropriate system settings, such as GPS or Wi-Fi scanning. Rather than directly enabling services such as the device's GPS, your app specifies the required level of accuracy/power consumption and desired update interval, and the device automatically makes the appropriate changes to system settings. These settings are defined by the
LocationRequest
data object.
This lesson shows you how to use the Settings API to check which settings are enabled, and present the Location Settings dialog for the user to update their settings with a single tap.
Connect to Location Services
In order to use the location services provided by Google Play Services and the fused location provider, connect your app using the Google API Client, then check the current location settings and prompt the user to enable the required settings if needed. For details on connecting with the Google API client, see Getting the Last Known Location.
Apps that use location services must request location permissions. For this lesson, coarse location detection is sufficient. Request this permission with the
uses-permission
element in your app manifest, as shown in the following example:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.gms.location.sample.locationupdates" >
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
</manifest>
If the device is running Android 6.0 or higher, and your app's target SDK is 23 or higher, the app has to list the permissions in the manifest and request those permissions at run time. For more information, see Requesting Permissions at Run Time.
Set Up a Location Request
To store parameters for requests to the fused location provider, create a
LocationRequest
. The parameters determine the level of accuracy for location requests. For details of all available location request options, see the LocationRequest
class reference. This lesson sets the update interval, fastest update interval, and priority, as described below:- Update interval
setInterval()
- This method sets the rate in milliseconds at which your app prefers to receive location updates. Note that the location updates may be faster than this rate if another app is receiving updates at a faster rate, or slower than this rate, or there may be no updates at all (if the device has no connectivity, for example).- Fastest update interval
setFastestInterval()
- This method sets the fastest rate in milliseconds at which your app can handle location updates. You need to set this rate because other apps also affect the rate at which updates are sent. The Google Play services location APIs send out updates at the fastest rate that any app has requested withsetInterval()
. If this rate is faster than your app can handle, you may encounter problems with UI flicker or data overflow. To prevent this, callsetFastestInterval()
to set an upper limit to the update rate.- Priority
setPriority()
- This method sets the priority of the request, which gives the Google Play services location services a strong hint about which location sources to use. The following values are supported:PRIORITY_BALANCED_POWER_ACCURACY
- Use this setting to request location precision to within a city block, which is an accuracy of approximately 100 meters. This is considered a coarse level of accuracy, and is likely to consume less power. With this setting, the location services are likely to use WiFi and cell tower positioning. Note, however, that the choice of location provider depends on many other factors, such as which sources are available.PRIORITY_HIGH_ACCURACY
- Use this setting to request the most precise location possible. With this setting, the location services are more likely to use GPS to determine the location.PRIORITY_LOW_POWER
- Use this setting to request city-level precision, which is an accuracy of approximately 10 kilometers. This is considered a coarse level of accuracy, and is likely to consume less power.PRIORITY_NO_POWER
- Use this setting if you need negligible impact on power consumption, but want to receive location updates when available. With this setting, your app does not trigger any location updates, but receives locations triggered by other apps.
Create the location request and set the parameters as shown in this code sample:
protected void createLocationRequest() { LocationRequest mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(10000); mLocationRequest.setFastestInterval(5000); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); }
The priority of
PRIORITY_HIGH_ACCURACY
, combined with the ACCESS_FINE_LOCATION
permission setting that you've defined in the app manifest, and a fast update interval of 5000 milliseconds (5 seconds), causes the fused location provider to return location updates that are accurate to within a few feet. This approach is appropriate for mapping apps that display the location in real time.
Performance hint: If your app accesses the network or does other long-running work after receiving a location update, adjust the fastest interval to a slower value. This adjustment prevents your app from receiving updates it can't use. Once the long-running work is done, set the fastest interval back to a fast value.
Get Current Location Settings
Once you have connected to Google Play services and the location services API, you can get the current location settings of a user's device. To do this, create a
LocationSettingsRequest.Builder
, and add one or more location requests. The following code snippet shows how to add the location request that was created in the previous step:LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(mLocationRequest);
Next check whether the current location settings are satisfied:
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(mGoogleClient, builder.build());
When the
PendingResult
returns, your app can check the location settings by looking at the status code from the LocationSettingsResult
object. To get even more details about the the current state of the relevant location settings, your app can call the LocationSettingsResult
object's getLocationSettingsStates()
method.Prompt the User to Change Location Settings
To determine whether the location settings are appropriate for the location request, check the status code from the
LocationSettingsResult
object. A status code of RESOLUTION_REQUIRED
indicates that the settings must be changed. To prompt the user for permission to modify the location settings, call startResolutionForResult(Activity, int)
. This method brings up a dialog asking for the user's permission to modify location settings. The following code snippet shows how to check the location settings, and how to call startResolutionForResult(Activity, int)
.result.setResultCallback(new ResultCallback<LocationSettingsResult>()) { @Override public void onResult(LocationSettingsResult result) { final Status status = result.getStatus(); final LocationSettingsStates = result.getLocationSettingsStates(); switch (status.getStatusCode()) { case LocationSettingsStatusCodes.SUCCESS: // All location settings are satisfied. The client can // initialize location requests here. ... break; case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: // Location settings are not satisfied, but this can be fixed // by showing the user a dialog. try { // Show the dialog by calling startResolutionForResult(), // and check the result in onActivityResult(). status.startResolutionForResult( OuterClass.this, REQUEST_CHECK_SETTINGS); } catch (SendIntentException e) { // Ignore the error. } break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: // Location settings are not satisfied. However, we have no way // to fix the settings so we won't show the dialog. ... break; } } });
The next lesson, Receiving Location Updates, shows you how to receive periodic location updates.
Receiving Location Updates
Request Location Updates
Before requesting location updates, your app must connect to location services and make a location request. The lesson on Changing Location Settingss hows you how to do this. Once a location request is in place you can start the regular updates by calling
requestLocationUpdates()
. Do this in the onConnected()
callback provided by Google API Client, which is called when the client is ready.
Depending on the form of the request, the fused location provider either invokes the
LocationListener.onLocationChanged()
callback method and passes it a Location
object, or issues a PendingIntent
that contains the location in its extended data. The accuracy and frequency of the updates are affected by the location permissions you've requested and the options you set in the location request object.
This lesson shows you how to get the update using the
LocationListener
callback approach. Call requestLocationUpdates()
, passing it your instance of the GoogleApiClient
, the LocationRequest
object, and a LocationListener
. Define a startLocationUpdates()
method, called from the onConnected()
callback, as shown in the following code sample:@Override public void onConnected(Bundle connectionHint) { ... if (mRequestingLocationUpdates) { startLocationUpdates(); } } protected void startLocationUpdates() { LocationServices.FusedLocationApi.requestLocationUpdates( mGoogleApiClient, mLocationRequest, this); }
Notice that the above code snippet refers to a boolean flag,
mRequestingLocationUpdates
, used to track whether the user has turned location updates on or off. For more about retaining the value of this flag across instances of the activity, see Save the State of the Activity.Define the Location Update Callback
The fused location provider invokes the
LocationListener.onLocationChanged()
callback method. The incoming argument is a Location
object containing the location's latitude and longitude. The following snippet shows how to implement the LocationListener
interface and define the method, then get the timestamp of the location update and display the latitude, longitude and timestamp on your app's user interface:public class MainActivity extends ActionBarActivity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
// 產生變化時, 會call onLocationChanged @Override public void onLocationChanged(Location location) { mCurrentLocation = location; mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); updateUI(); } private void updateUI() { mLatitudeTextView.setText(String.valueOf(mCurrentLocation.getLatitude())); mLongitudeTextView.setText(String.valueOf(mCurrentLocation.getLongitude())); mLastUpdateTimeTextView.setText(mLastUpdateTime); } }
Stop Location Updates
Stop Location Updates
Consider whether you want to stop the location updates when the activity is no longer in focus, such as when the user switches to another app or to a different activity in the same app. This can be handy to reduce power consumption, provided the app doesn't need to collect information even when it's running in the background. This section shows how you can stop the updates in the activity's
onPause()
method.
To stop location updates, call
removeLocationUpdates()
, passing it your instance of the GoogleApiClient
object and a LocationListener
, as shown in the following code sample:@Override protected void onPause() { super.onPause(); stopLocationUpdates(); } protected void stopLocationUpdates() { LocationServices.FusedLocationApi.removeLocationUpdates( mGoogleApiClient, this); }
Use a boolean,
mRequestingLocationUpdates
, to track whether location updates are currently turned on. In the activity's onResume()
method, check whether location updates are currently active, and activate them if not:@Override public void onResume() { super.onResume(); if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) { startLocationUpdates(); } }
Save the State of the Activity
A change to the device's configuration, such as a change in screen orientation or language, can cause the current activity to be destroyed. Your app must therefore store any information it needs to recreate the activity. One way to do this is via an instance state stored in a
Bundle
object.
The following code sample shows how to use the activity's
onSaveInstanceState()
callback to save the instance state:public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, mRequestingLocationUpdates); savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation); savedInstanceState.putString(LAST_UPDATED_TIME_STRING_KEY, mLastUpdateTime); super.onSaveInstanceState(savedInstanceState); }
Define an
updateValuesFromBundle()
method to restore the saved values from the previous instance of the activity, if they're available. Call the method from the activity's onCreate()
method, as shown in the following code sample:@Override public void onCreate(Bundle savedInstanceState) { ... updateValuesFromBundle(savedInstanceState); } private void updateValuesFromBundle(Bundle savedInstanceState) { if (savedInstanceState != null) { // Update the value of mRequestingLocationUpdates from the Bundle, and // make sure that the Start Updates and Stop Updates buttons are // correctly enabled or disabled. if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) { mRequestingLocationUpdates = savedInstanceState.getBoolean( REQUESTING_LOCATION_UPDATES_KEY); setButtonsEnabledState(); } // Update the value of mCurrentLocation from the Bundle and update the // UI to show the correct latitude and longitude. if (savedInstanceState.keySet().contains(LOCATION_KEY)) { // Since LOCATION_KEY was found in the Bundle, we can be sure that // mCurrentLocationis not null. mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY); } // Update the value of mLastUpdateTime from the Bundle and update the UI. if (savedInstanceState.keySet().contains(LAST_UPDATED_TIME_STRING_KEY)) { mLastUpdateTime = savedInstanceState.getString( LAST_UPDATED_TIME_STRING_KEY); } updateUI(); } }
For more about saving instance state, see the Android Activity class reference.
Note: For a more persistent storage, you can store the user's preferences in your app's
SharedPreferences
. Set the shared preference in your activity's onPause()
method, and retrieve the preference in onResume()
. For more information about saving preferences, read Saving Key-Value Sets.
沒有留言:
張貼留言