Не могу создать обработчик внутри потока, который не называется лупер.подготовить()


что означает следующее исключение; как я могу это исправить?

этот код:

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)
19 792

19 ответов:

вы вызываете его из рабочего потока. Вам нужно позвонить Toast.makeText() (и большинство других функций, связанных с 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, который хорошо работает для большинства вещей, работающих в фоновом режиме. Он имеет крючки, которые вы можете позвонить, чтобы указать прогресс, и когда это будет сделано.

вы также можете использовать активности.runOnUiThread().

нужно позвонить Toast.makeText(...) из потока пользовательского интерфейса:

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

это копировать-вставить из другой (дубликат) так ответ.

обновление - 2016

лучшая альтернатива-использовать RxAndroid (конкретные привязки для RxJava) для P на MVP чтобы взять на себя ответственность за данные fo.

начать с возвращения Observable из вашего существующего метода.

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

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

использовать этот заметный такой -

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 thread и фоновый поток. Согласно документации android -

не получить доступ к Android UI toolkit из-за пределов потока пользовательского интерфейса, чтобы чтобы исправить эту проблему, 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();

LOOPER

класс, используемый для запуска цикла сообщений для потока. Темы по умолчанию не иметь цикл сообщений, связанный с ними; создайте один, позвоните подготовьте() в потоке, который должен выполнить цикл, а затем цикл () к пусть он обрабатывает сообщения до тех пор, пока цикл не будет остановлен.

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 позволяет выполнять асинхронную работу с пользователем взаимодействие. Он выполняет операции блокировки в рабочем потоке и затем публикует результаты в потоке пользовательского интерфейса, не требуя от вас обработка потоков и / или обработчиков себе.

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

проводник

обработчик позволяет отправлять и обрабатывать сообщения и Runnable объекты связанный с MessageQueue потока.

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

попробуйте это, когда вы видите runtimeException из-за Looper не подготовлен перед обработчиком.

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

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

я столкнулся с той же проблемой, и вот как я ее исправил:

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

надеюсь, что это помогает.

Toast.makeText() должен быть вызван из главного потока / UI. Looper.getMainLooper() помогает вам достичь этого:

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

преимущество этого метода заключается в том, что вы можете использовать его в классах без активности (или без контекста).

причина ошибки:

рабочие потоки предназначены для выполнения фоновых задач, и вы не можете ничего показать в пользовательском интерфейсе в рабочем потоке, если вы не вызываете метод, например runOnUiThread. Если вы попытаетесь показать что-либо в потоке пользовательского интерфейса без вызова runOnUiThread, будет java.lang.RuntimeException.

Итак, если вы находитесь в activity но вызов Toast.makeText() из рабочего потока, сделать это:

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

приведенный выше код гарантирует, что вы отображение тост-сообщения в UI thread так как вы называете его внутри runOnUiThread метод. Так что больше не java.lang.RuntimeException.

Я получал эту ошибку, пока не сделал следующее.

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

}

вот что я сделал.

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

визуальные компоненты "заблокированы" для изменений из внешних потоков. Итак, поскольку тост показывает материал на главном экране, который управляется основным потоком, вам нужно запустить этот код в этом потоке. Надеюсь, что это поможет:)

Это потому что гренка.makeText () вызывает из рабочего потока. Это должен быть вызов из основного потока пользовательского интерфейса, как это

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

ответ ChicoBird работал для меня. Единственное изменение, которое я сделал, было в создании UIHandler, где я должен был сделать

HandlerThread uiThread = new HandlerThread("UIHandler");

затмение отказалось принять что-либо еще. Полагаю, это имеет смысл.

и uiHandler явно глобальный класс, определенный где-то. Я все еще не утверждаю, что понимаю, как Android делает это и что происходит, но я рад, что это работает. Теперь я перейду к его изучению и посмотрю, могу ли я понять, что делает Android и почему один приходится проходить через все эти обручи и петли. Спасибо за помощь ChicoBird.

я столкнулся с той же проблемой, когда мои обратные вызовы будут пытаться показать диалог.

Я решил это с помощью специальных методов в Activity-at the 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();
        }
    });
}

для пользователя 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();
            });
}

для отображения диалогового окна или тостера в потоке наиболее сжатым способом является использование объекта 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()
...
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });

Тост, AlertDialogs должен работать в потоке пользовательского интерфейса, вы можете использовать Asynctask чтобы правильно использовать их в разработке android.но в некоторых случаях нам нужно настроить тайм-ауты, поэтому мы используем Threads, но в потоках мы не можем использовать тосты, Alertdialogs, как мы используем в AsyncTask.So нам нужен отдельный проводник для всплывающем те.

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

В приведенном выше примере я хочу спать мой поток в 3sec и после того, как я хочу, чтобы показать тост сообщение,что в 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);
       }
};

я использовал switch case здесь, потому что если вам нужно показать другое сообщение таким же образом, вы можете использовать switch case в классе обработчика...надеюсь, это поможет вам

это обычно происходит, когда что-то в главном потоке вызывается из любого фонового потока. Рассмотрим пример , например.

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


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

в приведенном выше примере мы устанавливаем текст в textview, который находится в основном потоке пользовательского интерфейса из метода doInBackground (), который работает только в рабочем потоке.

сначала нужно импортировать заданную строку.

import android.os.StrictMode;

и затем добавить ниже строки onCreate() способ Вашей деятельности:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

Я использую следующий код, чтобы показать сообщение из не основного потока "контекст",

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