我正在尝试制作一款关于Geofencing的Android应用。我已经按照developers page和the sample code上的教程进行了操作,但是它没有按预期工作。自从我将位置地址和地理围栏结合起来后,我可能会对Android应用程序的生命周期造成一些失败。
我的问题是具体说明为什么我没有在onHandleIntent
课程的IntentService
中调用GeofenceTransitionIntentService.java
。 getGeofencePending
中的MainActivity.java
函数是否有任何错误。
这是代码:
package com.example.geofence;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.Builder;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.internal.mr;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
public class MainActivity extends Activity implements OnClickListener,
ConnectionCallbacks, OnConnectionFailedListener, LocationListener, ResultCallback <Status>{
protected static final String TAG = "creating-and-monitoring-geofences";
GoogleApiClient mGoogleApiClient;
LocationRequest mLocationRequest;
Boolean mRequestingLocationUpdates = true;
Boolean mAddressRequested = true;
String mAddressOutput = null;
Location mCurrentLocation;
protected Location mLastLocation;
private AddressResultReceiver mResultReceiver;
String mLastUpdateTime;
TextView latitudeText, longitudeText, lastUpdateTimeText, locationAddress;
private SharedPreferences mSharedPreferences;
private Button mAddGeofencesButton, mRemoveGeofencesButton;
protected ArrayList<Geofence> mGeofenceList = null;
private PendingIntent mGeofencePendingIntent;
private boolean mGeofencesAdded;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(this, "OnCreate", Toast.LENGTH_SHORT).show();
setContentView(R.layout.activity_main);
mResultReceiver = new AddressResultReceiver(new Handler());
latitudeText = (TextView) findViewById(R.id.latitudeText);
longitudeText = (TextView) findViewById(R.id.longitudeText);
lastUpdateTimeText = (TextView) findViewById(R.id.lastUpdateText);
locationAddress = (TextView) findViewById(R.id.locationAddressText);
mAddGeofencesButton = (Button) findViewById(R.id.add_geofence);
mRemoveGeofencesButton = (Button) findViewById(R.id.remove_geofence);
mGeofenceList = new ArrayList<Geofence>();
mGeofencePendingIntent = null;
mSharedPreferences = getSharedPreferences(Constants.SHARED_PREFERENCES_NAME,
MODE_PRIVATE);
mGeofencesAdded = mSharedPreferences.getBoolean(Constants.GEOFENCES_ADDED_KEY, false);
mAddGeofencesButton.setOnClickListener(this);
mRemoveGeofencesButton.setOnClickListener(this);
setButtonsEnabledState();
buildGoogleApiClient();
}
protected void startIntentService() {
Intent intent = new Intent(this, FetchAddressIntentService.class);
intent.putExtra(Constants.RECEIVER, mResultReceiver);
intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
startService(intent);
}
/*
@Override
protected 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);
}
*/
@Override
public void onClick(View v) {
if(v==mAddGeofencesButton){
addGeofencesButtonHandler(v);
}
if(v==mRemoveGeofencesButton){
removeGeofencesButtonHandler(v);
}
}
@Override
protected void onPause() {
super.onPause();
stopLocationUpdates();
}
@Override
public void onResume() {
super.onResume();
Toast.makeText(this, "onResume", Toast.LENGTH_SHORT).show();
if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) {
startLocationUpdates();
}
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(ConnectionResult result) {
// TODO Auto-generated method stub
Toast.makeText(this, "onConnectionFailed", Toast.LENGTH_SHORT).show();
}
@SuppressLint("NewApi")
@Override
public void onConnected(Bundle connectionHint) {
Toast.makeText(this, "onConnected", Toast.LENGTH_SHORT).show();
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
String latitude = String.valueOf(mLastLocation.getLatitude());
String longitude = String.valueOf(mLastLocation.getLongitude());
latitudeText.setText("latitude: " + latitude);
longitudeText.setText("longitude: " + longitude);
}
if(mRequestingLocationUpdates){
createLocationRequest();
startLocationUpdates();
}
if (mLastLocation != null) {
// Determine whether a Geocoder is available.
if (!Geocoder.isPresent()) {
Toast.makeText(this, "No geocoder available",
Toast.LENGTH_SHORT).show();
return;
}
if (mAddressRequested) {
startIntentService();
}
}
}
@Override
public void onConnectionSuspended(int cause) {
// TODO Auto-generated method stub
Toast.makeText(this, "onConnectionSuspended", Toast.LENGTH_SHORT).show();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
protected void startLocationUpdates() {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this); //this refers to the interface LocationListener
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
@Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
updateUI();
}
public void updateUI() {
latitudeText.setText(String.valueOf(mCurrentLocation.getLatitude()));
longitudeText.setText(String.valueOf(mCurrentLocation.getLongitude()));
lastUpdateTimeText.setText(mLastUpdateTime);
locationAddress.setText(mAddressOutput);
}
public void showToast(String text){
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
class AddressResultReceiver extends ResultReceiver {
public AddressResultReceiver(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
showToast("onReceiveResult");
// Display the address string
// or an error message sent from the intent service.
mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
//displayAddressOutput();
System.out.println(mAddressOutput);
Log.e("RESULT!!!", mAddressOutput);
locationAddress.setText(mAddressOutput);
// Show a toast message if an address was found.
if (resultCode == Constants.SUCCESS_RESULT) {
;
}
}
}
/**
* 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);
}
}
/**
* 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);
}
}
private void logSecurityException(SecurityException securityException) {
Log.e(TAG, "Invalid location permission. " +
"You need to use ACCESS_FINE_LOCATION with geofences", securityException);
}
/**
* 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) {
Toast.makeText(this, "mGeofencePendingIntent != null", Toast.LENGTH_SHORT).show();
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().
Toast.makeText(this, "mGeofencePendingIntent == null", Toast.LENGTH_SHORT).show();
mGeofencePendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}
/**
* 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);
builder.addGeofence(new Geofence.Builder()
.setRequestId("CurrentLocation")
.setCircularRegion(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), 50)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
.build());
Toast.makeText(this, "Geofence request added", Toast.LENGTH_SHORT).show();
// Return a GeofencingRequest.
return builder.build();
}
@Override
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.commit();
// 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);
}
}
/**
* Ensures that only one button is enabled at any time. The Add Geofences button is enabled
* if the user hasn't yet added geofences. The Remove Geofences button is enabled if the
* user has added geofences.
*/
private void setButtonsEnabledState() {
if (mGeofencesAdded) {
mAddGeofencesButton.setEnabled(false);
mRemoveGeofencesButton.setEnabled(true);
} else {
mAddGeofencesButton.setEnabled(true);
mRemoveGeofencesButton.setEnabled(false);
}
}
}
和GeofenceTransitionIntentService类,应该处理触发事件
package com.example.geofence;
import java.util.ArrayList;
import java.util.List;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingEvent;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
public class GeofenceTransitionsIntentService extends IntentService{
protected static final String TAG = "geofence-transitions-service";
public GeofenceTransitionsIntentService(String name) {
super("name");
}
@Override
protected void onHandleIntent(Intent intent) {
Toast.makeText(this, "OnHandleIntent", Toast.LENGTH_SHORT).show();
Log.e(TAG, "OnHandleIntent");
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = GeoFenceErrorMessages.getErrorString(this,
geofencingEvent.getErrorCode());
Log.e(TAG, errorMessage);
return;
}
Log.e("DEBUG", "No geofencingEvent error");
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
Log.e("DEBUG", "getGeofenceTransition has been called");
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
Log.e("DEBUG", "Geofence Transition enter or 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) {
Log.e("DEBUG", "getGeofenceTransitionDetails");
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);
Log.e("DEBUG", "sendNotification");
// 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) {
Log.e("DEBUG", "getTransitionString");
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);
}
}
}
清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.geofence"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".FetchAddressIntentService"
android:exported="false"/>
<service android:name=".GeofenceTransitionsIntentService"/>
</application>
</manifest>
答案 0 :(得分:0)
我刚刚通过删除GeofenceTransitionIntentService.java
的构造函数的参数来解决了这个问题