android中使用GoogleMap的地理位置服务

来源:互联网 发布:c语言第一节课怎么讲 编辑:程序博客网 时间:2024/05/03 20:39
写在前面:android中使用地理位置功能,可以借助Google给我们提供的框架,要是有地理位置功能,你需要引用Google Play Services,请在sdk manager中下载。
如果你还要使用地图功能,请到google官网申请api-key

如果要看官方例子可以到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>

2.实时刷新最新位置,获取最新位置信息;

连接成功后,在回调方法中执行位置监听;首先配置位置请求参数,创建位置请求对象,后调用监听。

<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>

3.获取到位置的经纬度后,可以将其转为地址信息。

这里使用开启服务来解析经纬度。

<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>

使用IntentService

<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>

4.使用地理围栏,在进入和退出围栏时有提醒。

<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>

5.继承GoogleMap到android项目中;

详细参考可以看,参考

下面说说最关键的地方,其他地方的操作例如如何操地图,添加覆盖物等等则需要看说明文档了。

集成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文件时填写的别名。








2 0
原创粉丝点击