Доступ к обработчику потока пользовательского интерфейса из службы


90

Я пробую что-то новое на Android, для чего мне нужно получить доступ к обработчику потока пользовательского интерфейса.

Знаю следующее:

  1. У потока пользовательского интерфейса есть собственный обработчик и цикл.
  2. Любое сообщение будет помещено в очередь сообщений потока пользовательского интерфейса.
  3. Лупер забирает событие и передает его обработчику.
  4. Обработчик обрабатывает сообщение и отправляет конкретное событие пользовательскому интерфейсу.

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

Я хотел бы знать, возможно ли это. Если да, то предложите несколько фрагментов кода, чтобы я мог попробовать.

С уважением, Гириш

Ответы:


179

Этот фрагмент кода создает обработчик, связанный с основным потоком (UI):

Handler handler = new Handler(Looper.getMainLooper());

Затем вы можете публиковать материалы для выполнения в основном потоке (UI) следующим образом:

handler.post(runnable_to_call_from_main_thread);

Если сам обработчик создается из основного (UI) потока, аргумент можно для краткости опустить:

Handler handler = new Handler();

В Руководстве для разработчиков Android по процессам и потокам есть дополнительная информация.


2
Протестировал, и он отлично работает! Пример варианта использования: у меня есть веб-интерфейс, который обслуживается сервером, запущенным непосредственно на устройстве. Поскольку интерфейс можно использовать для прямого взаимодействия с пользовательским интерфейсом, а сервер должен работать в собственном потоке, мне нужен был способ прикоснуться к потоку пользовательского интерфейса извне Activity. Описанный вами метод отлично сработал.
mrPjer

1
Блестяще. Работает как шарм, и очень полезно. БЛАГОДАРЮ ВАС.
JRun 02

perfect ^^ просто использовал его для обновления моего пользовательского интерфейса из StreamingService. именно то, что мне нужно, спасибо!
An-droid

знаете ли вы, могу ли я создать одноэлементный экземпляр обработчика и использовать его каждый раз, когда мне нужно что-то запускать в потоке пользовательского интерфейса?
HelloWorld

Думаю, мы никогда не узнаем
Денни

28

Создайте Messengerобъект, прикрепленный к вашему, Handlerи передайте Messengerего Service(например, в Intentдополнении for startService()). ServiceМожет затем послать Messageк HandlerПОСРЕДСТВОМ Messenger. Вот пример приложения, демонстрирующий это.


Спасибо за этот совет. Это было полезно. См. Следующий стек для потока событий касания к моей активности MyDemo.dispatchTouchEvent (MotionEvent) строка: 20 PhoneWindow $ DecorView.dispatchTouchEvent (MotionEvent) строка: 1696 ViewRoot.handleMessage (сообщение) строка: 1658 ViewRoot (обработчик) .dispatchMessage (сообщение ) line: 99 Looper.loop () line: 123 // Обработка событий начинается здесь ActivityThread.main (String []) line: 4203 Здесь ViewRoot является обработчиком. Я хочу получить ссылку на этот обработчик ... можно ли получить это из моего приложения?
iLikeAndroid

@iLikeAndroid: если вы не создавали Handler, вы не можете получить к нему доступ, AFAIK.
CommonsWare

Спасибо. Я попытался создать экземпляр ViewRoot. Это не что иное, как обработчик. Теперь я могу отправлять сообщения этому обработчику. Обработчик получает сообщение. Но ViewRoot не может обработать сообщение, так как оно не инициализировано должным образом. Мне нужно вызвать ViewRoot.setView (), чтобы инициализировать соответствующие данные для ViewRoot. Я хочу знать, есть ли представление по умолчанию или базовое представление и т. Д., Которое я могу использовать для инициализации?
iLikeAndroid

@iLikeAndroid: Нет ViewRootв Android SDK, AFAICT.
CommonsWare

1
@ hadez30: Лично я мало пользуюсь связанными сервисами. Вы по-прежнему можете использовать Handler/ Messenger, хотя я бы заменил все это шиной событий (например, EventBus greenrobot).
CommonsWare

4

На данный момент я предпочитаю использовать библиотеку шины событий, такую ​​как Otto, для такого рода проблем. Просто подпишитесь на желаемые компоненты (активность):

protected void onResume() {
    super.onResume();
    bus.register(this);
}

Затем укажите метод обратного вызова:

public void onTimeLeftEvent(TimeLeftEvent ev) {
    // process event..
}

а затем, когда ваша служба выполнит такую ​​инструкцию:

bus.post(new TimeLeftEvent(340));

Этот POJO будет передан в указанное выше действие и во все другие компоненты подписки. Просто и элегантно.


3

Предлагаю попробовать следующий код:

    new Handler(Looper.getMainLooper()).post(() -> {

        //UI THREAD CODE HERE



    });

Некоторое дополнительное объяснение требуется, чтобы помочь OP.
Moog

2

Вы можете получить значения через широковещательный приемник ... следующим образом: сначала создайте свой собственный IntentFilter как,

Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");

Затем создайте внутренний класс BroadcastReceiver как,

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    /** Receives the broadcast that has been fired */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction()=="YOUR_INTENT_FILTER"){
           //HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
           String receivedValue=intent.getStringExtra("KEY");
        }
    }
};

Теперь зарегистрируйте свой широковещательный приемник в onResume () как,

registerReceiver(broadcastReceiver, intentFilter);

И, наконец, отмените регистрацию BroadcastReceiver в onDestroy () как,

unregisterReceiver(broadcastReceiver);

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

Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

.... ура :)


1

В kotlinто как вы можете это сделать

Скажем, если вы хотите показать тост-сообщение от службы

val handler = Handler(Looper.getMainLooper())
handler.post {
   Toast.makeText(context, "This is my message",Toast.LENGTH_LONG).show()
}

0

Решение:

  1. Создайте обработчик с помощью Looper из основного потока: requestHandler
  2. Создайте с Handlerпомощью LooperMain Thread: responseHandler и переопределите handleMessageметод
  3. опубликовать Runnable задачу по запросуHandler
  4. Внутри Runnableзадачи вызовите sendMessage на responseHandler
  5. Это sendMessageрезультат вызова handleMessage в responseHandler.
  6. Получить атрибуты из Messageи обработать, обновить UI

Образец кода:

    /* Handler from UI Thread to send request */

    Handler requestHandler = new Handler(Looper.getMainLooper());

     /* Handler from UI Thread to process messages */

    final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {

            /* Processing handleMessage */

            Toast.makeText(MainActivity.this,
                    "Runnable completed with result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

    for ( int i=0; i<10; i++) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                   /* Send an Event to UI Thread through message. 
                      Add business logic and prepare message by 
                      replacing example code */

                    String text = "" + (++rId);
                    Message msg = new Message();

                    msg.obj = text.toString();
                    responseHandler.sendMessage(msg);
                    System.out.println(text.toString());

                } catch (Exception err) {
                    err.printStackTrace();
                }
            }
        };
        requestHandler.post(myRunnable);
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.