Tidak dapat membuat handler di dalam utas yang belum disebut Looper.prepare ()

982

Apa arti dari pengecualian berikut; bagaimana saya bisa memperbaikinya?

Ini kodenya:

Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);

Ini pengecualian:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
     at android.os.Handler.<init>(Handler.java:121)
     at android.widget.Toast.<init>(Toast.java:68)
     at android.widget.Toast.makeText(Toast.java:231)
michael
sumber
8
periksa perpustakaan ini compile 'com.shamanland:xdroid-toaster:0.0.5', itu tidak memerlukan runOnUiThread()atau Contextvariabel, semua rutin hilang! hanya memohon di Toaster.toast(R.string.my_msg);sini adalah contoh: github.com/shamanland/xdroid-toaster-example
Oleksii K.
120
Benar-benar pesan kesalahan yang bodoh! Itu bisa sesederhana - tidak dapat memanggil ini dari utas non-UI seperti yang dilakukan ketika pandangan disentuh dari utas non-UI.
Dheeraj Bhaskar
11
Bagi mereka yang mendapatkan pesan pengecualian yang sama dari kode yang berbeda: Apa arti pesan pengecualian adalah bahwa Anda memanggil kode melalui utas yang belum menyiapkan Looper. Biasanya itu berarti Anda tidak menelepon jika dari utas UI tetapi Anda harus (kasus OP) - utas normal tidak mempersiapkan Looper, tetapi utas UI selalu melakukannya.
Helin Wang
@OleksiiKropachov implementasi perpustakaan yang Anda sebutkan sangat mirip dengan menjalankan runOnUiThread ().
Helin Wang
ya, tapi ini pembungkus yang sangat berguna
Oleksii K.

Jawaban:

696

Anda memanggilnya dari utas pekerja. Anda perlu memanggil Toast.makeText()(dan sebagian besar fungsi lain yang berurusan dengan UI) dari dalam utas utama. Anda bisa menggunakan pawang, misalnya.

Cari Berkomunikasi dengan Thread UI di dokumentasi. Pendeknya:

// Set this up in the UI thread.

mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message message) {
        // This is where you do your work in the UI thread.
        // Your worker tells you in the message what to do.
    }
};

void workerThread() {
    // And this is how you call it from the worker thread:
    Message message = mHandler.obtainMessage(command, parameter);
    message.sendToTarget();
}

Pilihan lain:

Anda bisa menggunakan AsyncTask , yang berfungsi dengan baik untuk sebagian besar hal yang berjalan di latar belakang. Ini memiliki kait yang dapat Anda panggil untuk menunjukkan kemajuan, dan ketika sudah selesai.

Anda juga bisa menggunakan Activity.runOnUiThread () .

EboMike
sumber
bagaimana dengan masalah aslinya (bukan tentang AlertDialog)?
Ivan G.
5
Hanya menambahkan dua sen ke apa yang dikatakan Cleggy. Akan lebih baik untuk memberikan demonstrasi singkat tentang apa yang Anda maksudkan (betapapun dibuat-buat), sebagai contoh kode sering dapat berbicara banyak untuk dirinya sendiri.
cdata
5
untuk jawaban teknis lengkap lihat prasanta-paul.blogspot.kr/2013/09/…
tony9099
3
Di hampir semua bahasa pemrograman AFAIK yang mendukung GUI, jika Anda memperbarui / mengubah / menampilkan / berinteraksi dengan GUI secara langsung, itu harus dilakukan pada utas utama program.
Ahmed
(and most other functions dealing with the UI)Contoh dari fungsi UI yang dapat digunakan dari latar belakang adalah android.support.design.widget.Snackbar- fungsinya tidak berkurang ketika tidak menelepon dari utas UI.
Scruffy
854

Anda perlu menelepon Toast.makeText(...)dari utas UI:

activity.runOnUiThread(new Runnable() {
  public void run() {
    Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
  }
});

Ini disalin dari jawaban SO (duplikat) lain .

Jacob Marble
sumber
13
Jawaban yang bagus Ini membuat saya bingung untuk sementara waktu. Sekadar diketahui, saya tidak perlu beraktifitas. sebelum menjalankanOnUiThread.
Cen92
448

UPDATE - 2016

