android – 获取位置并仅在条件为真时将数据发送到远程服务器

我正在构建一个需要将用户位置发送到远程服务器的应用程序(在本例中为Pusher)。 目标是近乎实时地更新他们在地图上的位置,但仅限于他们在工作时,否则应用程序将不需要跟踪他们的位置。

如果他们离开他们接受工作的活动(因此被放置在地图上)并且他们完全离开应用程序,我需要位置更新保持活动状态。 一旦他们到达目的地,我希望停止此背景跟踪。

我一直在关注Android的Service组件,但我不确定它是否是我需要的。 更新应无限期地在后台进行,但仅在用户被分配到作业时(更新在他们接受作业时开始,在到达目的地时结束)。

Bound服务会是最好的吗? 如果是这样,那么与此问题相关的一些代码将非常受欢迎,因为我能找到的很多内容都是通用的,并且可以返回随机整数。

这是我希望在后台执行的代码:

 package com.example.locationtester; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.TextView; import android.widget.Toast; import com.pusher.client.Pusher; import com.pusher.client.PusherOptions; import com.pusher.client.channel.PrivateChannel; import com.pusher.client.channel.PrivateChannelEventListener; import com.pusher.client.connection.ConnectionEventListener; import com.pusher.client.connection.ConnectionState; import com.pusher.client.connection.ConnectionStateChange; import com.pusher.client.util.HttpAuthorizer; import org.json.JSONException; import org.json.JSONObject; public class MainActivity extends AppCompatActivity { private TextView mLatLabel; private TextView mLongLabel; private TextView mAccuracy; private Double mLat; private Double mLong; private boolean isSubscribed = false; private Pusher mPusher; private PrivateChannel mChannel; private static final long MIN_TIME_BW_UPDATES = 1000 * 60; private static final String TAG = "GPSTest"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLatLabel = (TextView) findViewById(R.id.latLabel); mLongLabel = (TextView) findViewById(R.id.longLabel); mAccuracy = (TextView) findViewById(R.id.accuracyLabel); HttpAuthorizer authorizer = new HttpAuthorizer("http://example.com"); PusherOptions options = new PusherOptions().setAuthorizer(authorizer).setEncrypted(true); mPusher = new Pusher("PUSHER_API_KEY", options); mPusher.connect(new ConnectionEventListener() { @Override public void onConnectionStateChange(ConnectionStateChange change) { Log.d(TAG, "State changed to " + change.getCurrentState() + " from " + change.getPreviousState()); } @Override public void onError(String message, String code, Exception e) { Log.d(TAG, "There was a problem connecting! " + e.toString()); } }, ConnectionState.ALL); mChannel = mPusher.subscribePrivate("private-location", new PrivateChannelEventListener() { @Override public void onAuthenticationFailure(String message, Exception e) { Log.e(TAG, "Error " + message); } @Override public void onSubscriptionSucceeded(String channelName) { Log.d(TAG, "Subscribed to " + channelName); isSubscribed = true; } @Override public void onEvent(String channelName, String eventName, String data) { } }); LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { handleLocationUpdate(location); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Toast.makeText(getApplicationContext(), "GPS Status Changed " + provider, Toast.LENGTH_LONG).show(); } @Override public void onProviderEnabled(String provider) { Toast.makeText(getApplicationContext(), "GPS Provider Enabled " + provider, Toast.LENGTH_LONG).show(); } @Override public void onProviderDisabled(String provider) { } }; // Register the listener with the Location Manager to receive location updates locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, 0, locationListener); } private void handleLocationUpdate(Location location) { mLat = location.getLatitude(); mLong = location.getLongitude(); mLatLabel.setText("Long: " + location.getLongitude()); mLongLabel.setText("Lat: " + location.getLatitude()); mAccuracy.setText("Accuracy: " + location.getAccuracy() + " at " + location.getTime()); Log.d(TAG, mLat + ""); if (isSubscribed) { JSONObject json = new JSONObject(); try { json.put("lat", mLat); json.put("long", mLong); json.put("time", location.getTime()); json.put("accuracy", location.getAccuracy()); mChannel.trigger("client-location-changed", json.toString()); } catch (JSONException e) { Log.e(TAG, "Problem adding JSON"); } } } } 

