android - 在后台服务中创建地理围栏

标签 android service android-context geofencing android-geofence

我正在制作一个 android 应用程序,它想要在我的服务器指定的 LatLong 坐标处创建一个地理围栏。

坐标会在后台从服务中定期更新。我正在读取这些坐标,并希望在没有用户干预的情况下在后台为其创建地理围栏。我尝试在服务中这样做,但它不起作用。我可以定期从服务器获取坐标,但无法为其创建地理围栏,出现 nullPointerException。

但是,我可以通过单击按钮(硬编码 LatLong 坐标)在 Activity 中创建地理围栏,但它在服务中不起作用。

我的服务:

public class GPSPollingService extends Service implements ResultCallback<Status>, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mLocationRequest;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private final String TAG = "GPSPollingService";

private PendingIntent mGeofencePendingIntent;
String url = "http://shielded.coolpage.biz/status_read.php";
private static final int REQUEST_CODE_LOCATION = 2;
private Boolean servicesAvailable = false;
private Activity mActivity;

ArrayList<Geofence> mCurrentGeofences = new ArrayList<Geofence>();;
RequestQueue requestQueue;
Location mLocation;

public GPSPollingService(){}

public GPSPollingService(Activity mActivity){
    super();
    this.mActivity = mActivity;

}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    mInProgress = false;
    requestQueue = Volley.newRequestQueue(this);
    // Instantiate the current List of geofences
    mCurrentGeofences = new ArrayList<Geofence>();

    mGeofencePendingIntent = null;
    setUpLocationClientIfNeeded();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    mGoogleApiClient.connect();
    Log.d(TAG,"onStartCommand: ");
    PowerManager mgr = (PowerManager)getSystemService(Context.POWER_SERVICE);
    /*
    WakeLock is reference counted so we don't want to create multiple WakeLocks. So do a check before initializing and acquiring.
    This will fix the "java.lang.Exception: WakeLock finalized while still held: MyWakeLock" error that you may find.
    */
    if (this.mWakeLock == null) { //**Added this
        this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
    }

    if (!this.mWakeLock.isHeld()) { //**Added this
        this.mWakeLock.acquire();
    }

    if(!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
        return START_STICKY;

    setUpLocationClientIfNeeded();
    if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress)
    {
        //appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Started", Constants.LOG_FILE);
        mInProgress = true;
        mGoogleApiClient.connect();
    }

    return START_STICKY;


}

private void setUpLocationClientIfNeeded()
{
    if(mGoogleApiClient == null)
        googleApiClientBuild();
}
protected synchronized void googleApiClientBuild(){
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
}


@Override
public void onConnected(@Nullable Bundle bundle) {

    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setInterval(120000); // Update location every 60 seconds

    Log.d(TAG,"in onConnected: ");

    if ( ContextCompat.checkSelfPermission( this, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) {

        ActivityCompat.requestPermissions( mActivity, new String[] {  android.Manifest.permission.ACCESS_COARSE_LOCATION  },
                REQUEST_CODE_LOCATION );
    }else {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        //Location mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        //txtOutput.setText(mLocation.toString());
        Log.d(TAG, "calling fusedLocationApi");
    }

    continueAddGeofences();
}

@Override
public void onConnectionSuspended(int i) {

    // Turn off the request flag
    mInProgress = false;
    // Destroy the current location client
    mGoogleApiClient = null;
    Log.d(TAG, "onConnectionSuspended ");
}

@Override
public void onLocationChanged(Location location) {
    mLocation = location;
    Log.d(TAG,"in onLocationChanged: ");
    Log.d(TAG, mLocation.toString());
    // save new location to db


    connectToDb();          // checking for status 1



}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    mInProgress = false;

    Log.d(TAG, "onConnectionFailed");
    /*
     * Google Play services can resolve some errors it detects.
     * If the error has a resolution, try sending an Intent to
     * start a Google Play services activity that can resolve
     * error.
     */
    if (connectionResult.hasResolution()) {
        Log.d(TAG, "google play services has solution");

        // If no resolution is available, display an error dialog
    } else {

        Log.d(TAG, "no solution for connection failure");
    }
}