Alternatif terbaik adalah dengan menggunakan RxAndroid(binding spesifik untuk RxJava) untuk Pdi MVPuntuk mengambil alih fo data.

Mulailah dengan kembali Observabledari metode yang ada.

private Observable<PojoObject> getObservableItems() {
    return Observable.create(subscriber -> {

        for (PojoObject pojoObject: pojoObjects) {
            subscriber.onNext(pojoObject);
        }
        subscriber.onCompleted();
    });
}

Gunakan ini Teramati seperti ini -

getObservableItems().
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<PojoObject> () {
    @Override
    public void onCompleted() {
        // Print Toast on completion
    }

    @Override
    public void onError(Throwable e) {}

    @Override
    public void onNext(PojoObject pojoObject) {
        // Show Progress
    }
});
}

-------------------------------------------------- -------------------------------------------------- ------------------------------

Saya tahu saya sedikit terlambat tetapi ini dia. Android pada dasarnya bekerja pada dua jenis utas yaitu utas UI dan utas latar . Menurut dokumentasi android -

Jangan mengakses toolkit UI Android dari luar utas UI untuk memperbaiki masalah ini, Android menawarkan beberapa cara untuk mengakses utas UI dari utas lainnya. Berikut adalah daftar metode yang dapat membantu:

Activity.runOnUiThread(Runnable)  
View.post(Runnable)  
View.postDelayed(Runnable, long)

Sekarang ada berbagai metode untuk mengatasi masalah ini.

Saya akan menjelaskannya dengan contoh kode:

runOnUiThread

new Thread()
{
    public void run()
    {
        myactivity.this.runOnUiThread(new Runnable()
        {
            public void run()
            {
                //Do your UI operations like dialog opening or Toast here
            }
        });
    }
}.start();

LOOPER

Kelas yang digunakan untuk menjalankan loop pesan untuk utas. Utas secara default tidak memiliki loop pesan yang terkait dengannya; untuk membuatnya, panggil prep () di utas yang menjalankan loop, dan kemudian loop () agar prosesnya pesan sampai loop dihentikan.

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

AsyncTask

AsyncTask memungkinkan Anda melakukan pekerjaan asinkron pada antarmuka pengguna Anda. Ia melakukan operasi pemblokiran pada utas pekerja dan kemudian menerbitkan hasilnya pada utas UI, tanpa mengharuskan Anda untuk menangani utas dan / atau penangan sendiri.

public void onClick(View v) {
    new CustomTask().execute((Void[])null);
}


private class CustomTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... param) {
        //Do some work
        return null;
    }

    protected void onPostExecute(Void param) {
        //Print Toast or open dialog
    }
}

Handler

Handler memungkinkan Anda mengirim dan memproses objek Message dan Runnable yang terkait dengan ThreadQueue utas.

Message msg = new Message();


new Thread()
{
    public void run()
    {
        msg.arg1=1;
        handler.sendMessage(msg);
    }
}.start();



Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        if(msg.arg1==1)
        {
            //Print Toast or open dialog        
        }
        return false;
    }
});
mjosh
sumber
7
Ini adalah persis apa yang saya cari. Terutama contoh pertama denganrunOnUiThread
Navin
5
Terima kasih, 5 tahun pemrograman Android dan saya tidak pernah tahu Viewjuga memiliki metode post(Runnable)dan postDelayed(Runnable, long)! Banyak Penangan yang sia-sia. :)
Fenix ​​Voltres
bagi mereka yang bingung dengan contoh Handler: apa utas yang mengikat "Handler baru (panggilan balik)"? Itu terikat pada utas yang menciptakan penangan.
Helin Wang
1
Mengapa ini alternatif terbaik ?
IgorGanapolsky
Saya menggunakan doInBackground dan saya ingin mendapatkan ArrayList kembali tetapi saya selalu mendapatkan kesalahan: Tidak dapat membuat handler di dalam utas yang belum disebut Looper.prepare (). Lihat ini pertanyaan saya stackoverflow.com/questions/45562615/… tapi saya tidak bisa mendapatkan solusinya dari jawaban ini di sini
WeSt
120

Toast.makeText()seharusnya hanya dipanggil dari utas Utama / UI. Looper.getMainLooper () membantu Anda untuk mencapainya:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
    }
});

