IntentService与PendingIntent结合使用时无法调用

3
我正在尝试制作一个有关地理围栏的Android应用程序。我已经按照开发者页面示例代码上的教程进行了操作,但它并没有像预期的那样工作。可能是因为我将位置地址和地理围栏结合在一起,所以可能出现了一些关于Android应用程序生命周期的错误。
我的问题是,为什么GeofenceTransitionIntentService.java类中的IntentService没有调用onHandleIntent函数?MainActivity.java中的getGeofencePending函数是否存在任何错误?
以下是代码:
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>

1
你在清单文件中添加了你的服务吗? - mromer
我已经在清单中添加了该服务。 - alperc
我刚刚通过移除GeofenceTransitionIntentService.java构造函数的参数解决了这个问题。 - alperc
1个回答

0

我刚刚通过移除GeofenceTransitionIntentService.java构造函数的参数来解决了这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接