İş parçacığı içinde Looper.prepare () çağırmamış işleyici oluşturulamıyor


981

Aşağıdaki istisna ne anlama gelir; nasıl düzeltebilirim?

Bu kod:

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

Bu bir istisna:

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)

8
Bu kütüphaneyi kontrol edin compile 'com.shamanland:xdroid-toaster:0.0.5', gerekli runOnUiThread()veya Contextdeğişken değil , tüm rutin gitti! sadece Toaster.toast(R.string.my_msg);burada çağırmak örnek: github.com/shamanland/xdroid-toaster-example
Oleksii K.

120
Ne aptalca bir hata mesajı! Bu kadar basit olabilirdi - UI olmayan bir iş parçacığından görünümlere dokunduğunuzda bunu bir UI olmayan iş parçacığından çağıramazsınız.
Dheeraj Bhaskar

11
Farklı koddan aynı özel durum iletisini alanlar için: Özel durum iletisinin anlamı, kodu Looper hazırlamayan bir iş parçacığı aracılığıyla çağırmanızdır. Normalde, UI iş parçacığından eğer aramıyorsanız, ancak (OP'nin durumu) yapmalısınız - normal bir iş parçacığı Looper hazırlamaz, ancak UI iş parçacığı her zaman yapar.
Helin Wang

@OleksiiKropachov bahsettiğiniz kütüphanenin uygulanması bir runOnUiThread () yapmaya çok benzer.
Helin Wang

evet, ama çok kullanışlı bir sarıcı
Oleksii K.

Yanıtlar:


696

Bir işçi dizisinden çağırıyorsunuz. Toast.makeText()Ana iş parçacığından aramanız (ve UI ile ilgili diğer birçok işlevi) aramanız gerekir . Örneğin bir işleyici kullanabilirsiniz.

Belgelerdeki UI İş Parçacığı ile iletişim kurma konusuna bakın . Kısaca:

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

Diğer seçenekler:

Arka planda çalışan çoğu şey için iyi çalışan bir AsyncTask kullanabilirsiniz . İlerlemeyi ve ne zaman bittiğini belirtmek için çağırabileceğiniz kancalar vardır.

Activity.runOnUiThread () öğesini de kullanabilirsiniz .


orijinal sorun ne olacak (AlertDialog ile ilgili değildi)?
Ivan G.

5
Cleggy'nin söylediklerine sadece iki sent ekledim. Kodlanmış bir örnek genellikle kendileri için ciltler konuşabildiğinden, ne demek istediğinizi (ne kadar da olsa) kısaca tanıtmak tercih edilir.
cdata

5
tam bir teknik cevap için bkz. prasanta-paul.blogspot.kr/2013/09/…
tony9099

3
GUI'yi destekleyen neredeyse tüm programlama dillerinde AFAIK, doğrudan GUI ile güncelleme / değiştirme / görüntüleme / etkileşimde bulunursanız, programın ana iş parçacığında yapılmalıdır.
Ahmed

(and most other functions dealing with the UI)Arka plandan kullanılabilen bir UI işlevi örneği android.support.design.widget.Snackbar- UI iş parçacığından çağrılmadığında işlevselliği azaltılır.
Scruffy

853

Toast.makeText(...)UI iş parçacığından aramanız gerekiyor :

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

Bu başka bir (yinelenen) SO yanıtından kopyala yapıştırılır .


13
Mükemmel cevap. Bu beni bir süre karıştırdı. Sadece not etmek gerekirse, aktiviteye ihtiyacım yoktu. önce runOnUiThread.
Cen92

448

GÜNCELLEME - 2016

İyi alternatiftir kullanmaktır RxAndroid(özel bağlamaları RxJavaiçin) Piçinde MVPveri fo almaya gider.

ObservableMevcut yönteminizden dönerek başlayın .

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

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

Bu Gözlemlenebilir'i şu şekilde kullanın -

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

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

Biraz geç kaldığımı biliyorum ama işte gidiyor. Android temel olarak UI iş parçacığı ve arka plan iş parçacığı olmak üzere iki iş parçacığı türünde çalışır . Android belgelerine göre -

Bu sorunu çözmek için Android UI araç setine UI iş parçacığının dışından erişmeyin, Android diğer iş parçacıklarından UI iş parçacığına erişmek için çeşitli yollar sunar. İşte size yardımcı olabilecek yöntemlerin bir listesi:

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

Şimdi bu sorunu çözmek için çeşitli yöntemler var.

Kod örneği ile açıklayacağım:

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

Bir iş parçacığı için bir ileti döngüsü çalıştırmak için kullanılan sınıf. Varsayılan olarak, iş parçacıklarının kendileriyle ilişkili bir mesaj döngüsü yoktur; bir tane oluşturmak için, döngüyü çalıştıracak olan iş parçacığında ready () öğesini çağırın ve sonra döngü durdurulana kadar iletileri işlemesini sağlamak için loop () öğesini çağırın.

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, kullanıcı arayüzünüzde eşzamansız çalışma yapmanızı sağlar. Bir çalışan iş parçacığında engelleme işlemlerini gerçekleştirir ve daha sonra iş parçacıklarını ve / veya işleyicileri kendiniz kullanmanıza gerek kalmadan sonuçları UI iş parçacığında yayınlar.

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

Bir işleyici, bir iş parçacığının MessageQueue ile ilişkili Message ve Runnable nesneleri göndermenizi ve işlemenizi sağlar.

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

7
Bu tam olarak ben aradığımı. Özellikle ilk örnekrunOnUiThread
Navin

5
Teşekkür ederiz, Android programlama 5 yıl ve ben bilmiyordum Viewayrıca yöntemleri vardır post(Runnable)ve postDelayed(Runnable, long)! Boşuna çok fazla işleyici. :)
Fenix ​​Voltres

