web-dev-qa-db-ja.com

Looper.prepare()を呼び出さなかったハンドラーをスレッド内に作成できません

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

これはコードです:

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)
830
michael

あなたはそれをワーカースレッドから呼び出しています。メインスレッド内から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() を使用することもできます。

596
EboMike

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

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

これは another(duplicate)SO answer からコピー&ペーストされます。

786
Jacob Marble

更新 - 2016

最善の方法は、データを管理するためにRxAndroidRxJavaPMVPに対する特定のバインディング)を使用することです。

既存のメソッドから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は基本的に2つのスレッドタイプ、つまり UIスレッド バックグラウンドスレッド で動作します。 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;
    }
});
419
mjosh

LooperによるruntimeExceptionがハンドラの前に準備されていない場合にこれを試してください。

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

handler.postDelayed(new Runnable() {
  @override
  void run() {
  // Run your task here
  }
}, 1000 );
76
Khulja Sim Sim

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

このメソッドの利点は、Activity以外の(またはContextなしの)クラスでも使用できることです。

57
Sa Qada

私は同じ問題に出くわしました、そして、私はそれをどのように修正したかです:

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

お役に立てれば。

39
ChicoBird

エラーの理由:

ワーカースレッドはバックグラウンドタスクを実行するためのものであり、 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メッセージをrunOnUiThreadメソッド内で呼び出しているので、UI threadでToastメッセージを表示していることを確認します。だから、もうJava.lang.RuntimeException

32
My God

次のようになるまで、私はこのエラーを受けていました。

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

}
21
EpicPandaForce

それが私がしたことです。

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

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

19
eiran

これは、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をありがとう。

7
Brian Reinhold
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });
5
Coldfin Lab

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
Geng Jiawen

私のコールバックがダイアログを表示しようとしたとき私は同じ問題に遭遇していました。 

私はActivityの専用メソッドを使ってそれを解決しました - Activity インスタンスメンバーレベルで - 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();
        }
    });
}
4
Gene Bo

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

runOnUiThread {
    // Add your ui thread code here
}
1
jungledev

ダイアログやトースターをスレッドで表示するには、最も簡潔な方法は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()
...
1
Liwen Zhao

Toast、AlertDialogs はUIスレッドで実行する必要があります。 Asynctask を使用してAndroid開発で正しく使用することができます。ただし、タイムアウトをカスタマイズする必要がある場合は Threads 、しかし、スレッドではToastを使用できません。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メッセージを見せたいのですが、そのために mainthread 実装ハンドラで。

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

あなたが同じように異なるメッセージを表示する必要がある場合は、Handlerクラス内でスイッチケースを使用することができます。

0
Ashana.Jackol

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

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


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

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

0
Handler handler2;  
Thread second_thread=new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler2=new Handler();
            }
        });
        second_thread.start();

これでhandler2はおそらくメインスレッドとは異なるスレッドを使ってメッセージを処理します。

0
Tarasantan

最初に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()
0
Hasan A Yousef

私は同じ問題を抱えていて、ToastをAsynctask <>のonPostExecute()オーバーライド関数に入れることによってそれを修正しました、そしてそれは働きました。 

0
alibabaei12