Looper.prepare()を呼び出していないスレッド内にハンドラーを作成できません


981

次の例外の意味は何ですか。どうすれば修正できますか?

これはコードです:

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

これは例外です:

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
このライブラリを確認してください。compile 'com.shamanland:xdroid-toaster:0.0.5'必要runOnUiThread()Context変数もありません。すべてのルーチンがなくなっています。Toaster.toast(R.string.my_msg);ここで呼び出すだけです。例は次のとおり
shamanland

120
なんてバカなエラーメッセージだ!非UIスレッドからビューに触れたときのように、非UIスレッドからこれを呼び出すことはできません。
Dheeraj Bhaskar 2014

11
異なるコードから同じ例外メッセージを受け取る人の場合:例外メッセージの意味は、ルーパーを準備していないスレッドを介してコードを呼び出していることです。通常、これはUIスレッドから呼び出しているのではなく、そうする必要があることを意味します(OPの場合)-通常のスレッドはLooperを準備しませんが、UIスレッドは常に行います。
Helin Wang

@OleksiiKropachovあなたが言及したライブラリの実装は、runOnUiThread()を実行することと非常に似ています。
Helin Wang

はい、しかしそれは非常に便利なラッパーです
Oleksii K.

回答:


696

ワーカースレッドから呼び出しています。Toast.makeText()メインスレッド内から(およびUIを処理する他のほとんどの関数)を呼び出す必要があります。たとえば、ハンドラーを使用できます。

ドキュメントで「UIスレッドとの通信」を参照してください。手短に:

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

その他のオプション:

AsyncTaskを使用できます。これは、バックグラウンドで実行されているほとんどの処理に適しています。これには、進行状況と完了を示すために呼び出すことができるフックがあります。

Activity.runOnUiThread()を使用することもできます。


元の問題はどうですか(AlertDialogに関するものではありませんでした)。
Ivan G.

5
クレギーが言ったことに私の2セントを加えるだけです。コード化された例は多くの場合それ自体でボリュームを話すことができるため、あなたが何を意味するのかについての簡単なデモンストレーションを提供することが望ましいです。
cdata 2013年

5
完全な技術的回答については、このprasanta-paul.blogspot.kr/2013/09/…を
tony9099

3
GUIをサポートするほとんどすべてのプログラミング言語AFAIKでは、GUIを直接更新/変更/表示/操作する場合、プログラムのメインスレッドで実行する必要があります。
アーメド

(and most other functions dealing with the UI)バックグラウンドから使用できるUI関数の例はandroid.support.design.widget.Snackbar、UIスレッドから呼び出さない場合でも機能が低下しないことです。
2016

854

Toast.makeText(...)UIスレッドから呼び出す必要があります。

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

これは、別の(重複した)SO回答からコピーして貼り付けられます


13
すばらしい答えです。これは私をしばらく混乱させました。ただ注意して、私は活動を必要としませんでした。runOnUiThreadの前。
Cen92 2014年

448

更新-2016

最良の選択肢は、使用することですRxAndroid(のための具体的なバインディングをRxJava用)PMVPデータFO取る担当。

Observable既存のメソッドから戻ることから始めます。

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

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

このObservableを次のように使用します-

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

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

私は少し遅れていることを知っていますが、ここに行きます。Androidは基本的に、UIスレッドバックグラウンドスレッドの 2つのスレッドタイプで動作します。Androidのドキュメントによると-

この問題を修正するために、UIスレッドの外部からAndroid UIツールキットにアクセスしないでください。Androidには、他のスレッドからUIスレッドにアクセスする方法がいくつかあります。役立つメソッドのリストを次に示します。

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

現在、この問題を解決するさまざまな方法があります。

コードサンプルで説明します。

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

ルーパー

スレッドのメッセージループを実行するために使用されるクラス。デフォルトでは、スレッドにはメッセージループが関連付けられていません。メッセージを作成するには、ループを実行するスレッドでprepare()を呼び出し、次にloop()を呼び出して、ループが停止するまでメッセージを処理させます。

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を使用すると、ユーザーインターフェイスで非同期作業を実行できます。ワーカースレッドでブロッキング操作を実行してから、結果をUIスレッドに公開します。スレッドやハンドラーを自分で処理する必要はありません。

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

