Menjalankan kode di utas utama dari utas lainnya

326

Dalam layanan android saya telah membuat utas untuk melakukan tugas latar belakang.

Saya memiliki situasi di mana utas perlu memposting tugas tertentu pada antrian pesan utas, misalnya a Runnable.

Apakah ada cara untuk mendapatkan Handlerutas utama dan memposting Message/ Runnableutasnya dari utas saya yang lain?

Terima kasih,

Ahmed
sumber
2
Anda juga dapat menggunakan penerima siaran Ubahsuaian .... coba jawabanku di sini, [Penerima Siaran Dalam] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes
Ada banyak cara. Terlepas dari jawaban David & komentar dzeikei dalam jawabannya, (3) Anda dapat menggunakan Penerima Siaran, atau (4) meneruskan pawang dalam ekstra dari Intent yang digunakan untuk memulai layanan, dan kemudian mengambil pawang utama di dalam layanan menggunakan getIntent ( ) .getExtras ().
Ashok Bijoy Debnath
2
@ sazzad-hissain-khan, Mengapa menandai pertanyaan ini dari 2012 dengan sebagian besar jawaban di Jawa dengan tag kotlin?
Tenfour04

Jawaban:

617

CATATAN: Jawaban ini telah mendapatkan begitu banyak perhatian, sehingga saya perlu memperbaruinya. Sejak jawaban asli diposting, komentar dari @dzeikei hampir mendapat perhatian sama seperti jawaban aslinya. Jadi, inilah 2 solusi yang mungkin:

1. Jika utas latar belakang Anda memiliki referensi ke Contextobjek:

Pastikan utas pekerja latar belakang Anda memiliki akses ke objek Konteks (bisa konteks Aplikasi atau konteks Layanan). Kemudian lakukan ini di utas latar belakang pekerja:

// 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. Jika utas latar belakang Anda tidak memiliki (atau membutuhkan) Contextobjek

(disarankan oleh @dikeikei):

// 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);
David Wasser
sumber
1
Terima kasih David, itu berhasil untuk saya, satu hal lagi jika Anda dapat membantu saya, jika saya memperpanjang Handler dan mengimplementasikan handleMessage () akankah itu mencegah utas utama menangani pesannya? itu hanya pertanyaan karena ingin tahu ..
Ahmed
2
Tidak. Jika Anda subkelas Handler(atau menggunakan Handler.Callbackantarmuka) handleMessage()metode Anda HANYA akan dipanggil untuk pesan yang telah diposting menggunakan handler Anda. Utas utama menggunakan penangan yang berbeda untuk mengirim / memproses pesan sehingga tidak ada konflik.
David Wasser
205
Saya percaya Anda bahkan tidak perlu konteks jika Anda menggunakanHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei
6
Titik kecil; kode Anda tidak pergi ke tempat ... saat ini. Seharusnyanew Runnable(){@Override public void run() {....}};
Richard Tingle
1
@SagarDevanga Ini bukan tempat yang tepat untuk mengajukan pertanyaan berbeda. Silakan kirim pertanyaan baru, bukan komentar untuk jawaban yang tidak terkait. Anda akan mendapatkan respons yang lebih baik dan lebih cepat seperti itu.
David Wasser
138

Sebagai komentator di bawah ini menunjuk dengan benar, ini bukan solusi umum untuk layanan, hanya untuk utas yang diluncurkan dari aktivitas Anda (layanan dapat berupa utas tersebut, tetapi tidak semuanya). Pada topik rumit komunikasi layanan-aktivitas, harap baca seluruh bagian Layanan pada dokumen resmi - ini rumit, jadi akan sangat membantu untuk memahami dasar-dasarnya: http://developer.android.com/guide/components/services.html #Notifikasi

Metode di bawah ini dapat berfungsi dalam kasus yang paling sederhana.

Jika saya mengerti Anda dengan benar, Anda memerlukan beberapa kode yang akan dieksekusi di utas GUI dari aplikasi (tidak dapat memikirkan hal lain yang disebut utas "utama"). Untuk ini ada metode di 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

Semoga ini yang Anda cari.

Alexey Vassiliev
sumber
20
OP mengatakan dia menjalankan utas di a Service. Anda tidak dapat menggunakan runOnUiThread()dalam Service. Jawaban ini menyesatkan dan tidak menjawab pertanyaan yang diajukan.
David Wasser
31

Ada cara sederhana lain, jika Anda tidak memiliki akses ke Konteks.

1). Buat penangan dari looper utama:

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

2). Menerapkan antarmuka Runnable:

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

3). Poskan Runnable Anda ke uiHandler:

uiHandler.post(runnable);

Itu saja ;-) Bersenang-senang dengan utas, tapi jangan lupa untuk menyinkronkannya.

tema_man
sumber
29

Jika Anda menjalankan kode dalam utas, mis. Lakukan penundaan beberapa tindakan, maka Anda perlu memohon runOnUiThreaddari konteksnya. Misalnya, jika kode Anda berada di dalam MainActivitykelas maka gunakan ini:

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

Jika metode Anda dapat dipanggil baik dari utas utama (UI) atau dari utas lainnya Anda perlu pemeriksaan seperti:

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

}
Alexander Volkov
sumber
7
OP mengatakan dia menjalankan utas di a Service. Anda tidak dapat menggunakan runOnUiThread()dalam Service.
David Wasser
@ Davidvider Apakah itu didokumentasikan di mana saja? Metode dokumen tidak menyebutkan apa pun tentangnya. developer.android.com/reference/android/app/…
Greg Brown
1
@GregBrown Seperti yang telah ditunjukkan oleh tautan Anda ke Activitydokumentasi, runOnUiThread()adalah metode Activity. Ini bukan metode Service, karena itu Anda tidak dapat menggunakannya. Apa yang bisa lebih jelas dari itu?
David Wasser
@ David FairWasser Cukup adil. Saya bahkan tidak ingat mengapa saya mengajukan pertanyaan itu sekarang (sudah diposting hampir setahun yang lalu).
Greg Brown
24

