Android Regular GPS Polling in Service, maximizing

2019-01-22 16:29发布

I am attempting to write a service, that will every X minutes attempt to get the GPS location of the device, and run and record in the background even when the app is not in focuse.

So, time to create a service.

I created a service, set up the locationListener, got the location Manager and requestLocationUpdates... all is good with the world.. rudimentary skeleton working.

Now, I don't want the GPS running constantly as this will kill the battery, what I would like to happen is the service, fire up the GPS, ask for an update, then shut down (at least its interest in the gps location) and then 5 or 10 minutes later do this again..

Simple enough, on the onLocationChanged() method of my listener, I add the line LocationManager (removeUpdates(locationListener)).. so when my service asks for an update, it gets only one, and shuts down.

I decide to add a little while loop, that effectively registers my location listener, and sleeps for X minutes. So logically, it should register that it wants information, then sleep.. an update comes in, I get that update in the onLocationChange and it unregisteres its intereste in the event, shutting down the GPS until the next execution of the loop.

Now, I have 2 questions 1) Does this logically seem okay? or is there a more elegant way? Remember I want this to record information whether or not the launching application is in focus, and honestly even if the launching app was killed would like the service to continue running potentially but that's a design decision I haven't fully made yet.

The 2nd question is, I will need to put this LOOP inside a thread since having it in the oncreate, causes the service to eventually be killed because its taking too long to come back from oncreate, so what would be the best way to go about doing this? AsyncTask is an option, but this is a task that is never in theory going to finish.. Handler also seems sort of silly, as there are no real callbacks, its just registering for the GPS updates, and the uninterest code is in the LocationListener onLocationChange().

There is no real communication that would come out of this thread once instantiated, though it would need to be signaled to die/end when the service is going to be shut down by some user interaction to do so...

So should I just just use a base thread? Go with an AsyncTask even though its never really coming back? or is using the Handler the better option? Or is my model just complete bad form out of the gate?

1条回答
手持菜刀,她持情操
2楼-- · 2019-01-22 17:00

I think having a service to manage the GPS is the sensible way to go, especially if you have more than one activity which might want location information. I adopted such an approach in my app.

Firstly I used an Ibinder in the service and bound to it from the activities with

bindService (new Intent(....),   mServconn, Context.BIND_AUTO_CREATE);

in onStart()

and unbindService(mServconn); in onStop() of each activity

I had the service use sendBroadcast() to BroadcastReceivers registered in the activities. The location data is passed via extras in the broadcast intent.

I used a state machine in the service with 3 states, IDLE, SEEKING and GOT_A_FIX_NOW_SLEEPING. The sleep time is passed in through a 'changeGPSParameters' method exposed in a public method of the service. Another parameter is the required accuracy, i.e. don't broadcast a message until you've had a fix better than the the required accuracy, then you can sleep. Sleep means turn off the GPS until the time has elapsed.

The timing is managed by a Runnable and a Handler posts messages to it with code like

mHandler.postDelayed(this, mSleepTime );

I find this works well. When no activities are bound to the service then onUnbind() will run in the service. In that method you just have to make sure that you stop the location listener and stop the timer with mHandler.removeCallbacks

UPDATE

Below is a simple example of a Runnable which you can start/stop by means of two buttons in your main.xml which should have a single textview to show the timer state:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;

public class TimerLoopActivity extends Activity {

    private Handler mHandler = new Handler();
    private int mSleepTime  = 2; //seconds
    private int mLoopCount = 0;
    private Runnable mUpdateTimeTask = new Runnable() {
        public void run() {
            // Code here for when timer completes
            mLoopCount++;
            setTextBoxMsg("Running - count = " + mLoopCount);
            mHandler.removeCallbacks(this);
            mHandler.postDelayed(this, mSleepTime * 1000); // keep looping
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        setTextBoxMsg("Timer Idle");
    }

    private void setTextBoxMsg(String string) {
        TextView tv = (TextView) findViewById(R.id.textView1);
        tv.setText(string);
    }

    public void myClickHandler(View target) {
        switch (target.getId()) {
            case R.id.startbutton:
                setTextBoxMsg("Starting timer");
                startTimer();
                break;
            case R.id.stopbutton:
                setTextBoxMsg("Stopping timer");
                mLoopCount = 0;
                stopTimer();
                break;
        }
    }

    private void stopTimer() { mHandler.removeCallbacks(mUpdateTimeTask); }
    private void startTimer() { mHandler.postDelayed(mUpdateTimeTask, mSleepTime * 1000);}
}

You can adapt this to put it in your service.

查看更多
登录 后发表回答