Запуск кода в основном потоке из другого потока

В службе Android я создал поток (ы) для выполнения некоторой фоновой задачи.

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

Есть ли способ получить Handler из основного потока и опубликовать в него _3 _ / _ 4_ из другого моего потока?


person Ahmed    schedule 20.06.2012    source источник
comment
Вы также можете использовать настраиваемый широковещательный приемник .... попробуйте мой ответ здесь [Внутренний широковещательный приемник] [1] [1]: http://stackoverflow.com/a/22541324/1881527   -  person Melbourne Lopes    schedule 21.03.2014
comment
Есть много способов. Помимо ответа Дэвида и комментария dzeikei в его ответе, (3) вы можете использовать широковещательный приемник или (4) передать обработчик в дополнениях Intent, используемых для запуска службы, а затем получить обработчик основного потока внутри службы с помощью getIntent ( ) .getExtras ().   -  person Ashok Bijoy Debnath    schedule 04.12.2014
comment
@ sazzad-hissain-khan, зачем помечать этот вопрос 2012 года с ответами в основном на Java с помощью тега kotlin?   -  person Tenfour04    schedule 07.08.2019


Ответы (16)


ПРИМЕЧАНИЕ. Этот ответ привлек столько внимания, что мне нужно его обновить. Поскольку исходный ответ был опубликован, комментарий от @dzeikei привлек почти такое же внимание, как и исходный ответ. Итак, вот 2 возможных решения:

1. Если в фоновом потоке есть ссылка на объект Context:

Убедитесь, что ваши фоновые рабочие потоки имеют доступ к объекту Context (может быть контекстом приложения или контекстом службы). Затем просто сделайте это в фоновом рабочем потоке:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. Если в фоновом потоке нет объекта Context (или он ему не нужен)

(предложено @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);
person David Wasser    schedule 20.06.2012
comment
Спасибо, Дэвид, это сработало для меня, еще одна вещь, если вы могли бы мне помочь, если я расширю Handler и impl handleMessage (), помешает ли это главному потоку обрабатывать свои сообщения? это всего лишь вопрос из ненависти .. - person Ahmed; 20.06.2012
comment
Нет. Если вы создаете подкласс Handler (или используете Handler.Callback интерфейс), ваш handleMessage() метод будет вызываться ТОЛЬКО для сообщений, которые были отправлены с помощью вашего обработчика. Основной поток использует другой обработчик для публикации / обработки сообщений, поэтому конфликтов нет. - person David Wasser; 20.06.2012
comment
Я считаю, что вам даже не понадобится контекст, если вы используете Handler mainHandler = new Handler(Looper.getMainLooper()); - person dzeikei; 05.11.2013
comment
Второстепенная точка; ваш код не идет туда, где ... сейчас. Это должно быть new Runnable(){@Override public void run() {....}}; - person Richard Tingle; 31.07.2014
comment
@DavidWasser, большое спасибо, ваш код делает именно то, что я хочу, но просто запрос. Я использую этот код, чтобы получить местоположение пользователя в splashScreen. Как я могу установить логическую переменную, чтобы проверить, получил ли я ответ, в зависимости от того, какое действие я решаю начать. - person Sagar Devanga; 21.01.2015
comment
@SagarDevanga Это не то место, чтобы задавать другой вопрос. Пожалуйста, разместите новый вопрос, а не комментарий к несвязанному ответу. Так вы получите лучший и быстрый ответ. - person David Wasser; 21.01.2015
comment
Также контекст не требуется: Handler mainThreadHandler = new Handler (Looper.getMainLooper ()); - person bduhbya; 12.11.2016
comment
Если вы не хотите использовать Runnable, просто сделайте это следующим образом: Message msg = mainHandler.obtainMessage (); msg.sendToTarget (); // Не используйте dispatchMessage - person Shailendra Yadav; 09.09.2019
comment
Если у вас есть доступ к активности, вы можете вызвать метод runOnUiThread () для публикации runnable для выполнения в потоке пользовательского интерфейса из другого потока. - person Ahad; 01.12.2019

Как правильно указал нижеприведенный комментатор, это не общее решение для сервисов, а только для потоков, запускаемых из вашей активности (сервис может быть таким потоком, но не все из них). По сложной теме коммуникации между сервисами и действиями, пожалуйста, прочтите весь раздел «Сервисы» в официальном документе - это сложно, поэтому было бы неплохо понять основы: http://developer.android.com/guide/components/services.html#Notifications

Приведенный ниже метод может работать в простейших случаях:

Если я правильно вас понял, вам нужен какой-то код для выполнения в потоке графического интерфейса приложения (не могу думать ни о чем другом, называемом основным потоком). Для этого есть метод на Activity:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

Док: http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29.

Надеюсь, это то, что вы ищете.

person Alexey Vassiliev    schedule 20.06.2012
comment
OP говорит, что он запускает потоки в Service. Вы не можете использовать runOnUiThread() в Service. Этот ответ вводит в заблуждение и не отвечает на заданный вопрос. - person David Wasser; 27.11.2013

Версии Kotlin

Когда вы занимаетесь деятельностью, используйте

runOnUiThread {
    //code that runs in main
}

Когда у вас есть контекст активности, тогда используйте mContext

mContext.runOnUiThread {
    //code that runs in main
}

Когда вы находитесь в месте, где контекст недоступен, используйте

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}
person Sazzad Hissain Khan    schedule 02.07.2019
comment
Не уверен, что это за версия Kotlin, но мне пришлось добавить фигурные скобки: runOnUiThread(){...} - person Wex; 14.05.2020
comment
У меня отлично работает с Android Studio 4.1.1 на Mac. - person Golden Thumb; 12.12.2020

