2

我正在尝试在后台服务中运行位置更新。该服务正在运行自己的工作线程,它已经在做很多其他事情,比如套接字通信。我希望它也能处理位置更新,但这似乎只适用于一项活动。据我所知,这是由于工作线程上缺少消息循环。我想我需要在Looper.prepare()某个地方使用,但也许我需要另一个线程来处理位置请求?我似乎无法让模拟器响应任何地理修复事件,所以我一定做错了什么。

下面是服务代码,去掉了所有不相关的部分。

public class MyService extends Service implements LocationListener {
    private Thread runner;
    private volatile boolean keepRunning;
    private LocationManager locationManager = null;


    @Override
    public void onCreate() {
        keepRunning = true;

        runner = new Thread(null, new Runnable() {
            public void run() { workerLoop(); }
        });
        runner.start();
        startGps();
    }

    @Override
    public void onDestroy() {
        keepRunning = false;
        runner.interrupt();
    }

    private void workerLoop() { 
        //Looper.myLooper(); How does this work??
        //Looper.prepare();

        // Main worker loop for the service
        while (keepRunning) {
            try {
                if (commandQueue.notEmpty()) {
                    executeJob();
                } else {
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
            }
        }   
        stopGps();
    }

    public void onLocationChanged(Location location) {
        // Doing something with the position...
    }       
    public void onProviderDisabled(String provider) {}
    public void onProviderEnabled(String provider) {}
    public void onStatusChanged(String provider, int status, Bundle extras) {}

    private void startGps() {
        if (locationManager == null)
            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        if (locationManager != null) {
            Criteria criteria = new Criteria();
            criteria.setAccuracy(Criteria.ACCURACY_FINE);
            criteria.setAltitudeRequired(false);
            criteria.setBearingRequired(true);
            criteria.setCostAllowed(false);
            criteria.setSpeedRequired(true);
            String provider = locationManager.getBestProvider(criteria, true);
            if (provider != null)
                locationManager.requestLocationUpdates(provider, 10, 5, (LocationListener) this);           
        }       
    }

    private void stopGps() {
        if (locationManager != null)
            locationManager.removeUpdates((LocationListener) this);
        locationManager = null;
    }

}
4

1 回答 1

0

The LocationListener doesn't care whether it is in the context of an activity or a service. You want to use the location uodates in your workerLoop(), right? The location updates (the call to the LocationListener) and the workerLoop() are both acting independently from each other. To get the location update to the workerLoop() you need to join the two threads. One method to do this is to use the blackboard pattern: The LocationListener writes the new location to a field of your class (this is easy since both are in the context of the same class):

private Location blackboard = null;
public void onLocationChanged(final Location location) {
    if( location != null )
        this.blackboard = location;
}

private void workerLoop() {
    ...
    if( this.blackboard != null ) {
        final Location locationUpdate = this.blackboard;
        this.blackboard = null;
        // .. do something with the location
    }
    ...
}

With the above code you may get race conditions when the LocationListener writes to the blackboard while the workerLoop() is reading or erasing the location. This can be solved by surrounding the access to the blackboard by a synchronized block like this:

synchronized(this) {
        this.blackboard = location
}

and likewise in the workerLoop(), and we must declare the blackboard volatile:

private volatile Location blackboard = null;

Alternatively you may use a Lock, confer to the docs for more details: http://docs.oracle.com/javase/tutorial/essential/concurrency/newlocks.html

于 2012-03-12T09:21:37.780 回答