我正在构建一个应用程序,需要将用户的位置发送到远程服务器(在本例中为 Pusher)。目标是近乎实时地更新他们在 map 上的位置,但仅当他们在工作时更新,否则应用程序将不需要跟踪他们的位置。
如果他们离开接受工作的 Activity (因此被放置在 map 上)并且完全离开应用程序,我需要位置更新保持 Activity 状态。一旦他们到达目的地,我希望停止这种后台跟踪。
我一直在研究 Android 的 Service
组件,但我不确定它是否是我所需要的。更新应无限期地在后台发生,但仅当用户被分配给作业时(更新在用户接受作业时开始,在到达目的地时结束)。
绑定(bind)服务是最好的选择吗?如果是这样,与此问题相关的一些代码将非常受到赞赏,因为我能找到的大部分代码都是通用的,并且可以执行诸如返回随机整数之类的操作。
这是我希望在后台执行的代码:
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");
}
}
}
}
更新
这是我在切换到 Google Play 服务的位置 API 后想到的。我已经测试过离开此 Activity (以及一般的应用程序),一切都保持顺利运行,证明位置更新,直到我单击按钮让它们停止。
对此代码的反馈将不胜感激:
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);
}
}
}
关于java - android - 仅当条件为真时获取位置并将数据发送到远程服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31840260/