@Override
public void onDestroy() {
    // Turn off the request flag
    this.mInProgress = false;

    if (this.servicesAvailable && this.mGoogleApiClient != null) {
        this.mGoogleApiClient.unregisterConnectionCallbacks(this);
        this.mGoogleApiClient.unregisterConnectionFailedListener(this);
        this.mGoogleApiClient.disconnect();
        // Destroy the current location client
        this.mGoogleApiClient = null;
    }
    // Display the connection status
    // Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ":
    // Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();

    if (this.mWakeLock != null) {
        this.mWakeLock.release();
        this.mWakeLock = null;
    }

    super.onDestroy();
}
public void connectToDb(){
    StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
        @Override
        public void onResponse(String s) {
            Toast.makeText(getApplicationContext() , ""+s,Toast.LENGTH_LONG).show();
            Log.d(TAG, ""+s);
            try {
                JSONObject jsonObject = new JSONObject(s);
                JSONArray jsonArray = jsonObject.getJSONArray("result");
                if(jsonArray.length()!=0){
                    Log.d(TAG,"array length not 0");
                    JSONObject personInDanger = jsonArray.getJSONObject(0);
                    setGeofence(Double.valueOf(personInDanger.getString("latitude")), Double.valueOf(personInDanger.getString("longitude")));
                }
        }catch (JSONException je) {
            // do something with it
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(getApplicationContext(), ""+volleyError, Toast.LENGTH_LONG).show();

        }
    });

    requestQueue.add(stringRequest);
}

/**
 * Verify that Google Play services is available before making a request.
 *
 * @return true if Google Play services is available, otherwise false
 */
private boolean servicesConnected() {

    // Check that Google Play services is available
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {

        // In debug mode, log the status
        Log.d(GeofenceUtils.APPTAG, getString(R.string.play_services_available));

        // Continue
        return true;

        // Google Play services was not available for some reason
    } else {

        // Display an error dialog
        Toast.makeText(getApplicationContext(), "Google play services not available", Toast.LENGTH_SHORT).show();
        //Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, (Activity) getApplicationContext(), 0);
        //if (dialog != null) {
        //    ErrorDialogFragment errorFragment = new ErrorDialogFragment();
        //    errorFragment.setDialog(dialog);
        //    errorFragment.show(getSupportFragmentManager(), GeofenceUtils.APPTAG);
        }
        return false;
    }

public void setGeofence(Double latitude , Double longitude){

    Log.d(TAG, "in setGeofence");
    if (!servicesConnected()) {

        return;
    }

    SimpleGeofence newGeofence = new SimpleGeofence(
            "1",
            // Get latitude, longitude, and radius from the db
            latitude,
            longitude,
            Float.valueOf("50.0"),
            // Set the expiration time
            Geofence.NEVER_EXPIRE,
            // Detect both entry and exit transitions
            Geofence.GEOFENCE_TRANSITION_ENTER
    );



    mCurrentGeofences.add(newGeofence.toGeofence());
    // Start the request. Fail if there's already a request in progress
    try {
        // Try to add geofences
        addGeofences(mCurrentGeofences);
    } catch (UnsupportedOperationException e) {
        // Notify user that previous request hasn't finished.
        Toast.makeText(this, R.string.add_geofences_already_requested_error,
                Toast.LENGTH_LONG).show();
    }

}

public void addGeofences(ArrayList<Geofence> geofences) throws UnsupportedOperationException {
    if (!mInProgress) {

        // Toggle the flag and continue
        mInProgress = true;

        // Request a connection to Location Services
        requestConnection();

        // If a request is in progress
    } else {

        // Throw an exception and stop the request
        throw new UnsupportedOperationException();
    }
}

private void requestConnection() {
    getLocationClient().connect();
}

private GoogleApiClient getLocationClient() {
    if (mGoogleApiClient == null) {

        mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }
    return mGoogleApiClient;

}

private void continueAddGeofences() {

    // Get a PendingIntent that Location Services issues when a geofence transition occurs
    mGeofencePendingIntent = createRequestPendingIntent();
    if (!mGoogleApiClient.isConnected()) {
        Toast.makeText(mActivity, "not connected", Toast.LENGTH_SHORT).show();
        return;
    }

    try {
        LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                // The GeofenceRequest object.
                mCurrentGeofences,
                // 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.
                mGeofencePendingIntent
        ).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(GeofenceUtils.APPTAG , "Invalid location permission. " +
            "You need to use ACCESS_FINE_LOCATION with geofences", securityException);
}

