IntentService: как правильно поставить в очередь? - PullRequest
5 голосов
/ 29 июля 2011

В моем коде я использую IntentService для прослушивания обновлений местоположения (либо GPS, либо обновлений сети), и этот IntentService запускается при получении события, поэтому он запускается с startService() из любого действия ,

public class AddLocationService extends IntentService implements LocationListener {
    /*My code here*/
}

    @Override
protected void onHandleIntent(Intent intent) {
    if(getOldLoc() == null) 
    { 
        //Get a new location
        this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TIME_INTERVAL_GPS, 0, this);
        this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, TIME_INTERVAL_GPS, 0, this);
        Log.d(AddLocationService.TAG, "Network listener started");

        this.time_start_listening = System.currentTimeMillis();
        mTimerThread mTimerRunnable = new mTimerThread();
        this.timerThread = new Thread(mTimerRunnable);
        this.timerThread.start();
    }
    else    
        /*REUSE OLD LOCATION*/
}

Теперь моя проблема: когда два события запускают это IntentService, а второе запускает его, пока первое все еще запрашивает обновления, я хотел бы, чтобы второе ожидало, пока первое не будет полностью завершено (местоположение найдено ИЛИ таймер нить заканчивается). Однако всякий раз, когда IntentService выполняется во второй раз (первый экземпляр все еще работает), он печатает мне журнал и делает так, как он выполнялся параллельно.

Однако я думал, что главная цель IntentService заключается в том, чтобы это было что-то последовательное, поэтому второе намерение должно было бы ждать, пока первое не будет сделано ...

Я что-то не так понял?

Ответы [ 3 ]

3 голосов
/ 29 июля 2011

Похоже, что ваш метод onHandleIntent не блокирует поток, в котором он выполняется, поэтому он быстро вернется и позволит обработать второе намерение. Более того, маловероятно, что любые обратные вызовы из LocationManager в этот поток будут обрабатываться, так как фоновый поток может быть уничтожен, когда завершится onHandleIntent.

Если вы действительно хотите использовать IntentService для управления своей очередью намерений, вам нужно будет выполнить обработку вашего местоположения в своем собственном потоке и присоединить поток IntentService к потоку местоположения, пока он ожидает обратного вызова местоположения.

Вот немного кода, который демонстрирует идею:

public class TestService extends IntentService {
    private static final String TAG = "TestService";

    private Location mLocation = null;

    public TestService() {
       super(TAG);
    }

    @Override
    public void onHandleIntent(Intent intent) {
        Log.d(TAG, "onHandleIntent");

        if (mLocation == null) {
            Log.d(TAG, "launching location thread");
            LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

            LocationThread thread = new LocationThread(locationManager);
            thread.start();
            try {
                thread.join(10000);
            } catch (InterruptedException e) {
                Log.d(TAG, "timeout");
                return;
            }

            Log.d(TAG, "join finished, loc="+mLocation.toString());
        } else {
             Log.d(TAG, "using existing loc="+mLocation.toString());
        }
    }

    private class LocationThread extends Thread implements LocationListener  {
        private LocationManager locationManager = null;

        public LocationThread(LocationManager locationManager) {
            super("UploaderService-Uploader");
            this.locationManager = locationManager;
        }

        @Override
        public void run() {
            Log.d(TAG, "Thread.run");
            Looper.prepare();
           this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
            this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);

            Looper.loop();
        }

        @Override
        public void onLocationChanged(Location location) {
            // TODO Auto-generated method stub
            Log.d(TAG, "onLocationChanged("+location.toString()+")");
            mLocation = location;
            Looper.myLooper().quit();
         }

         @Override
         public void onProviderDisabled(String arg0) {
         }

         @Override
         public void onProviderEnabled(String arg0) {
         }

         @Override
         public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
         }

   }
}

Интерес представляет Looper, который запускает цикл сообщений в потоке (чтобы разрешить обработку обратных вызовов).

Учитывая усилия, требуемые для этого с IntentService, может быть, стоит вместо этого изучить наследование от Service и управление своей собственной очередью намерений.

2 голосов
/ 29 июля 2011

onHandleIntent уже в своем собственном потоке.Вы не должны (не должны) создавать там.Это все обрабатывается IntentService для вас.

1 голос
/ 29 июля 2011

Спасибо за миллион, это именно то, что мне нужно для обработки запросов о местоположении.Спасибо за объяснения и прояснение для меня, я не был хорошо знаком со всей концепцией петлителей, теперь я понимаю это лучше!

Если кому-то нужны такие вещи, не забудьтеостановите зацикливание потока, если поток местоположения не останавливается естественным образом (конец времени на join(millis)), добавив это в onHandleIntent():

                if(thread.isAlive())
                {
                    thread.onThreadStop();
                    try{
                        thread.interrupt();
                    }catch (Exception e) {
                        Log.d(TAG, "Exception on interrupt: " + e.getMessage());
                    }
                }   

после thread.join(yourTime), например, если вы неНе найдя обновления местоположения, вы все равно остановите поток через определенное время.И по методу onThreadStop():

                /*We remove location updates here and stop the looper*/
                public void onThreadStop()
                {
                    this.locationManager1.removeUpdates(this);
                    handleLocationChange(AddLocationService.this.currentBestLocation);
                    Looper.myLooper().quit();
                }

Однако я думал, что видел, как мои два намерения обрабатывались в первый раз, когда я запускал этот код, но теперь только первое обрабатывается, когда у меня есть несколько намерений, пока я все еще запрашиваюместоположение обновлений.Мой метод onHandleIntent(), кажется, выполняется правильно, останавливает поток после указанного времени и даже отображает самый последний журнал (последний оператор метода), но второе намерение не выполняется ... У вас есть идея, почему?

...