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

标签 java android location google-play-services locationmanager

我正在构建一个应用程序,需要将用户的位置发送到远程服务器(在本例中为 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/

相关文章:

android - 适用于 Android 的附近连接 API - 不适用于某些设备

java - Android计算两个位置之间的距离

java - CreateView 方法上的自定义对话框在 firebase firestore onCompletelistener 获取数据到列表之前返回

java - 使用 Jersey ResourceConfig 为 Swagger 设置自定义应用程序类

java - 如何以循环方式突出显示 TextArea 中的单词

android - Firebase FCM 变得非常不稳定。寻找解决方案/替代方案

java - HTML 文件无法在可执行 jar 中打开

android - 用于 Facebook 登录的 Google 智能锁

android - 在 Android 中获取当前国家/地区 - 旅行

android - 如何在Kotlin中设置从服务到 Activity 的回调?