从前台服务更新Singleton成员属性

时间:2020-05-06 20:02:17

标签: java android

我有一个位置跟踪应用程序。我有一个前台服务,当应用程序进入后台时,它将继续获取位置。那部分工作正常。如果输出位置,则可以看到不同的点和正确的时间戳。

在后台,我需要将该数据发布到API端点。我的GPSHeartbeat类是单例,它公开了一个让我更新Singletons位置属性的函数。

在前台时,一切正常。在后台运行时,位置已更新,但单身人士在 之前 中拥有最后一个位置。

我的APICommunicator正按其间隔在后台触发,只是位置不正确。

这里是广播接收器,负责收听前景服务的位置更改。

这在后台和前景均能正常工作。成功获取更新的位置。

private void onNewLocation(Location location)
    {
        Log.i(TAG, "onNewLocationRec'd: " + location);

        mLocation = location;

        // Notify anyone listening for broadcasts about the new location.
        Intent intent = new Intent(ACTION_BROADCAST);
        intent.putExtra(EXTRA_LOCATION, location);
        LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

        // Update notification content if running as a foreground service.
        if (serviceIsRunningInForeground(this)) {
            mNotificationManager.notify(NOTIFICATION_ID, getNotification());
        }
    }

BroadcastReceiver是称为HomeActivity的Activity的内部类。这将从服务获得正确的位置。如果输出日志,则与服务广播的日志相同。

public class HomeActivity extends AppCompatActivity
{

    private GPSHeartbeat   mGPSHeartbeat;

    private GPSReceiver myReceiver;
    private LocationUpdatesService mService = null;
    private       boolean           mBound             = false;

    private final ServiceConnection mServiceConnection = new ServiceConnection()
    {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
            LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
            mService = binder.getService();
            mBound   = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name)
        {
            mService = null;
            mBound   = false;
        }
    };

    private void GPSBeginRequestingUpdates()
    {
        //Wait 5 seconds to spin up
        (new Handler()).postDelayed(this::StartGPSUpdates, 5000);

    }

    private void StartGPSUpdates()
    {
        mService.requestLocationUpdates();
    }


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        myReceiver = new GPSReceiver();
        setContentView(R.layout.activity_home);
        mGPSHeartbeat = GPSHeartbeat.instance(getApplicationContext()).setInterval(6);
    }


    @Override
    protected void onStart()
    {
        super.onStart();
        bindService(new Intent(getApplicationContext(), LocationUpdatesService.class), mServiceConnection, Context.BIND_AUTO_CREATE);

    }

    @Override
    protected void onStop()
    {
        if (mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
        super.onStop();
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(myReceiver, new IntentFilter(LocationUpdatesService.ACTION_BROADCAST));
        GPSBeginRequestingUpdates();
    }

    @Override
    protected void onPause()
    {
        LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(myReceiver);
        super.onPause();
    }


    private class GPSReceiver extends BroadcastReceiver
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {

            Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
            if (location != null) {
                Log.i(TAG, "\nonReceived New Location: " + GPSUtils.getLocationText(location));
                GPSHeartbeat.instance(context.getApplicationContext()).SetLocation(location);
            }
        }

    }

}

单身汉。 SetLocation()确实会收到正确的位置。仅在我的POST请求期间,APICommunicator使用GPSHeartbeat的旧位置。即使它刚刚更新。