ハンドラ

ハンドラーを使用すると、スレッドのMessageQueueに関連付けられたMessageオブジェクトとRunnableオブジェクトを送信して処理できます。

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
これはまさに私が探していたものです。特に最初の例runOnUiThread
Navinが2013年

5
Androidのプログラミングのおかげで、5年後、私は知らなかったViewものメソッドを持ってpost(Runnable)してpostDelayed(Runnable, long)!非常に多くのハンドラが無駄です。:)
Fenix Voltres 2015

ハンドラーの例で混乱している人のために:「新しいハンドラー(コールバック)」がバインドされているスレッドは何ですか?ハンドラを作成したスレッドにバインドされています。
Helin Wang

1
なぜこれが最善の選択肢なのですか?
IgorGanapolsky 2017

doInBackgroundを使用してArrayListを取得したいのですが、常にエラーが発生します:Looper.prepare()を呼び出していないスレッド内にハンドラーを作成できません。これは私の質問であるstackoverflow.com/questions/45562615/…を参照してください。しかし、ここでこの回答から解決策を得ることができません
WeSt

120

Toast.makeText()Main / UIスレッドからのみ呼び出す必要があります。Looper.getMainLooper()はそれを達成するのに役立ちます:

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

この方法の利点は、アクティビティやコンテキストなしで使用できることです。


2
他の回答に感謝しました。永続性を管理するために、ライブラリシュガーレコードを使用しています。その中で私は活動を持っていません。しかし、これは素晴らしい働きをします
cabaji99

91

Looperがハンドラーの前に準備されていないため、runtimeExceptionが表示される場合は、これを試してください。

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

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

ハンドラーは抽象クラスです。これはコンパイルされません
Stealth Rabbi 2014

2
@StealthRabbiは、正しい名前空間からハンドラーをインポートします(例android.os.Handler
NightFury、

これは問題ではないかもしれません。呼び出し元クラスであるピリオドからルーパーが存在しない可能性があります。
IgorGanapolsky 2017

42

私は同じ問題に遭遇しました、そしてここに私がそれを修正した方法があります:

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を作成するには、以下を実行する必要があります。

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

お役に立てれば。


私はあなたのコードを使おうとしましたが、私は失わonCreate methodれ、私の状況でAsyncTask から、またはAsyncTaskから呼び出す方法がわかりません。
Nick Kahn

2
その最後の行を読むべきではありません uiHandler = new UIHandler(uiThread.getLooper()); か?
Beer Me

36

エラーの理由:

ワーカースレッドはバックグラウンドタスクを実行するためのものであり、runOnUiThreadなどのメソッドを呼び出さない限り、ワーカースレッド内のUIには何も表示できません。runOnUiThreadを呼び出さずにUIスレッドに何かを表示しようとすると、がありますjava.lang.RuntimeException

したがって、ワーカースレッドからactivity呼び出している場合は、次のようにしますToast.makeText()

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

上記のコードにより、ToastメッセージがメソッドUI thread内で呼び出されているため、Toastメッセージが確実に表示されますrunOnUiThread。だからもうjava.lang.RuntimeException


23

それは私がやったことです。

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

ビジュアルコンポーネントは、外部スレッドからの変更に「ロック」されます。したがって、トーストはメインスレッドによって管理されているものをメイン画面に表示するため、このスレッドでこのコードを実行する必要があります。それが役に立てば幸い:)


私はこれと同じ方法を使用しました。ただし、Runnableの匿名の内部クラスがアクティビティへの暗黙的な参照を保持するため、これはリークの可能性を開いたままにしますか?
Peter G. Williams

1
それはすばらしい点です:)安全のために、getApplicationContext()またはそのようなものを使用してください。私が知っているそのコードで問題があったことは一度もありませんが
eiran

22

