【问题标题】:Counting time in background service在后台服务中计算时间
【发布时间】:2017-12-19 14:53:17
【问题描述】:

我已经制作了一个正在运行的应用程序,为此,我制作了一个服务类来更新用户位置(删除了不重要的代码):

public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener, ILocationConstants {

    private static final String TAG = LocationService.class.getSimpleName();
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;

    private LocationData data;

    @Override
    public void onCreate() {
        super.onCreate();

        data = new LocationData();

        startForeground();
    }

    private void startForeground() {
        //BUIDLING A NOTIFICATION

        startForeground(101, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        data = Parcels.unwrap(intent.getParcelableExtra(LOCATION_MESSAGE));

        buildGoogleApiClient();

        mGoogleApiClient.connect();

        if (mGoogleApiClient.isConnected()) {
            startLocationUpdates();
        }

        return START_STICKY;
    }

    protected synchronized void buildGoogleApiClient() {

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

        createLocationRequest();
    }

    protected void createLocationRequest() {
        mLocationRequest = new LocationRequest();

        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    protected void startLocationUpdates() {

        try {

            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);

        } catch (SecurityException ex) {


        } catch (Exception e) {

        }
    }

    private void sendUpdates() {

        data.millisecondTime = SystemClock.uptimeMillis() - data.startTime;
        data.updateTime = data.timeBuff + data.millisecondTime;
        data.seconds = (int) (data.updateTime / 1000);
        data.minutes = data.seconds / 60;
        data.hours = data.minutes / 60;
        data.minutes = data.minutes % 60;
        data.seconds = data.seconds % 60;
        data.milliSeconds = (int) (data.updateTime % 1000);

        Intent locationIntent = new Intent();
        locationIntent.setAction(LOACTION_ACTION);
        locationIntent.putExtra(LOCATION_MESSAGE, Parcels.wrap(data));

        LocalBroadcastManager.getInstance(this).sendBroadcast(locationIntent);
    }

    protected void stopLocationUpdates() {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
    }

    @Override
    public void onDestroy() {
        stopLocationUpdates();

        mGoogleApiClient.disconnect();

        super.onDestroy();
    }

    @Override
    public void onConnected(Bundle connectionHint) throws SecurityException {
        Log.i(TAG, "Connected to GoogleApiClient");

        if (data.mLastLocation == null) {
            data.mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            sendUpdates();
        }

        startLocationUpdates();

    }

    @Override
    public void onLocationChanged(Location location) {

        sendLocationChanged(location);

        Log.d(TAG, "onLocationChanged: " + location.getLatitude() + ", " + location.getLongitude());

        sendUpdates();

    }

    public void sendLocationChanged(Location location) {
        //SEND DATA TO THE SERVER
    }

    @Override
    public void onConnectionSuspended(int cause) {

        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {

        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

}

在每次位置更新后,我还会计算经过的时间(我必须通过添加来做到这一点,因为用户可能会暂停活动)。问题是当我在办公室运行应用程序时,时间是准确的(例如 35 分钟),但是当我骑自行车时,时间太短(实际上大约 30 分钟过去时显示 15 分钟)。对于时间透视,唯一的区别是它的调用频率更高(在办公室 gps 仅每 10-20 秒刷新一次,但在外面可能甚至每 2 秒刷新一次)。路线和距离都很好 - 服务不会被杀死或类似的东西。我也在更新活动时间,但在每次更新时用广播(由服务)时间替换它,但即使删除它也不能解决问题。为什么会这样?

【问题讨论】:

    标签: java android android-service


    【解决方案1】:

    正如SystemClock documentation中提到的,

    uptimeMillis() 自系统启动后以毫秒为单位计数。 当系统进入深度睡眠时此时钟停止(CPU关闭,显示 黑暗,设备等待外部输入)

    所以这可能是你得到不同时间的原因。另一方面,还有elapsedRealtime()elapsedRealtimeNanos() 方法,它们

    返回系统启动后的时间,包括深度睡眠。 这个时钟保证是单调的,甚至会继续滴答作响 当 CPU 处于省电模式时,推荐的依据是 通用间隔计时

    尝试使用其中之一。

    【讨论】:

    • 就是这样!使用 elapsedRealtime() 解决了这个问题。我从没想过 updateMillis() 会以这种方式工作。
    【解决方案2】:

    我建议使用 onLocationChanged() 中每次位置更新获得的时间并将其传递给您的 sendUpdates() 方法

    void onLocationChanged(Location location){
    
        long currentTime=location.getTime(); // unix time in milliseconds
    
        .......
    
        sendUpdates(currentTime);
    
    }
    

    然后在 sendUpdates(long currentTime) 中进行时间计算。如果我理解正确,您只对应用启动后经过的时间感兴趣。在这种情况下,我想您可以通过仅使用 data.startTime、data.seconds、data.minutes 和 data.hours 来简化您的 data 对象。您可以在应用启动后从第一次修复的位置获取开始时间(利用标志或类似的方式检测第一次修复)。

    然后在 sendUpdates(long currentTime) 中,我将计算经过时间的秒、分、小时,如下所示:

    int elapsedSeconds=int((currentTime-data.StartTime)/1000);
    data.hours=int(elapsedSeconds/3600);
    data.minutes=int(elapsedSeconds/60)-60*data.hours;
    data.seconds=elapsedSeconds-60*(data.minutes+60*data.hours);
    

    【讨论】:

    • 谢谢,很酷的解决方案,但@Kirill Simonov 的解决方案比这更容易。
    猜你喜欢
    • 1970-01-01
    • 2016-04-12
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多