İşleyici örneği tarafından karıştırılmamış olanlar için: "yeni İşleyici (geri arama)" nın bağlandığı iş parçacığı nedir? İşleyiciyi oluşturan iş parçacığına bağlıdır.
Helin Wang

1
Neden bu en iyi alternatif ?
IgorGanapolsky

Ben usw doInBackground ve bir ArrayList geri almak istiyorum ama her zaman hata alıyorum: Looper.prepare () çağırmamış iplik içinde işleyici oluşturulamıyor. Bu benim sorum. Stackoverflow.com/questions/45562615/… ama burada bu cevaptan çözüm alamıyorum
WeSt

120

Toast.makeText()yalnızca Ana / UI iş parçacığından çağrılmalıdır. Looper.getMainLooper () bunu başarmanıza yardımcı olur:

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

Bu yöntemin bir avantajı, Etkinlik veya Bağlam olmadan kullanabilmenizdir.


2
Benim için çalışan diğer cevaplar teşekkürler. Kalıcılığı yönetmek için kütüphane şeker kaydı kullanıyorum. Ve içinde aktivite yok. Ama bu harika çalışıyor
cabaji99

91

İşleyiciden önce hazırlanmamış Looper nedeniyle runtimeException'ı gördüğünüzde bunu deneyin.

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

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );

İşleyici soyut bir sınıftır. bu derlenmez
Stealth Haham

2
@StealthRabbi import Handler doğru isim alanından ieandroid.os.Handler
NightFury

Sorun bu olmayabilir. Çağıran sınıftan bir ilmek yapıcı olmayabilir.
IgorGanapolsky

42

Aynı problemle karşılaştım ve işte böyle düzelttim:

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

UIHandler oluşturmak için aşağıdakileri yapmanız gerekir:

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

Bu yardımcı olur umarım.


Kodunuzu kullanmaya çalıştım ama kaybettim ve onCreate methodAsyncTask gelen ya da benim durumumdan nasıl arayacağınızdan emin değilim sadece işler nasıl çalıştığını öğrenmek için tüm kodu gönderir misiniz?
Nick Kahn

2
Bu son satır okunmamalı uiHandler = new UIHandler(uiThread.getLooper()); mı?
Beer Me

36

Bir hatanın nedeni:

Alt iş parçacıkları arka plan görevleri yapmak içindir ve runOnUiThread gibi bir yöntem çağırmadıkça alt iş parçacığı içindeki kullanıcı arayüzünde hiçbir şey gösteremezsiniz . UO iş parçacığında runOnUiThread çağırmadan bir şey göstermeye çalışırsanız, bir java.lang.RuntimeException.

Yani, bir işçi iş parçacığından gelen activityama çağırıyorsanız Toast.makeText(), bunu yapın:

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

Yukarıdaki kod Tost mesajını bir yöntem UI threadiçinde çağırdığınızdan beri göstermenizi sağlar runOnUiThread. Artık yok java.lang.RuntimeException.


23

Ben de öyle yaptım.

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

Görsel bileşenler dış iş parçacıklarındaki değişikliklere "kilitlenir". Bu nedenle, tost ana ekranda ana iş parçacığı tarafından yönetilen şeyleri gösterdiğinden, bu kodu o iş parçacığında çalıştırmanız gerekir. Umarım yardımcı olur:)


Ben de aynı yöntemi kullandım. Bununla birlikte, bu izinler Runnable'ın anonim iç sınıfı Faaliyete örtük bir başvuruda bulunacağı için sızıntı olasılığını açar mı?
Peter

1
Bu iyi bir nokta :) sadece güvenli tarafta olmak için getApplicationContext () veya bunun gibi bir şey kullanın.
altough

22

Aşağıdakileri yapana kadar bu hatayı alıyordum.

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

