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


в службе android я создал поток (ы) для выполнения некоторых фоновых задач.

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

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

спасибо,

11 252

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() {
});

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

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

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

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 .

  1. создать HandlerThread и запустить его
  2. создать проводник С Looper от HandlerThread :requestHandler
  3. post a Runnable задачи requestHandler

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

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

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

android-looper-handler-handlerthread-i

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