public void onResult(Status status) {
    Intent broadcastIntent = new Intent();

    // Temp storage for messages
    String msg;


    if (status.isSuccess()) {

        Toast.makeText(
                mActivity, "Geofences added",
                Toast.LENGTH_SHORT
        ).show();

        msg = mActivity.getString(R.string.add_geofences_result_success, status);

        // In debug mode, log the result
        Log.d(GeofenceUtils.APPTAG, msg);

        // Create an Intent to broadcast to the app
        broadcastIntent.setAction(GeofenceUtils.ACTION_GEOFENCES_ADDED)
                .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
                .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, msg);
        // If adding the geofences failed
    } else {

        /*
         * Create a message containing the error code and the list
         * of geofence IDs you tried to add
         */
        msg = mActivity.getString(
                R.string.add_geofences_result_failure,
                status.getStatusCode(), status.getStatusMessage());

        // Log an error
        Log.e(GeofenceUtils.APPTAG, msg);

    }

}

private PendingIntent createRequestPendingIntent() {

    // If the PendingIntent already exists
    if (null != mGeofencePendingIntent) {

        // Return the existing intent
        return mGeofencePendingIntent;

        // If no PendingIntent exists
    } else {
        Intent intent = new Intent("com.example.android.geofence.ACTION_RECEIVE_GEOFENCE");
        return PendingIntent.getBroadcast(
                mActivity,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
    }
}

我的日志:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
                                                                                 at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:506)
                                                                                 at android.app.PendingIntent.getBroadcast(PendingIntent.java:495)
                                                                                 at com.example.android.safetyalert.GPSPollingService.createRequestPendingIntent(GPSPollingService.java:449)
                                                                                 at com.example.android.safetyalert.GPSPollingService.continueAddGeofences(GPSPollingService.java:369)
                                                                                 at com.example.android.safetyalert.GPSPollingService.onConnected(GPSPollingService.java:163)
                                                                                 at com.google.android.gms.common.internal.zzl.zzm(Unknown Source)
                                                                                 at com.google.android.gms.internal.zzof.zzk(Unknown Source)
                                                                                 at com.google.android.gms.internal.zzod.zzsb(Unknown Source)
                                                                                 at com.google.android.gms.internal.zzod.onConnected(Unknown Source)
                                                                                 at com.google.android.gms.internal.zzoh.onConnected(Unknown Source)
                                                                                 at com.google.android.gms.internal.zznw.onConnected(Unknown Source)
                                                                                 at com.google.android.gms.common.internal.zzk$1.onConnected(Unknown Source)
                                                                                 at com.google.android.gms.common.internal.zzd$zzj.zztp(Unknown Source)
                                                                                 at com.google.android.gms.common.internal.zzd$zza.zzc(Unknown Source)
                                                                                 at com.google.android.gms.common.internal.zzd$zza.zzw(Unknown Source)
                                                                                 at com.google.android.gms.common.internal.zzd$zze.zztr(Unknown Source)
                                                                                 at com.google.android.gms.common.internal.zzd$zzd.handleMessage(Unknown Source)
                                                                                 at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                 at android.os.Looper.loop(Looper.java:135)
                                                                                 at android.app.ActivityThread.main(ActivityThread.java:5312)
                                                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                                                 at java.lang.reflect.Method.invoke(Method.java:372)
                                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
                                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)

我已经尝试了所有我能想到的。 我正在轮询 GPS,以便为操作系统提供准确的位置,从而提高地理围栏的准确性。我知道它会耗尽我的电池,但现在这不是问题。

最佳答案

Service 已经有 Context。你不应该使用 Activity 中的 Context 因为如果你的 Activity 被杀死 mActivity 将是空的导致所有排序的问题。将 createRequestPendingIntent 中的 else block 替换为这个

} else {
    Intent intent = new Intent("com.example.android.geofence.ACTION_RECEIVE_GEOFENCE");
    return PendingIntent.getBroadcast(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
}

关于android - 在后台服务中创建地理围栏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38263820/

相关文章:

java - 如何从 Firebase 实时数据库 Java 中的子节点检索数组数据? (Firebase 回收器适配器)

android - 处理我的应用程序中的链接

linux - 如何在RHEL Azure VM上的/mnt/resource中创建目录

android - IntentService 连接到服务器监听

android - 如何从任何地方获取包名?

android - 创建自己的 LiveWallpaperPreview?

Android MediaStore 不会设置文件名

linux - Ubuntu - 如何自动重新启动崩溃或卡住的进程?

java - setWebViewClient 错误 - someView 中的 someView(context) 无法应用于 ()

android - 什么时候使用和不使用 android Application 类?