Blok kode terkondensasi adalah sebagai berikut:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

Ini tidak termasuk meneruskan referensi Kegiatan atau referensi Aplikasi.

Kotlin Equivalent:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })
david m lee
sumber
20

Versi Kotlin

Saat Anda melakukan suatu kegiatan , maka gunakan

runOnUiThread {
    //code that runs in main
}

Ketika Anda memiliki konteks aktivitas , mContext kemudian gunakan

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

Ketika Anda berada di suatu tempat di mana tidak ada konteks yang tersedia , maka gunakan

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}
Sazzad Hissain Khan
sumber
Tidak yakin apa versi Kotlin ini, tapi saya harus menambahkan kawat gigi:runOnUiThread(){...}
Wex
5

Cara paling sederhana terutama jika Anda tidak memiliki konteks, jika Anda menggunakan RxAndroid yang dapat Anda lakukan:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}
Inn0vative1
sumber
5

Kode Kotlin yang lebih tepat menggunakan handler:

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

Salah satu metode yang dapat saya pikirkan adalah ini:

1) Biarkan UI mengikat ke layanan.
2) Paparkan metode seperti di bawah ini oleh Binderyang mendaftarkan Anda Handler:

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

3) Di utas UI, panggil metode di atas setelah mengikat ke layanan:

mBinder.registerHandler(new Handler());

4) Gunakan pawang di utas Layanan untuk mengirim tugas Anda:

mHandler.post(runnable);
Dheeraj Vepakomma
sumber
3

HandlerThread adalah pilihan yang lebih baik untuk Thread java normal di Android.

  1. Buat HandlerThread dan mulai saja
  2. Buat Handler dengan Looper dari HandlerThread:requestHandler
  3. postsebuah Runnabletugas direquestHandler

Komunikasi dengan UI Thread dari HandlerThread

  1. Buat Handlerdengan Looperutas utama: responseHandlerdan ganti handleMessagemetode
  2. Di dalam Runnabletugas Thread lain ( HandlerThreaddalam hal ini), panggilan sendMessagediresponseHandler
  3. sendMessageDoa hasil ini handleMessagedi responseHandler.
  4. Dapatkan atribut dari Messagedan proses, perbarui UI

Contoh : Pembaruan TextViewdengan data yang diterima dari layanan web. Karena layanan web harus dijalankan pada utas non-UI, dibuat HandlerThreaduntuk Operasi Jaringan. Setelah Anda mendapatkan konten dari layanan web, kirim pesan ke penangan utas utama Anda (Utas UI) dan ituHandler akan menangani pesan dan perbarui UI.

Kode sampel:

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

Artikel yang berguna:

handlerthreads-and-why-you-should-be-use-them-in-your-android-apps

android-looper-handler-handlerthread-i

Ravindra babu
sumber
2

Saya tahu ini adalah pertanyaan lama, tetapi saya menemukan thread utama satu-liner yang saya gunakan di Kotlin dan Java. Ini mungkin bukan solusi terbaik untuk layanan, tetapi untuk memanggil sesuatu yang akan mengubah UI di dalam sebuah fragmen, ini sangat sederhana dan jelas.

Jawa (8):

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

Kotlin:

this.runOnUiThread {
     //your main thread code
}
mevdev
sumber
1

Ikuti metode ini. Dengan menggunakan cara ini, Anda cukup memperbarui UI dari utas latar belakang. runOnUiThread bekerja pada utas utama (UI). Saya pikir cuplikan kode ini tidak terlalu rumit dan mudah, terutama untuk pemula.

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

                   }
                });
            }
        });

dalam hal layanan

buat handler di oncreate

 handler = new Handler();

lalu gunakan seperti ini

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }
Batas
sumber
Terima kasih atas cuplikan kode ini, yang mungkin memberikan bantuan terbatas dan segera. Sebuah penjelasan yang tepat akan sangat meningkatkan nilai jangka panjang dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah ini, dan akan membuatnya lebih bermanfaat untuk pembaca masa depan dengan lainnya, pertanyaan-pertanyaan serupa. Harap edit jawaban Anda untuk menambahkan beberapa penjelasan, termasuk asumsi yang Anda buat.
iBug
1

untuk Kotlin, Anda dapat menggunakan Anko corountines :

memperbarui

doAsync {
   ...
}

usang

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}
Francis
sumber
1

Jadi yang paling berguna adalah melakukan semacam:

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

Dan kemudian:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}
HotJard
sumber
0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

Ini juga dapat bekerja di kelas layanan tanpa masalah.

Wahyu AF
sumber
Meskipun kode ini dapat menjawab pertanyaan, Anda masih dapat mempertimbangkan untuk menambahkan beberapa kalimat penjelas karena hal ini meningkatkan nilai jawaban Anda untuk pengguna lain!
MBT
0

Dengan Kotlin, persis seperti ini di dalam fungsi apa pun:

runOnUiThread {
   // Do work..
}
Abdulmajeed Alyafey
sumber
2
Ini hanya ketika Anda berada di sebuah Activity.
Farsheel