Ve bunu tek bir sınıf haline getirdi:

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

}

Toaster'ı nerede kullanıyorsunuz ? İlk
kod

1
ben gibi kullanılan bir kolaylık sınıf Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));bir süre içinde tost kullanılarak getirilememiş olmasına rağmen, (! uzun ben biliyorum bunu daha sonra azaltılmış)
EpicPandaForce

Peki neyi ApplicationHolder.INSTANCEdeğerlendiriyor?
IgorGanapolsky

Bir statik değişken CustomApplicationolarak set CustomApplication.onCreate()işlemi mevcut ise, her zaman uygulamayı dikkate alınarak, bu bağlamda genel olarak kullanılabilen, var
EpicPandaForce

12
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });

2
Bu benim için çalıştı ve lambda kullanıyorumrunOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
Black_Zerg

Teşekkürler. Benim için çalıştı
Amin

11

Harika Kotlin çözümü:

runOnUiThread {
    // Add your ui thread code here
}

4
runOnUiThreadAktivitenin bir parçasıdır yaniactivity?.runOnUiThread { ... }
AtomicStrongForce

9

Bunun nedeni Toast.makeText () öğesinin bir iş parçacığından çağırmasıdır. Bunun gibi ana UI iş parçacığından çağrı olmalıdır

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

8

ChicoBird'in yanıtı benim için çalıştı. Yaptığım tek değişiklik, yapmak zorunda olduğum UIHandler'ın oluşturulmasıydı

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse başka bir şey kabul etmeyi reddetti. Sanırım mantıklı.

Ayrıca uiHandleraçıkça bir yerde tanımlanmış bir sınıf küreseldir. Hala Android'in bunu nasıl yaptığını ve neler olduğunu anladığımı iddia etmiyorum ama işe yaradığına sevindim. Şimdi incelemeye devam edeceğim ve Android'in ne yaptığını ve neden tüm bu çemberlerden ve döngülerden geçmesi gerektiğini anlayabileceğim. ChicoBird yardım için teşekkürler.


6

önce ara Looper.prepare(), sonra Toast.makeText().show()son aramayı şu şekilde ara Looper.loop():

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()

Bu cevabın neden yetersiz olduğu?
DkPathak

5

Rxjava ve RxAndroid Kullanıcısı için:

public static void shortToast(String msg) {
    Observable.just(msg)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(message -> {
                Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
            });
}

Bu parantezler gereksiz
Borja

4

Geri çağrılarım bir iletişim kutusu göstermeye çalıştığında da aynı sorunu yaşıyordum.

Etkinlik at - Ben Etkinliğinizde adanmış yöntemlerle bunu çözmüş örneği üyesi düzeyinde - kullanımınınrunOnUiThread(..)

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

2
Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

Şimdi handler2, mesajları işlemek için ana iş parçacığından farklı bir iş parçacığı kullanacaktır.


1

Bir iş parçacığında bir iletişim kutusu veya ekmek kızartma makinesi görüntülemek için en özlü yol Activity nesnesini kullanmaktır.

Örneğin:

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

0

Tost, AlertDialogs UI iş parçacığı üzerinde çalıştırmak için ihtiyaçları kullanabilirsiniz Asynctask kullandığımız bu yüzden biz zaman çıkışları özelleştirmek için gereken bazı durumlarda development.but android düzgün onları kullanmak için konu ama Konuda biz kullanmak gibi Tost, Alertdialogs kullanamazsınız Açılır pencereler için ayrı bir Handler'a ihtiyacımız var .

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

Üstü örnekte ben 3sec benim iplik uyumak istiyorum ve ben bunun için bir tost mesajı göstermek istiyorum sonra içine mainthread işleyicisi uygulamak.

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

Burada anahtar kasasını kullandım, çünkü aynı şekilde farklı mesaj göstermeniz gerekiyorsa, Handler sınıfı içinde anahtar kasasını kullanabilirsiniz ... umarım bu size yardımcı olacaktır


0

Bu genellikle herhangi bir arka plan iş parçacığından ana iş parçacığında bir şey çağrıldığında olur. Örneğin bir örneğe bakalım.

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


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

Yukarıdaki örnekte, yalnızca çalışan bir iş parçacığında çalışan doInBackground () yönteminden ana kullanıcı arabirimindeki metin görünümünde metin ayarlıyoruz.


0

Ben aynı sorunu vardı ve ben sadece Toyn Asynctask <> onPostExecute () geçersiz kılma işlevini koyarak düzeltildi ve çalıştı.


-2

ana olmayan iş parçacığı "bağlam" dan mesaj göstermek için aşağıdaki kodu kullanın,

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

sonra aşağıdaki gibi kullanın:

class myClass implements IShowMessage{

  showMessage("your message!");
 @Override
    public Context getContext() {
        return getApplicationContext();
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.