如何确保我更新到正确的位置?

  public class GPSHeartbeat extends Service {
    private static          String                 TAG       = "GPSHeartbeat";
    private static volatile GPSHeartbeat           _instance;
    private final           WeakReference<Context> mContextRef;
    private                 Boolean                isRunning = false;
    private                 int                    mInterval;

    private Location mLocation;

    private Handler         mHandler;
    private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
    private Future          mLongRunningTaskFuture;
    private Runnable        mStatusChecker   = new Runnable()
    {
        @Override
        public void run()
        {
            try {
                tick(); //this function can change value of mInterval.
            }
            finally {
                if (isRunning()) {
                    // 100% guarantee that this always happens, even if your update method throws an exception
                    mHandler.postDelayed(mStatusChecker, mInterval);
                }
            }
        }
    };

    private GPSHeartbeat(Context context)
    {
        mContextRef = new WeakReference<>(context.getApplicationContext());
    }

    public static GPSHeartbeat instance(Context context)
    {
        if (_instance == null) {
            _instance = new GPSHeartbeat(context);
        } else {
            if (!context.equals(_instance.mContextRef.get())) {
                _instance = null;
                _instance = new GPSHeartbeat(context);
            }
        }

        return _instance;
    }

    public void SetLocation(Location loc)
    {
        Log.i(TAG, "setLocation(): " + loc);
        this.mLocation = loc;
    }

    public GPSHeartbeat setInterval(int interval)
    {
        this.mInterval = interval * 1000;
        return this;
    }


    public void start()
    {
        if (isRunning()) return;
        mHandler = new Handler();
        mLongRunningTaskFuture = mExecutorService.submit(mStatusChecker);
        mStatusChecker.run();
        isRunning = true;
    }

    public void stop()
    {
        if (mHandler != null) {
            mHandler.removeCallbacks(mStatusChecker);
        }
        if (mLongRunningTaskFuture != null) {
            //kill the task:
            try {
                mLongRunningTaskFuture.cancel(true);
                mLongRunningTaskFuture = null;
                mHandler               = null;
            }
            catch (Exception e) {
                Log.e(TAG, "Unable to cancel task: " + e.getLocalizedMessage());
            }
        }
        isRunning = false;

    }

    public Location currentLocation()
    {
        return mLocation;
    }


    public boolean isRunning()
    {
        return isRunning;
    }

    private void tick()
    {
        // Fire off the APICommuncator.Post() method
    }

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

APICommuncator

public class APICommuncator
{
    private static          String                 TAG = "APICommuncator";
    private static volatile APICommuncator          _instance;
    private final           WeakReference<Context> mContextRef;

    private GPSHeartbeat _gpsHeartbeat;


    private APICommuncator(Context context)
    {
        mContextRef     = new WeakReference<>(context.getApplicationContext());
        _gpsHeartbeat   = GPSHeartbeat.instance(context.getApplicationContext());

    }

    public static APICommuncator i(Context context)
    {
        if (_instance == null) {
            _instance = new APICommuncator(context);
        } else {
            if (!context.equals(_instance.mContextRef.get())) {
                _instance = null;
                _instance = new APICommuncator(context);
            }
        }

        return _instance;
    }

    public void Post(){
        // Do the background thing and grab
        // getLocationNode() which gets the OLD location before it went to the background.
    }


    private JSONObject getLocationNode()
    {

        Location location = _gpsHeartbeat.currentLocation();
        if (location == null) {
            return null;
        }

        JSONObject node = null;
        try {
            node = new JSONObject();
            node.put("Latitude", String.valueOf(location.getLatitude()));
            node.put("Longitude", String.valueOf(location.getLongitude()));
            node.put("HAccuracy", String.valueOf(location.getAccuracy()));
            node.put("VAccuracy", String.valueOf(location.getAccuracy()));
            node.put("Altitude", String.valueOf(location.getAltitude()));
            node.put("Speed", String.valueOf(location.getSpeed() * 2.237));
            node.put("Heading", String.valueOf(location.getBearing()));
            node.put("Timestamp", String.valueOf((location.getTime() / 1000)));
        }
        catch (JSONException | NullPointerException e) {
            e.printStackTrace();
        }

        return node;
    }
}

在清单中:

   <service
        android:name=".gpsheartbeat.GPSHeartbeat"
        android:exported="true"
        android:permission="android.permission.BIND_JOB_SERVICE" />

    <service
        android:name=".gpsheartbeat.LocationUpdatesService"
        android:enabled="true"
        android:exported="true"
        android:foregroundServiceType="location" />

1 个答案:

答案 0 :(得分:0)

实际上,我看不到您正在使用前台服务。在应用程序变为后台后不久,不会终止前台服务。另外,与API的通信应在前台服务范围内,因为活动可能会被系统杀死。