Выполнение кода в основном потоке из другого потока
в службе android я создал поток (ы) для выполнения некоторых фоновых задач.
у меня есть ситуация, когда поток должен опубликовать определенную задачу в очереди сообщений основного потока, например a Runnable
.
есть ли способ сделать Handler
основной поток и сообщение Message
/Runnable
к нему из моего другого потока ?
спасибо,
11 ответов:
Примечание: этот ответ получил так много внимания, что мне нужно обновить его. Поскольку оригинальный ответ был опубликован, комментарий от @dzeikei получил почти столько же внимания, сколько и оригинальный ответ. Итак, вот 2 возможных решения:
1. Если ваш фоновый поток имеет ссылку на
Как правильно указал комментатор ниже, это не общее решение для служб, а только для потоков, запущенных из вашей деятельности (служба может быть такой поток, но не все из них). По сложной теме служебно-деятельностного общения ознакомьтесь с целым разделом услуг официального документа-он сложный, поэтому стоило бы разобраться в основах: 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() { });
надеюсь, это то, что вы ищете.
есть еще один простой способ, если у вас нет доступа к контексту.
1). Создайте обработчик из основного петлителя:
Handler uiHandler = new Handler(Looper.getMainLooper());
2). Реализовать интерфейс Runnable:
Runnable runnable = new Runnable() { // your code here }
3). Отправьте свой Runnable в uiHandler:
uiHandler.post(runnable);
вот и все ; -) развлекайтесь с потоками, но не забывайте их синхронизировать.
если вы запускаете код в потоке, например, задерживаете какое-то действие, то вам нужно вызвать
runOnUiThread
из контекста. Например, если ваш код находится внутриMainActivity
класс тогда используйте это:MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { myAction(); } });
если ваш метод может быть вызван либо из main (UI thread), либо из других потоков, вам нужна проверка, например:
public void myMethod() { if( Looper.myLooper() == Looper.getMainLooper() ) { myAction(); } else { }
сокращенный кодовый блок выглядит следующим образом:
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 })
один метод, который я могу придумать, это:
1) пусть пользовательский интерфейс привязать к службе.
2) выставить метод, как показано ниже поBinder
, которая регистрирует вашHandler
:public void registerHandler(Handler handler) { mHandler = handler; }
3) в потоке пользовательского интерфейса вызовите приведенный выше метод после привязки к службе:
mBinder.registerHandler(new Handler());
4) Используйте обработчик в потоке службы, чтобы опубликовать свою задачу:
mHandler.post(runnable);
HandlerThread
Это лучший вариант для обычных потоков java в Android .
- создать HandlerThread и запустить его
- создать проводник С Looper от HandlerThread :
requestHandler
post
aRunnable
задачиrequestHandler
связь с потоком пользовательского интерфейса от
HandlerThread
- создать
Handler
СLooper
для основного потока :responseHandler
и переопределитьhandleMessage
метод- внутри
Runnable
задача другого потока (HandlerThread
в этом случае), вызовsendMessage
onresponseHandler
- этой
sendMessage
результат вызоваhandleMessage
inresponseHandler
.- получить атрибуты из
Message
и обработать его, обновить UIпример обновление:
TextView
С данными, полученными от веб-службы. Поскольку веб-служба должна вызываться в потоке без пользовательского интерфейса, createdHandlerThread
для работы в Сети. Как только вы получите содержимое из веб-службы, отправьте сообщение своему обработчику основного потока (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-и-зачем-ты-должен-быть-через-них-в-ваш-андроид-приложения
следуйте этому методу. Используя этот способ вы можете просто обновить UI из фонового потока. 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); }
Я знаю, что это старый вопрос, но я наткнулся на основной поток однострочный, который я использую как в Kotlin, так и в Java. Это может быть не лучшим решением для службы, но для вызова чего-то, что изменит пользовательский интерфейс внутри фрагмента, это чрезвычайно просто и очевидно.
Java (8):
getActivity().runOnUiThread(()->{ //your main thread code });
Котлин:
this.runOnUiThread(()->{ //your main thread code });
public void mainWork() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //Add Your Code Here } }); }
Это также может работать в классе обслуживания не вопрос.
для Котлина можно использовать Anko corountines:
async(UI) { // Code run on UI thread // Use ref() instead of this@MyActivity }