Есть еще один простой способ, если у вас нет доступа к Context.

1). Создайте обработчик из основного петлителя:

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

2). Реализуйте интерфейс Runnable:

Runnable runnable = new Runnable() { // your code here }

3). Разместите свой Runnable в uiHandler:

uiHandler.post(runnable);

Вот и все ;-) Развлекайтесь с потоками, но не забывайте их синхронизировать.

person tema_man    schedule 03.07.2015

Если вы запускаете код в потоке, например отложите какое-то действие, тогда вам нужно вызвать runOnUiThread из контекста. Например, если ваш код находится внутри класса MainActivity, используйте это:

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});

Если ваш метод может быть вызван либо из основного (поток пользовательского интерфейса), либо из других потоков, вам нужна проверка, например:

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}
person Alexander Volkov    schedule 27.09.2013
comment
OP говорит, что он запускает потоки в Service. Вы не можете использовать runOnUiThread() в Service. - person David Wasser; 27.11.2013
comment
@DavidWasser Это где-нибудь задокументировано? В документации метода ничего об этом не упоминается. developer.android.com/reference/ android / app / - person Greg Brown; 26.08.2015
comment
@GregBrown Как вы указали в своей ссылке на Activity документацию, runOnUiThread() - это метод Activity. Это не метод Service, поэтому вы не можете его использовать. Что может быть яснее этого? - person David Wasser; 04.08.2016
comment
@DavidWasser Достаточно честно. Я даже не помню, почему я задал этот вопрос сейчас (его разместили почти год назад). - person Greg Brown; 07.08.2016

Блок сжатого кода выглядит следующим образом:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

При этом не требуется передавать ссылку на действие или ссылку на приложение.

Котлин эквивалент:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })
person david m lee    schedule 20.02.2018

Самый простой способ, особенно если у вас нет контекста, если вы используете RxAndroid, вы можете:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}
person Inn0vative1    schedule 30.10.2018

Более точный код Kotlin с использованием обработчика:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }
person Hamed Jaliliani    schedule 14.01.2019

Один из методов, который я могу придумать, таков:

1) Дайте возможность пользовательскому интерфейсу привязаться к службе.
2) Откройте метод, подобный приведенному ниже, с помощью Binder, который регистрирует ваш Handler:

public void registerHandler(Handler handler) {
    mHandler = handler;
}

3) В потоке пользовательского интерфейса вызовите указанный выше метод после привязки к службе:

mBinder.registerHandler(new Handler());

4) Используйте обработчик в ветке Сервиса, чтобы опубликовать свою задачу:

mHandler.post(runnable);
person Dheeraj Vepakomma    schedule 20.06.2012

HandlerThread - лучший вариант для обычных потоков Java в Android.

  1. Создайте HandlerThread и запустите его.
  2. Создайте Handler с помощью Looper из HandlerThread: requestHandler
  3. post Runnable задача на requestHandler

Связь с потоком пользовательского интерфейса от HandlerThread

  1. Создайте Handler с Looper для основного потока: responseHandler и переопределите метод handleMessage
  2. Внутри Runnable задачи другого потока (в данном случае HandlerThread) вызовите sendMessage на responseHandler
  3. Этот sendMessage результат вызова handleMessage в responseHandler.
  4. Получить атрибуты из Message и обработать его, обновить пользовательский интерфейс

Пример: обновите TextView данными, полученными от веб-службы. Поскольку веб-сервис должен вызываться в потоке, отличном от UI, он создан HandlerThread для работы в сети. Получив контент из веб-службы, отправьте сообщение обработчику основного потока (потока пользовательского интерфейса), и этот Handler обработает сообщение и обновит пользовательский интерфейс.

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

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


        } catch (Exception err) {
            err.printStackTrace();
        }
    }
};
requestHandler.post(myRunnable);

Полезные статьи:

handlerthreads-and-why-you-should-be-using-them-in-your-android-apps

android-looper-handler-handlerthread-i

person Ravindra babu    schedule 09.05.2017

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

Java (8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });

Котлин:

this.runOnUiThread {
     //your main thread code
}
person mevdev    schedule 14.06.2018

Так что удобнее всего делать что-то вроде:

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}

И после:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}
person HotJard    schedule 21.10.2019
comment
AsyncTask кажется устаревшим - person Sunkas; 25.06.2021

Следуйте этому методу. Таким образом вы можете просто обновить пользовательский интерфейс из фонового потока. runOnUiThread работает в основном (UI) потоке. Я думаю, что этот фрагмент кода менее сложен и прост, особенно для новичков.

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });

в случае услуги

создать обработчик в oncreate

 handler = new Handler();

тогда используйте это так

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }
person MarGin    schedule 19.02.2018

для Kotlin вы можете использовать Anko corountines:

обновить

doAsync {
   ...
}

не рекомендуется

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}
person Francis    schedule 17.09.2018

public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

Это также может без проблем работать в классе обслуживания.

person Revelation A.F    schedule 23.08.2018
comment
Хотя этот код может ответить на вопрос, вы все же можете рассмотреть возможность добавления нескольких пояснительных предложений, поскольку это увеличивает ценность вашего ответа для других пользователей! - person MBT; 23.08.2018

В Kotlin это внутри любой функции:

runOnUiThread {
   // Do work..
}
person Abdulmajeed Alyafey    schedule 25.09.2019
comment
Это только когда вы находитесь на Activity. - person Farsheel; 06.01.2020