Запуск кода в основном потоке из другого потока
В сервисе 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)}
}