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

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

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

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

Спасибо,

Ответ 1

ПРИМЕЧАНИЕ. Этот ответ получил так много внимания, что мне нужно его обновить. Поскольку исходный ответ был опубликован, комментарий от @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);

Ответ 2

Как правильно указал комментатор ниже, это не общее решение для служб, а только для потоков, запущенных из вашей активности (услугой может быть такой поток, но не все из них). По сложной теме коммуникации служебной деятельности, пожалуйста, прочитайте весь раздел Служб официального документа - он сложный, поэтому он будет платить за понимание основ: 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() {
});

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

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

Ответ 3

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

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

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

2). Внедрение интерфейса Runnable:

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

3). Отправьте свой Runnable на uiHandler:

uiHandler.post(runnable);

Что все;-) Удачи в потоках, но не забудьте их синхронизировать.

Ответ 4

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

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

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

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

}

Ответ 5

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

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

Ответ 6

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

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

runOnUiThread {
    //code that runs in main
}

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

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

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

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

Ответ 7

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

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

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

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

mBinder.registerHandler(new Handler());

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

mHandler.post(runnable);

Ответ 8

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

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

Ответ 9

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

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }

Ответ 10

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

  • Создайте HandlerThread и запустите его
  • Создайте Handler с Looper from HandlerThread: requestHandler
  • post a Runnable задача на requestHandler

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

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

Пример: обновить TextView с помощью данных, полученных от веб-службы. Поскольку веб-сервис должен быть вызван в потоке, отличном от UI, создан HandlerThread для работы в сети. После того, как вы получите контент из веб-службы, отправьте сообщение в обработчик основного потока (UI Thread) и 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

Ответ 11

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

Java (8):

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

Котлин:

this.runOnUiThread {
     //your main thread code
}

Ответ 12

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

Ответ 13

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

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

Ответ 14

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

Обновить

doAsync {
   ...
}

осуждается

async(UI) {
    // Code run on UI thread
    // Use ref() instead of [email protected]
}

Ответ 15

С Kotlin, это так же, как внутри любой функции:

runOnUiThread {
   // Do work..
}

Ответ 16

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

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