Android: как правильно управлять последовательными потоками в бесконечном цикле - PullRequest
2 голосов
/ 06 октября 2011

Я создал IntentService с бесконечным циклом внутри onHandleIntent, затем добавил статические методы start, resume, pause, stop, чтобы напрямую вызывать его в моей деятельности.

Сценарий заключается в том, что внутри бесконечного цикла я вызываю методы обратного вызова, создающие новый поток для выполнения длинного процесса.

Проблема в том, что я беспокоюсь о непрерывном создании потоков из-за бесконечного цикла. Я уверен, что есть лучший способ справиться с этим. Я имею в виду ThreadPool или что-то, позволяющее использовать только один поток последовательно. Так что я экономлю время, память, накладные расходы и т.д ..

ДРУГОЙ ПОДХОД ОЧЕНЬ ДОБРО ПОЖАЛОВАТЬ. Спросите меня другую информацию по мере необходимости. Тогда я буду обновлять здесь.

Вот мои коды (посмотрите на SampleCallback):

IntentService

import android.app.IntentService;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class SampleCallbackIntentService extends IntentService {
    private final String LOG_LOGCAT_TAG = "SampleCallbackIntentService";
    private Handler _handler;

    public SampleCallbackIntentService(String name) {
        super(name);
    }

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

        // initialize variables for pause & resume thread
        _mPauseLock = new Object();
        _mPaused = false;
        _mFinished = false;

        // initialize handler to switch to UI/Main thread
         _handler = new Handler()
            {
                @Override
                public void handleMessage(final Message msg)
                {
                    _callback.doSomethingFromUIThread(msg);
                }
            };
    }

    private final SampleCallback _callback = new SampleCallback() {
        @Override
        public void doSomethingFromCurrentThread(final Object object) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //do long running process.
                                    // I will access object here.
                }
            }).start();

        }

        @Override
        public void doSomethingFromUIThread(final Message msg) {
            //may update UI here.
        }
    };

    private final int CALLBACK_MESSAGE = 1;
    @Override
    protected void onHandleIntent(Intent arg0) {
        Log.i(LOG_LOGCAT_TAG, "loop started");
        while (!_mFinished) {
            // do stuff here
            // create the object variable. Then pass to callback method
            _callback.doSomethingFromCurrentThread(object);

            // process and create the result to pass
            String someResult = "some result here";
            _handler.sendMessage(_handler.obtainMessage(CALLBACK_MESSAGE, someResult));

            synchronized (_mPauseLock) {
                while (_mPaused) {
                    try {
                        Log.i(LOG_LOGCAT_TAG, "loop paused");
                        _mPauseLock.wait();
                        Log.i(LOG_LOGCAT_TAG, "loop resumed");
                    } catch (InterruptedException e) {
                        Log.e(LOG_LOGCAT_TAG, "error occured on pause", e);
                    }
                }
            }
            try {
                //using sleep here might be not good design.
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Log.e(LOG_LOGCAT_TAG, "error occured on sleep", e);
            }
        }
        Log.i(LOG_LOGCAT_TAG, "loop ended");
    }

    private static Object _mPauseLock;
    private static boolean _mPaused;
    private static boolean _mFinished;

     public static void start(Context context) {
         Intent service = new Intent(context, SampleCallbackIntentService .class);
         if(context.startService(service)==null) {
             Log.e(LOG_LOGCAT_TAG, "Service cannot be started");
         } else {
             Log.i(LOG_LOGCAT_TAG, "start() called");
         }

     }

    /**
     * Call this on pause.
     */
    public static void pause() {
        Log.i(LOG_LOGCAT_TAG, "pause() called");
        synchronized (_mPauseLock) {
            _mPaused = true;
        }
    }

    /**
     * Call this on resume.
     */
    public static void resume() {
        Log.i(LOG_LOGCAT_TAG, "resume() called");
        synchronized (_mPauseLock) {
            _mPaused = false;
            _mPauseLock.notifyAll();
        }
    }

     public static void stop() {
         if(_mPauseLock == null) return;
         synchronized (_mPauseLock) {
             Log.i(LOG_LOGCAT_TAG, "stop() called");
             _mFinished = true;
         }
     }
}

SampleCallback

import android.os.Message;

public interface SampleCallback {

    public void doSomethingFromCurrentThread(final Object object);

    public void doSomethingFromUIThread(final Message msg);
}

UPDATES1 Я использую местоположение API помимо Google API. Я создам проект библиотеки Android и использую этот API, чтобы получить последнее местоположение (например, каждые 2 сек) в фоновом режиме.

На стороне приложения просто нужно вызвать статические методы, чтобы использовать его (например, start (context, callback), pause (), resume (), stop ()). Он имеет обратные вызовы для получения местоположения. После получения необходимой информации из объекта местоположения я создам новый поток для вызова моих собственных созданных обратных вызовов (которые реализованы на стороне приложения).

Ответы [ 3 ]

2 голосов
/ 14 октября 2011

Вы можете использовать AsyncTask вместо того, чтобы каждый раз создавать новую тему? AsyncTask управляет фиксированным пулом потоков (или одним фоновым потоком - в зависимости от версии Android) и позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса без необходимости манипулировать потоками и / или обработчиками.

Однако мне интересно, зачем вам нужно создавать бесконечный цикл внутри метода onHandleIntent? Тем самым вы не позволяете вашим IntentService получать дальнейшие намерения. Так как в IntentService:

Все запросы обрабатываются в одном рабочем потоке - они могут принимать как долго по мере необходимости (и не будет блокировать основной цикл приложения), но одновременно обрабатывается только один запрос.

Я думаю, что вы хотите выполнить какой-то долго выполняющийся код из потока пользовательского интерфейса в IntentService. Но это не требует создания бесконечного цикла в рабочем потоке IntentService. Просто отправляйте запросы по мере необходимости в IntentService, используя вызов Context.startService(Intent). Если вы хотите, чтобы IntentService отправил какой-либо результат или просто вызвал обратный вызов в потоке пользовательского интерфейса, вы можете передать объект Messenger (или ResultReceiver) с помощью Intent.

Активность

final Handler uiHandler = new Handler(Looper.getMainLooper());

private void postTask() {
  Intent intent = new Intent("com.yourservice.DOACTION");
  intent.putExtra("messenger", new Messenger(handler));
  intent.putExtra("object", YourObject());   // pass other Parcelable objects
  startService(intent);
}

IntentService

protected void onHandleIntent(Intent intent) {
  Messenger messenger = intent.getParcelableExtra("messenger");
  YourObject object = intent.getParcelableExtra("object");

  //... do work here ...

  Message msg = Message.obtain();
  msg.what = CALLBACK_MESSAGE;
  msg.setData(someResult);
  messenger.send(Message.obtain()); 
}
1 голос
/ 06 октября 2011

Просмотрите документы для ExecutorService (не путать со Службами Android) и Executors . Там есть несколько примеров использования пулов потоков.

0 голосов
/ 19 октября 2011

Итак, подождите, зачем вам использовать все эти обратные вызовы?Разве вы не можете просто заставить каждое намерение кодировать то, что нужно сделать, а затем заставить ваш onHandleIntent выполнить другой код, основанный на информации намерения.Это способ, которым IntentService предназначен для использования.

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

...