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


326

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

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

Есть ли способ , чтобы получить Handlerиз главного потока и поста Message/ Runnableк нему из другого потока?

Спасибо,


2
Вы также можете использовать Пользовательский приемник вещания .... попробуйте мой ответ здесь, [Внутренний приемник вещания] [1] [1]: stackoverflow.com/a/22541324/1881527
Мельбурн Лопес

Есть много способов. Помимо ответа Дэвида и комментария dzeikei в его ответе, (3) вы можете использовать Broadcast Receiver или (4) передать обработчику дополнительные функции Intent, использованные для запуска службы, а затем получить обработчик основного потока внутри службы с помощью getIntent ( ) .getExtras ().
Ашок Биджой Дебнатх

2
@ sazzad-hissain-khan, Зачем отмечать этот вопрос 2012 года в основном ответами на Java тегом kotlin?
Tenfour04

Ответы:


617

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

1. Если ваш фоновый поток имеет ссылку на 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);

1
Спасибо Дэвиду, это сработало для меня, еще одна вещь, если бы вы могли мне помочь, если бы я расширил Handler и impl handleMessage (), это помешало бы главному потоку обрабатывать свои сообщения? это только вопрос из любопытства ..
Ахмед

2
Нет. Если вы создаете подкласс Handler(или используете Handler.Callbackинтерфейс), ваш handleMessage()метод будет вызываться ТОЛЬКО для сообщений, отправленных с использованием вашего обработчика. Основной поток использует другой обработчик для отправки / обработки сообщений, поэтому нет конфликта.
Дэвид Вассер

205
Я полагаю, что вам даже не понадобится контекст, если вы используетеHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
Незначительная точка; ваш код не идет туда, где ... в настоящее время. Это должно бытьnew Runnable(){@Override public void run() {....}};
Ричард Тингл

1
@SagarDevanga Это не то место, где можно задать другой вопрос. Пожалуйста, разместите новый вопрос, а не комментарий к несвязанному ответу. Таким образом, вы получите лучший и быстрый ответ.
Дэвид Вассер

138

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

Метод ниже может работать в самых простых случаях.

Если я вас правильно понимаю, вам нужно выполнить некоторый код в потоке GUI приложения (не могу думать ни о чем другом, называемом «основным» потоком). Для этого есть метод на 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

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


20
ОП говорит, что он работает темы в Service. Вы не можете использовать runOnUiThread()в Service. Этот ответ вводит в заблуждение и не затрагивает заданный вопрос.
Дэвид Вассер

31

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

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

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

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

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

3). Опубликуйте свой Runnable для uiHandler:

uiHandler.post(runnable);

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


29

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

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

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

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

}

7
ОП говорит, что он работает темы в Service. Вы не можете использовать runOnUiThread()в Service.
Дэвид Вассер

@DavidWasser Это где-нибудь задокументировано? В документации по методам ничего не сказано об этом. developer.android.com/reference/android/app/…
Грег Браун

1
@GregBrown Как вы указали своей ссылкой на Activityдокументацию, runOnUiThread()это метод Activity. Это не метод Service, поэтому вы не можете его использовать. Что может быть понятнее этого?
Дэвид Вассер

@ DavidWasser Справедливо достаточно. Я даже не помню, почему я задал этот вопрос сейчас (он был опубликован почти год назад).
Грег Браун

24

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

   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
    })

20

Котлин версии

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

runOnUiThread {
    //code that runs in main
}

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

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

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

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}

Не уверен, что это за версия Kotlin, но мне пришлось добавить фигурные скобки:runOnUiThread(){...}
Wex

5

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

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}


4

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

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

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

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

mBinder.registerHandler(new Handler());

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

mHandler.post(runnable);

3

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

  1. Создать HandlerThread и запустите его
  2. Создайте обработчик с Looper из HandlerThread:requestHandler
  3. postRunnableзадача поrequestHandler

Связь с UI Thread от HandlerThread

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

Пример : обновление TextViewс данными, полученными от веб-службы. Поскольку веб-служба должна вызываться в потоке, не связанном с пользовательским интерфейсом, созданным 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-и-почему-вы-должны-быть, используя их-в-ваш-Android-приложений

Android-петлителя-обработчик-handlerthread-я


2

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

Java (8):

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

Котлин:

this.runOnUiThread {
     //your main thread code
}

1

Следуйте этому методу. Используя этот способ, вы можете просто обновить пользовательский интерфейс из фонового потока. 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);
    }

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

1

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

Обновить

doAsync {
   ...
}

Устаревшее

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

1

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

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)}
}

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

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


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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.