Keuntungan dari metode ini adalah Anda dapat menggunakannya tanpa Aktivitas atau Konteks.

Ayaz Alifov
sumber
2
Terima kasih atas jawaban lain yang tidak berfungsi untuk saya. Saya menggunakan catatan gula perpustakaan untuk mengelola kegigihan. Dan di dalamnya saya tidak punya kegiatan. Tetapi ini bekerja dengan sangat baik
cabaji99
91

Coba ini, ketika Anda melihat runtimeException karena Looper tidak siap sebelum penangan.

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

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );
Khulja Sim Sim
sumber
Handler adalah kelas abstrak. ini tidak dapat dikompilasi
Stealth Rabbi
2
@StealthRabbi mengimpor Handler dari namespace yang benar yaituandroid.os.Handler
NightFury
Ini mungkin bukan masalahnya. Seorang looper mungkin tidak ada dari kelas panggilan, titik.
IgorGanapolsky
42

Saya mengalami masalah yang sama, dan inilah cara saya memperbaikinya:

private final class UIHandler extends Handler
{
    public static final int DISPLAY_UI_TOAST = 0;
    public static final int DISPLAY_UI_DIALOG = 1;

    public UIHandler(Looper looper)
    {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg)
    {
        switch(msg.what)
        {
        case UIHandler.DISPLAY_UI_TOAST:
        {
            Context context = getApplicationContext();
            Toast t = Toast.makeText(context, (String)msg.obj, Toast.LENGTH_LONG);
            t.show();
        }
        case UIHandler.DISPLAY_UI_DIALOG:
            //TBD
        default:
            break;
        }
    }
}

protected void handleUIRequest(String message)
{
    Message msg = uiHandler.obtainMessage(UIHandler.DISPLAY_UI_TOAST);
    msg.obj = message;
    uiHandler.sendMessage(msg);
}

Untuk membuat UIHandler, Anda harus melakukan yang berikut:

    HandlerThread uiThread = new HandlerThread("UIHandler");
    uiThread.start();
    uiHandler = new UIHandler((HandlerThread) uiThread.getLooper());

Semoga ini membantu.

ChicoBird
sumber
saya mencoba menggunakan kode Anda tetapi saya kehilangan dan tidak yakin bagaimana menelepon dari onCreate methodatau dari AsyncTask dalam situasi saya, tolong posting seluruh kode hanya untuk mempelajari cara kerjanya?
Nick Kahn
2
Bukankah seharusnya baris terakhir itu dibaca uiHandler = new UIHandler(uiThread.getLooper()); ?
Beer Me
36

Alasan kesalahan:

Utas pekerja dimaksudkan untuk melakukan tugas-tugas latar belakang dan Anda tidak dapat menampilkan apa pun di UI dalam utas pekerja kecuali Anda memanggil metode seperti runOnUiThread . Jika Anda mencoba menampilkan sesuatu di utas UI tanpa memanggil runOnUiThread, akan ada a java.lang.RuntimeException.

Jadi, jika Anda berada di sebuah activity tetapi menelepon Toast.makeText()dari utas pekerja, lakukan ini:

runOnUiThread(new Runnable() 
{
   public void run() 
   {
      Toast toast = Toast.makeText(getApplicationContext(), "Something", Toast.LENGTH_SHORT).show();    
   }
}); 

Kode di atas memastikan bahwa Anda menampilkan pesan Toast di UI threadkarena Anda memanggilnya runOnUiThreadmetode di dalam . Jadi tidak ada lagi java.lang.RuntimeException.

Tuhanku
sumber
23

itulah yang saya lakukan.

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Toast(...);
    }
});

Komponen visual "dikunci" untuk perubahan dari utas luar. Jadi, karena roti panggang menunjukkan hal-hal di layar utama yang dikelola oleh utas utama, Anda perlu menjalankan kode ini pada utas itu. Semoga itu bisa membantu :)

Eiran
sumber
Saya menggunakan metode yang sama. Namun, apakah ini meninggalkan kemungkinan kebocoran, karena kelas dalam anonim dari Runnable akan memiliki referensi implisit untuk Kegiatan?
Peter G. Williams
1
Itu poin yang bagus :) cukup gunakan getApplicationContext () atau sesuatu seperti itu, untuk berada di sisi aman. meskipun saya tidak pernah punya masalah dengan kode yang saya tahu
eiran
22

