Запуск коду в основному потоці з іншого потоку


326

У сервісі Android я створив нитки для виконання фонового завдання.

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

Чи є спосіб отримати Handlerосновну нитку та додати Message/ Runnableдо неї з моєї іншої теми?

Дякую,


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

Є багато способів. Окрім відповіді Девіда та коментаря dzeikei у його відповіді, (3) ви можете використовувати приймач широкомовної передачі або (4) передавати обробник у додатках намірів, що використовуються для запуску послуги, а потім отримати обробник основного потоку всередині сервісу за допомогою getIntent ( ) .getExtras ().
Ашок Біджой Дебнат

2
@ sazzad-hissain-khan, навіщо мітити це питання з 2012 року, з більшості відповідей на Java за допомогою тега kotlin?
Tenfour04

Відповіді:


617

ПРИМІТКА. Ця відповідь привернула стільки уваги, що мені потрібно її оновити. Оскільки оригінальна відповідь була опублікована, коментар від @dzeikei привернув майже стільки ж уваги, скільки і оригінальну відповідь. Ось два варіанти:

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 # Повідомлення

Наведений нижче метод може працювати в найпростіших випадках.

Якщо я правильно вас зрозумів, вам потрібно виконати якийсь код, який потрібно виконати в потоці 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
}

Не впевнений, що це за версія runOnUiThread(){...}
Котліна

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 Threads в Android.

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

Спілкування з UI Thread від HandlerThread

  1. Створіть метод Handlerз Looperдля основного потоку: responseHandlerта handleMessageметод переопределення
  2. Усередині Runnableзавдання іншого Thread ( HandlerThreadв даному випадку), телефонуйте sendMessageзаresponseHandler
  3. Цей sendMessageрезультат виклику handleMessageв responseHandler.
  4. Отримайте атрибути від 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-use-them-in-your-android-apps

android-looper-handler-handlerthread-i


2

Я знаю, що це давнє питання, але я натрапив на один вкладиш основного потоку, який я використовую як у Kotlin, так і в Java. Це може бути не найкращим рішенням для послуги, але для виклику чогось, що змінить інтерфейс користувача всередині фрагмента, це надзвичайно просто і очевидно.

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

Тож найзручніше - це робити щось таке:

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.