如果要看官方例子可以到https://github.com/googlesamples/android-play-location.git下载
使用Google地理服务都需要如下,
引用Google Play Services
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
声明权限
定位功能涉及到权限,下面2选1;
ACCESS_COARSE_LOCATION 粗略位置
或者ACCESS_FINE_LOCATION精确位置
1.基本使用,获取上一次的位置。
<span style="font-size:18px;">/** * 构建位置服务客户端对象 */ protected synchronized void buildGoogleApiClient() { mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); } /** *在onStart中使用客户端连接GooglePlay Service */ @Override protected void onStart() { super.onStart(); mGoogleApiClient.connect(); } /** * 离开界面时,断开连接 */ @Override protected void onStop() { super.onStop(); if (mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); } } /** * 连接GooglePlay Service成功后,可以在回调方法中获取上一次的位置 */ @Override public void onConnected(Bundle connectionHint) { // Provides a simple way of getting a device's location and is well suited for // applications that do not require a fine-grained location and that do not need location // updates. Gets the best and most recent location currently available, which may be null // in rare cases when a location is not available. 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, R.string.no_location_detected, Toast.LENGTH_LONG).show(); } } /** * 连接失败 * @param result */ @Override public void onConnectionFailed(ConnectionResult result) { // Refer to the javadoc for ConnectionResult to see what error codes might be returned in // onConnectionFailed. Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode()); } /** * 连接被暂停了,重新连接 * @param cause */ @Override public void onConnectionSuspended(int cause) { // The connection to Google Play services was lost for some reason. We call connect() to // attempt to re-establish the connection. Log.i(TAG, "Connection suspended"); mGoogleApiClient.connect(); }</span>
连接成功后,在回调方法中执行位置监听;首先配置位置请求参数,创建位置请求对象,后调用监听。
<span style="font-size:18px;"> /** * The desired interval for location updates. Inexact. Updates may be more or less frequent. */ public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000; /** * The fastest rate for active location updates. Exact. Updates will never be more frequent * than this value. */ public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2; /** * 创建位置请求对象mLocationRequest,封装监听参数 */ protected void createLocationRequest() { mLocationRequest = new LocationRequest(); // Sets the desired interval for active location updates. This interval is // inexact. You may not receive updates at all if no location sources are available, or // you may receive them slower than requested. You may also receive updates faster than // requested if other applications are requesting location at a faster interval. mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); // Sets the fastest rate for active location updates. This interval is exact, and your // application will never receive updates faster than this value. mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); }</span>
<span style="font-size:18px;"> /** * 开始监听位置变化 */ protected void startLocationUpdates() { // The final argument to {@code requestLocationUpdates()} is a LocationListener // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). LocationServices.FusedLocationApi.requestLocationUpdates( mGoogleApiClient, mLocationRequest, this); }</span>
<span style="font-size:18px;"> /** * 回调方法中获取到改变的最新位置 */ @Override public void onLocationChanged(Location location) { mCurrentLocation = location; mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); updateUI(); Toast.makeText(this, getResources().getString(R.string.location_updated_message), Toast.LENGTH_SHORT).show(); }</span>
这里使用开启服务来解析经纬度。
<span style="font-size:18px;"> if (mLastLocation != null) { // Determine whether a Geocoder is available. if (!Geocoder.isPresent()) { Toast.makeText(this, R.string.no_geocoder_available, Toast.LENGTH_LONG).show(); return; } // It is possible that the user presses the button to get the address before the // GoogleApiClient object successfully connects. In such a case, mAddressRequested // is set to true, but no attempt is made to fetch the address (see // fetchAddressButtonHandler()) . Instead, we start the intent service here if the // user has requested an address, since we now have a connection to GoogleApiClient. if (mAddressRequested) { startIntentService(); //开启服务解析经纬度 } }</span>
<span style="font-size:18px;"> /** * Creates an intent, adds location data to it as an extra, and starts the intent service for * fetching an address. */ protected void startIntentService() { // Create an intent for passing to the intent service responsible for fetching the address. Intent intent = new Intent(this, FetchAddressIntentService.class); // Pass the result receiver as an extra to the service. intent.putExtra(Constants.RECEIVER, mResultReceiver); // Pass the location data as an extra to the service. intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation); // Start the service. If the service isn't already running, it is instantiated and started // (creating a process for it if needed); if it is running then it remains running. The // service kills itself automatically once all intents are processed. startService(intent); }</span>
<span style="font-size:18px;">public class FetchAddressIntentService extends IntentService { private static final String TAG = "FetchAddressIS"; /** * The receiver where results are forwarded from this service. */ protected ResultReceiver mReceiver; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public FetchAddressIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Tries to get the location address using a Geocoder. If successful, sends an address to a * result receiver. If unsuccessful, sends an error message instead. * Note: We define a {@link android.os.ResultReceiver} in * MainActivity to process content * sent from this service. * * This service calls this method from the default worker thread with the intent that started * the service. When this method returns, the service automatically stops. */ @Override protected void onHandleIntent(Intent intent) { String errorMessage = ""; mReceiver = intent.getParcelableExtra(Constants.RECEIVER); // Check if receiver was properly registered. if (mReceiver == null) { Log.wtf(TAG, "No receiver received. There is nowhere to send the results."); return; } // Get the location passed to this service through an extra. Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA); // Make sure that the location data was really sent over through an extra. If it wasn't, // send an error error message and return. if (location == null) { errorMessage = getString(R.string.no_location_data_provided); Log.wtf(TAG, errorMessage); deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); return; } // Errors could still arise from using the Geocoder (for example, if there is no // connectivity, or if the Geocoder is given illegal location data). Or, the Geocoder may // simply not have an address for a location. In all these cases, we communicate with the // receiver using a resultCode indicating failure. If an address is found, we use a // resultCode indicating success. // The Geocoder used in this sample. The Geocoder's responses are localized for the given // Locale, which represents a specific geographical or linguistic region. Locales are used // to alter the presentation of information such as numbers or dates to suit the conventions // in the region they describe. Geocoder geocoder = new Geocoder(this, Locale.getDefault()); // Address found using the Geocoder. List<Address> addresses = null; try { // Using getFromLocation() returns an array of Addresses for the area immediately // surrounding the given latitude and longitude. The results are a best guess and are // not guaranteed to be accurate. addresses = geocoder.getFromLocation( location.getLatitude(), location.getLongitude(), // In this sample, we get just a single address. 1); } catch (IOException ioException) { // Catch network or other I/O problems. errorMessage = getString(R.string.service_not_available); Log.e(TAG, errorMessage, ioException); } catch (IllegalArgumentException illegalArgumentException) { // Catch invalid latitude or longitude values. errorMessage = getString(R.string.invalid_lat_long_used); Log.e(TAG, errorMessage + ". " + "Latitude = " + location.getLatitude() + ", Longitude = " + location.getLongitude(), illegalArgumentException); } // Handle case where no address was found. if (addresses == null || addresses.size() == 0) { if (errorMessage.isEmpty()) { errorMessage = getString(R.string.no_address_found); Log.e(TAG, errorMessage); } deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); } else { Address address = addresses.get(0); ArrayList<String> addressFragments = new ArrayList<String>(); // Fetch the address lines using {@code getAddressLine}, // join them, and send them to the thread. The {@link android.location.address} // class provides other options for fetching address details that you may prefer // to use. Here are some examples: // getLocality() ("Mountain View", for example) // getAdminArea() ("CA", for example) // getPostalCode() ("94043", for example) // getCountryCode() ("US", for example) // getCountryName() ("United States", for example) for(int i = 0; i < address.getMaxAddressLineIndex(); i++) { addressFragments.add(address.getAddressLine(i)); } Log.i(TAG, getString(R.string.address_found)); deliverResultToReceiver(Constants.SUCCESS_RESULT, TextUtils.join(System.getProperty("line.separator"), addressFragments)); } } /** * Sends a resultCode and message to the receiver. */ private void deliverResultToReceiver(int resultCode, String message) { Bundle bundle = new Bundle(); bundle.putString(Constants.RESULT_DATA_KEY, message); mReceiver.send(resultCode, bundle); } }</span>将服务声明下
<span style="font-size:18px;"> <service android:name=".FetchAddressIntentService" android:exported="false"/></span>
<span style="font-size:18px;">/** * Adds geofences, which sets alerts to be notified when the device enters or exits one of the * specified geofences. Handles the success or failure results returned by addGeofences(). */ public void addGeofencesButtonHandler(View view) { if (!mGoogleApiClient.isConnected()) { Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show(); return; } try { LocationServices.GeofencingApi.addGeofences( mGoogleApiClient, // The GeofenceRequest object. getGeofencingRequest(), // A pending intent that that is reused when calling removeGeofences(). This // pending intent is used to generate an intent when a matched geofence // transition is observed. getGeofencePendingIntent() ).setResultCallback(this); // Result processed in onResult(). } catch (SecurityException securityException) { // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission. logSecurityException(securityException); } }</span>
<span style="font-size:18px;"> /** * Builds and returns a GeofencingRequest. Specifies the list of geofences to be monitored. * Also specifies how the geofence notifications are initially triggered. */ private GeofencingRequest getGeofencingRequest() { GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); // The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a // GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device // is already inside that geofence. builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER); // Add the geofences to be monitored by geofencing service. builder.addGeofences(mGeofenceList); // Return a GeofencingRequest. return builder.build(); }</span>
<span style="font-size:18px;"> /** * Gets a PendingIntent to send with the request to add or remove Geofences. Location Services * issues the Intent inside this PendingIntent whenever a geofence transition occurs for the * current list of geofences. * * @return A PendingIntent for the IntentService that handles geofence transitions. */ 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); }</span>
<span style="font-size:18px;">/** * This sample hard codes geofence data. A real app might dynamically create geofences based on * the user's location. */ public void populateGeofenceList() { for (Map.Entry<String, LatLng> entry : Constants.BAY_AREA_LANDMARKS.entrySet()) { mGeofenceList.add(new Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId(entry.getKey()) // Set the circular region of this geofence. .setCircularRegion( entry.getValue().latitude, entry.getValue().longitude, Constants.GEOFENCE_RADIUS_IN_METERS ) // Set the expiration duration of the geofence. This geofence gets automatically // removed after this period of time. .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS) // Set the transition types of interest. Alerts are only generated for these // transition. We track entry and exit transitions in this sample. .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT) // Create the geofence. .build()); } }</span>
删除围栏。
<span style="font-size:18px;"> /** * Removes geofences, which stops further notifications when the device enters or exits * previously registered geofences. */ public void removeGeofencesButtonHandler(View view) { if (!mGoogleApiClient.isConnected()) { Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show(); return; } try { // Remove geofences. LocationServices.GeofencingApi.removeGeofences( mGoogleApiClient, // This is the same pending intent that was used in addGeofences(). getGeofencePendingIntent() ).setResultCallback(this); // Result processed in onResult(). } catch (SecurityException securityException) { // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission. logSecurityException(securityException); } }</span>
<span style="font-size:18px;"> /** * Runs when the result of calling addGeofences() and removeGeofences() becomes available. * Either method can complete successfully or with an error. * * Since this activity implements the {@link ResultCallback} interface, we are required to * define this method. * * @param status The Status returned through a PendingIntent when addGeofences() or * removeGeofences() get called. */ public void onResult(Status status) { if (status.isSuccess()) { // Update state and save in shared preferences. mGeofencesAdded = !mGeofencesAdded; SharedPreferences.Editor editor = mSharedPreferences.edit(); editor.putBoolean(Constants.GEOFENCES_ADDED_KEY, mGeofencesAdded); editor.apply(); // Update the UI. Adding geofences enables the Remove Geofences button, and removing // geofences enables the Add Geofences button. setButtonsEnabledState(); Toast.makeText( this, getString(mGeofencesAdded ? R.string.geofences_added : R.string.geofences_removed), Toast.LENGTH_SHORT ).show(); } else { // Get the status code for the error and log it using a user-friendly message. String errorMessage = GeofenceErrorMessages.getErrorString(this, status.getStatusCode()); Log.e(TAG, errorMessage); } }</span>
使用服务来监听这种进出围栏的状态,
<span style="font-size:18px;">/** * Listener for geofence transition changes. * * Receives geofence transition events from Location Services in the form of an Intent containing * the transition type and geofence id(s) that triggered the transition. Creates a notification * as the output. */ public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } @Override public void onCreate() { super.onCreate(); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override 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<Geofence> 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)); } } /** * Gets transition details and returns them as a formatted string. * * @param context The app context. * @param geofenceTransition The ID of the geofence transition. * @param triggeringGeofences The geofence(s) triggered. * @return The transition details formatted as String. */ private String getGeofenceTransitionDetails( Context context, int geofenceTransition, List<Geofence> triggeringGeofences) { String geofenceTransitionString = getTransitionString(geofenceTransition); // Get the Ids of each geofence that was triggered. ArrayList triggeringGeofencesIdsList = new ArrayList(); for (Geofence geofence : triggeringGeofences) { triggeringGeofencesIdsList.add(geofence.getRequestId()); } String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList); return geofenceTransitionString + ": " + triggeringGeofencesIdsString; } /** * Posts a notification in the notification bar when a transition is detected. * If the user clicks the notification, control goes to the MainActivity. */ private void sendNotification(String notificationDetails) { // Create an explicit content Intent that starts the main Activity. Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); // Construct a task stack. TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Add the main Activity to the task stack as the parent. stackBuilder.addParentStack(MainActivity.class); // Push the content Intent onto the stack. stackBuilder.addNextIntent(notificationIntent); // Get a PendingIntent containing the entire back stack. PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Get a notification builder that's compatible with platform versions >= 4 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // Define the notification settings. builder.setSmallIcon(R.drawable.ic_launcher) // In a real app, you may want to use a library like Volley // to decode the Bitmap. .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)) .setColor(Color.RED) .setContentTitle(notificationDetails) .setContentText(getString(R.string.geofence_transition_notification_text)) .setContentIntent(notificationPendingIntent); // Dismiss notification once the user touches it. builder.setAutoCancel(true); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } /** * Maps geofence transition types to their human-readable equivalents. * * @param transitionType A transition type constant defined in Geofence * @return A String indicating the type of transition */ private String getTransitionString(int transitionType) { switch (transitionType) { case Geofence.GEOFENCE_TRANSITION_ENTER: return getString(R.string.geofence_transition_entered); case Geofence.GEOFENCE_TRANSITION_EXIT: return getString(R.string.geofence_transition_exited); default: return getString(R.string.unknown_geofence_transition); } } }</span>
详细参考可以看,参考
下面说说最关键的地方,其他地方的操作例如如何操地图,添加覆盖物等等则需要看说明文档了。
集成GoogleMap关键就是要申请用于Google地图的apikey,分正式的和测试的key;
正式的key是你打包上架时要用的;测试key是你平时用于测试调试的时候用的。
这2个key的生成是使用正式的keystoreh或者测试的keystore文件,然后利用keytool 工具生成对应的SHA1值,然后到google官网填写包名和SHA1去申请对应的apikey;
测试SHA1的值,keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
正式SHA1的值,keytool -list -v -keystore your_keystore_name -alias your_alias_name
your_alias_name为你生成正式keystore文件时填写的别名。