次の操作を行うまで、このエラーが発生していました。

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

そしてこれをシングルトンクラスにした:

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

}

どこでトースターを使用していますか?最初のスニペットでは使用されていません...
IgorGanapolsky 2017

1
それは私が同じように使用便利なクラスだったToaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));私はしばらく乾杯を使用していないが、(!長い私の知っているが、我々は、この後の減少)
EpicPandaForce

では、何をApplicationHolder.INSTANCE評価するのでしょうか?
IgorGanapolsky 2017

CustomApplicationset の静的変数はCustomApplication.onCreate()、プロセスが存在する間は常にアプリケーションが存在することを考慮して、このコンテキストはグローバルに使用できます
EpicPandaForce

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

2
これは私にとってはうまくいき、ラムダを使用しますrunOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
Black_Zerg

ありがとう。それは私のために働いた
アミン

11

素晴らしいKotlinソリューション:

runOnUiThread {
    // Add your ui thread code here
}

4
runOnUiThreadこれはアクティビティの一部ですactivity?.runOnUiThread { ... }
AtomicStrongForce

9

これは、Toast.makeText()がワーカースレッドから呼び出されているためです。このようにメインUIスレッドから呼び出す必要があります

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

8

ChicoBirdの答えがうまくいきました。私が行った唯一の変更は、UIHandlerの作成でした。

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipseは他のものを受け入れることを拒否しました。私が思うに意味があります。

また、uiHandlerどこかで明確に定義されたクラスグローバルです。私はまだAndroidがこれをどのように行っているのか、何が起こっているのかを理解しているとは主張していませんが、それが機能してうれしいです。次に、それを調べて、Androidが何をしているか、そしてこれらすべてのフープとループを通過する必要がある理由を理解できるかどうかを確認します。助けてくれてありがとうChicoBird。


6

最初の呼び出しLooper.prepare()、次にToast.makeText().show()最後の呼び出しのLooper.loop()ように:

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

なぜこの答えは過小評価されているのですか?
DkPathak

5

RxjavaおよびRxAndroidユーザーの場合:

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

これらのブラケットは不要です
ボルハ

4

コールバックがダイアログを表示しようとすると、同じ問題が発生していました。

私はそれをアクティビティの専用メソッドで解決しました-アクティビティインスタンスのメンバーレベルで -その使用runOnUiThread(..)

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

ここで、handler2はメインスレッドとは異なるスレッドを使用してメッセージを処理します。


1

スレッドでダイアログまたはトースターを表示するには、最も簡潔な方法は、Activityオブジェクトを使用することです。

例えば:

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

トースト、AlertDialogsはUIスレッドで実行する必要があります。Android開発でAsynctaskを使用して適切に使用できます。ただし、タイムアウトをカスタマイズする必要がある場合があるため、Threadsを使用しますが、スレッドでは、Toast、Alertdialogsを使用できません。 AsyncTaskでは、これらを ポップアップするための別のハンドラーが必要です。

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

上記の例では、メインスレッドの実装ハンドラーでそのために3 秒でスレッドをスリープ状態にした後、Toastメッセージを表示したいと考えています。

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

ここではスイッチケースを使用しました。別のメッセージを同じように表示する必要がある場合は、ハンドラークラス内でスイッチケースを使用できます。これが役立つことを願っています。


0

これは通常、メインスレッドの何かがバックグラウンドスレッドから呼び出されたときに発生します。たとえば、例を見てみましょう。

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


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

上記の例では、メインのUIスレッドにあるテキストビューにテキストを設定しています。これは、ワーカースレッドでのみ動作するdoInBackground()メソッドから行われます。


0

私は同じ問題を抱えていて、Asynctask <>のonPostExecute()オーバーライド関数にトーストを置くだけでそれを修正し、それは機能しました。


-2

次のコードを使用して、メインスレッド以外の「コンテキスト」からのメッセージを表示します。

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

次に、次のように使用します。

class myClass implements IShowMessage{

  showMessage("your message!");
 @Override
    public Context getContext() {
        return getApplicationContext();
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.