Saya mendapatkan kesalahan ini sampai saya melakukan hal berikut.

public void somethingHappened(final Context context)
{
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(
        new Runnable()
        {
            @Override
            public void run()
            {
                Toast.makeText(context, "Something happened.", Toast.LENGTH_SHORT).show();
            }
        }
    );
}

Dan menjadikan ini sebagai kelas tunggal:

public enum Toaster {
    INSTANCE;

    private final Handler handler = new Handler(Looper.getMainLooper());

    public void postMessage(final String message) {
        handler.post(
            new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(ApplicationHolder.INSTANCE.getCustomApplication(), message, Toast.LENGTH_SHORT)
                        .show();
                }
            }
        );
    }

}
EpicPandaForce
sumber
Di mana Anda menggunakan pemanggang roti ? Dalam cuplikan pertama Anda, ini tidak digunakan ...
IgorGanapolsky
1
itu adalah kelas kenyamanan yang saya gunakan Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));(lama saya tahu! kami mengurangi ini nanti), meskipun saya belum pernah menggunakan bersulang untuk sementara waktu
EpicPandaForce
Jadi apa yang ApplicationHolder.INSTANCEdievaluasi?
IgorGanapolsky
Variabel statis CustomApplicationset in CustomApplication.onCreate(), mengingat aplikasi selalu ada saat proses ada, konteks ini dapat digunakan secara global
EpicPandaForce
12
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });
Coldfin Lab
sumber
2
Ini bekerja untuk saya dan saya menggunakan lambdarunOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
Black_Zerg
Terima kasih. Itu bekerja untuk saya
Amin
11

Solusi Kotlin yang luar biasa:

runOnUiThread {
    // Add your ui thread code here
}
jungledev
sumber
4
runOnUiThreadadalah bagian dari Activity yaituactivity?.runOnUiThread { ... }
AtomicStrongForce
9

Ini karena Toast.makeText () memanggil dari utas pekerja. Itu harus panggilan dari utas UI utama seperti ini

runOnUiThread(new Runnable() {
      public void run() {
        Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
      }
 });
Biswajit Karmakar
sumber
8

Jawaban oleh ChicoBird bekerja untuk saya. Satu-satunya perubahan yang saya lakukan adalah dalam penciptaan UIHandler di mana saya harus melakukan

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse menolak menerima hal lain. Masuk akal kurasa.

Juga uiHandlerjelas kelas global yang didefinisikan di suatu tempat. Saya masih tidak mengaku mengerti bagaimana Android melakukan ini dan apa yang sedang terjadi tetapi saya senang itu bekerja. Sekarang saya akan melanjutkan untuk mempelajarinya dan melihat apakah saya dapat memahami apa yang dilakukan Android dan mengapa seseorang harus melalui semua simpai dan simpang ini. Terima kasih atas bantuan ChicoBird.

Brian Reinhold
sumber
6

panggilan pertama Looper.prepare()dan kemudian panggilan panggilan Toast.makeText().show()terakhir Looper.loop()seperti:

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()
Hasan A Yousef
sumber
Mengapa jawaban ini diremehkan?
DkPathak
5

Untuk Pengguna Rxjava dan RxAndroid:

public static void shortToast(String msg) {
    Observable.just(msg)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(message -> {
                Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
            });
}
Geng Jiawen
sumber
Kurung itu tidak perlu
Borja
4

Saya mengalami masalah yang sama ketika panggilan balik saya mencoba menampilkan dialog.

Saya memecahkannya dengan metode khusus dalam Kegiatan - pada tingkat anggota instance Kegiatan - yang digunakanrunOnUiThread(..)

public void showAuthProgressDialog() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mAuthProgressDialog = DialogUtil.getVisibleProgressDialog(SignInActivity.this, "Loading ...");
        }
    });
}

public void dismissAuthProgressDialog() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (mAuthProgressDialog == null || ! mAuthProgressDialog.isShowing()) {
                return;
            }
            mAuthProgressDialog.dismiss();
        }
    });
}
Gene Bo
sumber
2
Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