UPDATE

这是我在切换到Google Play Service的Location API后想出的。 我已经测试过离开这个活动(和一般的应用程序),一切都保持平稳运行,certificate位置更新,直到我点击按钮让他们停止。

反馈将在此代码中受到赞赏:

 public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { private GoogleApiClient mGoogleApiClient; private LocationRequest mLocationRequest; public static final String TAG = MainActivity.class.getSimpleName(); public static final int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000; private boolean isSubscribed = false; private Pusher mPusher; private PrivateChannel mChannel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startTracking = (Button) findViewById(R.id.btnStartTracking); Button stopTracking = (Button) findViewById(R.id.btnStopTracking); final Button startActivity = (Button) findViewById(R.id.btnStartActivity); HttpAuthorizer authorizer = new HttpAuthorizer("http://example.com"); PusherOptions options = new PusherOptions().setAuthorizer(authorizer).setEncrypted(true); mPusher = new Pusher("API_KEY", options); mPusher.connect(new ConnectionEventListener() { @Override public void onConnectionStateChange(ConnectionStateChange change) { Log.d(TAG, "State changed to " + change.getCurrentState() + " from " + change.getPreviousState()); } @Override public void onError(String message, String code, Exception e) { Log.d(TAG, "There was a problem connecting! " + e.toString()); } }, ConnectionState.ALL); mChannel = mPusher.subscribePrivate("private-location", new PrivateChannelEventListener() { @Override public void onAuthenticationFailure(String message, Exception e) { Log.e(TAG, "Error " + message); } @Override public void onSubscriptionSucceeded(String channelName) { Log.d(TAG, "Subscribed to " + channelName); isSubscribed = true; } @Override public void onEvent(String channelName, String eventName, String data) { } }); // Build the Google Api Client and set the API for LocationServices mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); // Create the LocationRequest object mLocationRequest = LocationRequest.create() .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) .setInterval(20 * 1000) // 3 seconds, in MS .setFastestInterval(1000); // 1 second, in MS startTracking.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mGoogleApiClient.isConnected()) { mGoogleApiClient.connect(); } } }); stopTracking.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); } } }); startActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), Activity2.class); startActivity(intent); } }); } @Override protected void onResume() { super.onResume(); if (!mGoogleApiClient.isConnected()) { mGoogleApiClient.connect(); } } @Override public void onConnected(Bundle bundle) { Log.i(TAG, "Location services connected"); LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); } private void handleNewLocation(Location location) { Log.d(TAG, location.toString()); double currentLatitude = location.getLatitude(); double currentLongitude = location.getLongitude(); LatLng latLng = new LatLng(currentLatitude, currentLongitude); int speed = (int) (location.getSpeed() * 2.2369); if (isSubscribed) { JSONObject json = new JSONObject(); try { json.put("lat", currentLatitude); json.put("long", currentLongitude); json.put("time", location.getTime()); json.put("accuracy", location.getAccuracy()); json.put("speed", speed); json.put("latLng", latLng); mChannel.trigger("client-location-changed", json.toString()); } catch (JSONException e) { Log.e(TAG, "Problem adding JSON"); } } } @Override public void onConnectionSuspended(int i) { Log.i(TAG, "Location services suspended. Please reconnect"); } @Override public void onLocationChanged(Location location) { handleNewLocation(location); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { if (connectionResult.hasResolution()) { try { // Start an Activity that tries to resolve the error connectionResult.startResolutionForResult(this, CONNECTION_FAILURE_RESOLUTION_REQUEST); } catch (IntentSender.SendIntentException e) { e.printStackTrace(); } } else { Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode()); } } } 

一种选择是抛弃LocationManager API并转移到FusedLocationProviderAPI。

FusedLocationProviderAPI允许您在最有效的管理器中发出位置请求时请求间歇性位置更新。

一些可以帮助“指向”正确方向的代码看起来像这样:

 public class GPSPlotter implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { public static final String UPLOAD_ACTION = "upload"; public static final String BACKGROUND_ACTION = "background"; private static final String TAG = "GPSPlotter: "; private static final int DEFAULT_INTENT_INTERVAL = 60; private static final int ALARM_REGISTER_BUFFER = 60000; /** * Static fields used in both Background and Foreground Location Updates. */ private static GPSPlotter gpsPlotterInstance; private ServiceType mCurrentServiceType; private GoogleApiClient mGoogleApiClient; private MyAccount mAccount; private static Location mCurrentLocation; private static CoordinateStorageDatabaseHelper mDbHelper; private static AlarmManager mAlarmManager; private static String mUserID; private static Context mContext; private int mIntentInterval; private GPSPlotter(Context theContext) { initializeInstance(); initializeFields(theContext); buildApiClient(); connectClient(); } /** * Returns an instance of the GPS Plotter. */ public static GPSPlotter getInstance(Context theContext) { if (gpsPlotterInstance == null) return new GPSPlotter(theContext); else return gpsPlotterInstance; } /** * Private method to initialize the fields of the GPS Plotter class. * * @param theContext is the application context. */ private void initializeFields(Context theContext) { mGoogleApiClient = null; mCurrentLocation = null; mDbHelper = new CoordinateStorageDatabaseHelper(theContext); mUserID = LocalStorage.getUserID(theContext); mContext = theContext; mAccount = null; mIntentInterval = DEFAULT_INTENT_INTERVAL; mCurrentServiceType = ServiceType.BACKGROUND; } /** * Private method to initialize an instance of the GPS Plotter class. */ private void initializeInstance() { gpsPlotterInstance = this; } /***********************************GOOGLE API CLIENT METHODS*********************************/ /** * Private helper method to initialize the Google Api Client with the * LocationServices Api and Build it for use. */ private void initializeGoogleApiClient() { mGoogleApiClient = new GoogleApiClient.Builder(mContext) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); } /** * Private helper method to determine whether or not GooglePlayServices * are installed on the local system. * * @return services are installed. */ private boolean googlePlayServicesInstalled() { int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext); return result == ConnectionResult.SUCCESS; } /** * Private method to build the Api Client for use with the LocationServices API. */ private synchronized void buildApiClient() { Log.w(TAG, "Building Google Api Client..."); initializeGoogleApiClient(); } /** * Private method used to connect the ApiClient to the Api hosted by Google for * Accessing Locations. */ private void connectClient() { mGoogleApiClient.connect(); } /***********************************UPLOAD PROCESSES AND INTENTS********************************/ /** * Private method to create a pending intent for issuing alarm manager requests. * * @param theIntent is the original intent. * @return thePendingIntent */ private PendingIntent buildUploadPendingIntent(Intent theIntent) { return PendingIntent.getBroadcast(mContext, 0, theIntent, 0); } /** * Private method to create an intent for issuing alarm manager requests. * * @return theIntent */ private Intent buildUploadIntent() { Intent theIntent = new Intent(mContext, BackgroundLocationReceiver.class); theIntent.setAction(UPLOAD_ACTION); return theIntent; } /** * Private method to register an instance of an AlarmManager that will issue uploads to the * WebService intermittently. Default duration is one hour. Akarm manager waits one minute * from the current time before issuing the request to the background services for firing * points to the database. */ private void registerAlarmManager() { mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, (System.currentTimeMillis() + ALARM_REGISTER_BUFFER), AlarmManager.INTERVAL_HOUR, buildUploadPendingIntent(buildUploadIntent())); } /** * Private method used to cancel the alarm manager in the case that a background point service * or a foreground point service are disabled. */ private void unregisterAlarmManager() { if (mAlarmManager != null) mAlarmManager.cancel(buildUploadPendingIntent(buildUploadIntent())); } /*****************************************LOCATION SERVICE REQUESTS****************************/ /** * User passes in a requested interval polling time in seconds as an * integer. * * @param theAccount is a reference to the parent activity used for updating views. */ public void beginManagedLocationRequests(MyAccount theAccount) { if (mAccount == null) mAccount = theAccount; startBackgroundUpdates(); } /** * Public method to end the managed Location Requests. */ public void endManagedLocationRequests() { endBackgroundUpdates(); } /** * This method handles the switch in polling rates by stopping and then starting once more the * background udpates, which in turn sets the interval in another method in the call stack. * @param theInterval the desired interval polling rate */ public void changeRequestIntervals(int theInterval) { mIntentInterval = theInterval; if (LocalStorage.getRequestingBackgroundStatus(mContext)) { endBackgroundUpdates(); startBackgroundUpdates(); } } /** * Private helper method to build an Intent that will be couple with a pending intent uses * for issuing background Location requests. * * @return theIntent */ private Intent buildBackgroundRequestIntent() { Intent intent = new Intent(mContext, BackgroundLocationReceiver.class); intent.setAction(BACKGROUND_ACTION); intent.putExtra(User.USER_ID, mUserID); return intent; } /** * Private helper method used to generate a PendingIntent for use when the User requests background service * within the FusedLocationApi until the Interval is changed. * * @return pendingIntent */ private PendingIntent buildRequestPendingIntent(Intent theIntent) { Log.w(TAG, "building pending intent"); return PendingIntent.getBroadcast(mContext, 0, theIntent, 0); } /** * Private method to start the Location Updates using the FusedLocation API in the background. */ private void startBackgroundUpdates() { Log.w(TAG, "Starting background updates"); if (googlePlayServicesInstalled()) { LocalStorage.putBackgroundRequestStatus(true, mContext); LocalStorage.putLocationRequestStatus(true, mContext); registerAlarmManager(); LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, buildLocationRequest(), buildRequestPendingIntent(buildBackgroundRequestIntent())); } } /** * Private method to end background updates. */ private void endBackgroundUpdates() { Log.w(TAG, "Ending background updates"); LocalStorage.putBackgroundRequestStatus(false, mContext); LocalStorage.putLocationRequestStatus(false, mContext); unregisterAlarmManager(); LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, buildRequestPendingIntent(buildBackgroundRequestIntent())); } /** * Private helper method used to generate a LocationRequest which will be used to handle all location updates * within the FusedLocationApi until the Interval is changed. * * @return locationRequest */ private LocationRequest buildLocationRequest() { int dateConversion = 1000; LocationRequest locationRequest = LocationRequest.create(); locationRequest.setInterval(mIntentInterval * dateConversion); locationRequest.setFastestInterval((mIntentInterval / 2) * dateConversion); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); Log.w(TAG, "Building location request"); return locationRequest; } } 

然后您将拥有一个后台服务,您将在上面的GPS绘图仪中注册:

 public class BackgroundService extends IntentService { /** * Private static final String to represent a TAG for this class. */ private static final String TAG = BackgroundService.class.getName(); public BackgroundService() { super("BackgroundService"); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { Log.w(TAG, "Intent is not null..."); GPSPlotter plotter = GPSPlotter.getInstance(getApplicationContext()); int counter = 0; while (!plotter.hasApiClientConnectivity()) { if (counter == 0) { Log.w(TAG, "Plotter does not have api connectivity."); counter++; } } Log.w(TAG, "Plotter is connected-" + Boolean.toString(plotter.hasApiClientConnectivity())); plotter.beginManagedLocationRequests(null); } } 

}