Sekarang handler2 akan menggunakan Thread yang berbeda untuk menangani pesan dari Thread utama.

Tarasantan
sumber
1

Untuk menampilkan dialog atau pemanggang di utas, cara paling ringkas adalah dengan menggunakan objek Aktivitas.

Sebagai contoh:

new Thread(new Runnable() {
    @Override
    public void run() {
        myActivity.runOnUiThread(new Runnable() {
            public void run() {
                myActivity.this.processingWaitDialog = new ProgressDialog(myActivity.this.getContext());
                myActivity.this.processingWaitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                myActivity.this.processingWaitDialog.setMessage("abc");
                myActivity.this.processingWaitDialog.setIndeterminate(true);
                myActivity.this.processingWaitDialog.show();
            }
        });
        expenseClassify.serverPost(
                new AsyncOperationCallback() {
                    public void operationCompleted(Object sender) {
                        myActivity.runOnUiThread(new Runnable() {
                            public void run() {
                                if (myActivity.this.processingWaitDialog != null 
                                        && myActivity.this.processingWaitDialog.isShowing()) {
                                    myActivity.this.processingWaitDialog.dismiss();
                                    myActivity.this.processingWaitDialog = null;
                                }
                            }
                        }); // .runOnUiThread(new Runnable()
...
Liwen Zhao
sumber
0

Bersulang, AlertDialogs harus berjalan di utas UI, Anda dapat menggunakan Asynctask untuk menggunakannya dengan benar dalam pengembangan android. Tapi beberapa kasus kita perlu menyesuaikan waktu menyendiri, jadi kami menggunakan Utas , tetapi di utas kami tidak dapat menggunakan Toast, Alertdialogs seperti yang kami gunakan di AsyncTask. Jadi kita perlu Handler terpisah untuk popup itu.

public void onSigned() {
    Thread thread = new Thread(){
        @Override
        public void run() {
            try{
                sleep(3000);
                Message message = new Message();
                message.what = 2;
                handler.sendMessage(message);
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    };
    thread.start();
}

dalam contoh di atas saya ingin tidur utas saya dalam 3sec dan setelah saya ingin menampilkan pesan Toast, untuk itu dalam handler implement mainthread Anda .

handler = new Handler() {
       public void handleMessage(Message msg) {
           switch(msg.what){
              case 1:
              Toast.makeText(getActivity(),"cool",Toast.LENGTH_SHORT).show();
              break;
           }
           super.handleMessage(msg);
       }
};

Saya menggunakan sakelar di sini, karena jika Anda perlu menampilkan pesan yang berbeda dengan cara yang sama, Anda dapat menggunakan sakelar dalam kelas Handler ... harap ini akan membantu Anda

Ashana.Jackol
sumber
0

Ini biasanya terjadi ketika sesuatu di utas utama dipanggil dari utas latar belakang apa pun. Mari kita lihat contoh, misalnya.

private class MyTask extends AsyncTask<Void, Void, Void> {


@Override
protected Void doInBackground(Void... voids) {
        textView.setText("Any Text");
        return null;
    }
}

Dalam contoh di atas, kami mengatur teks pada tampilan teks yang ada di utas utama UI dari metode doInBackground (), yang hanya beroperasi pada utas pekerja.

Prashant Paliwal
sumber
0

Saya memiliki masalah yang sama dan saya memperbaikinya hanya dengan meletakkan Toast di onPostExecute () mengesampingkan fungsi Asynctask <> dan berhasil.

alibabaei12
sumber
-2

saya menggunakan kode berikut untuk menampilkan pesan dari "konteks" bukan utas utama,

@FunctionalInterface
public interface IShowMessage {
    Context getContext();

    default void showMessage(String message) {
        final Thread mThread = new Thread() {
            @Override
            public void run() {
                try {
                    Looper.prepare();
                    Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
                    Looper.loop();
                } catch (Exception error) {
                    error.printStackTrace();
                    Log.e("IShowMessage", error.getMessage());
                }
            }
        };
        mThread.start();
    }
}

kemudian gunakan sebagai berikut:

class myClass implements IShowMessage{

  showMessage("your message!");
 @Override
    public Context getContext() {
        return getApplicationContext();
    }
}
Mohamed.Abdo
sumber