首页 文章

无法在未调用Looper.prepare()的线程内创建处理程序

提问于
浏览
815

以下例外意味着什么;我该怎么解决?

这是代码:

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)

20 回答

  • 773

    首先需要导入给定的行 .

    import android.os.StrictMode;
    

    然后,在Activity的 onCreate() 方法中添加以下行:

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

    我有同样的问题,我只是通过将Toast放在Asynctask <>的onPostExecute()覆盖函数中来修复它并且它有效 .

  • 75

    更新 - 2016年

    最好的替代方法是使用 RxAndroidRxJava 的特定绑定)为 MVP 中的 P 来处理数据 .

    首先从现有方法返回 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 threadbackground thread . 根据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();
    

    LOOPER

    用于为线程运行消息循环的类 . 默认情况下,线程没有与之关联的消息循环;创建一个,在运行循环的线程中调用prepare(),然后循环()让它处理消息,直到循环停止 .

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

    处理程序

    Handler允许您发送和处理与线程的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;
        }
    });
    
  • 19

    我遇到了同样的问题,这就是我修复它的方法:

    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

    您需要从UI线程调用 Toast.makeText(...)

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

    这是从another (duplicate) SO answer复制粘贴的 .

  • -2

    Reason for an error:

    工作线程用于执行后台任务,除非您调用类似 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();    
       }
    });
    

    上面的代码确保您在 UI thread 中显示Toast消息,因为您在 runOnUiThread 方法中调用它 . 所以没有 java.lang.RuntimeException .

  • 32

    我得到了这个错误,直到我做了以下事情 .

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

    这是因为Toast.makeText()是从工作线程调用的 . 应该像这样从主UI线程调用它

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

    Toast, AlertDialogs 需要在UI线程上运行,你可以使用 Asynctask 在android开发中正确使用它们 . 但有些情况我们需要自定义超时,所以我们使用 Threads ,但在线程中我们不能使用Toast,我们在AsyncTask中使用的Alertdialogs那么我们需要单独的 Handler 来弹出那些 .

    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秒内睡眠我的线程,之后我想在你的 mainthread 工具处理程序中显示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);
           }
    };
    

    我在这里使用了switch case,因为如果你需要以相同的方式显示不同的消息,你可以在Handler类中使用switch case ...希望这会对你有所帮助

  • 417

    试试这个,当你看到由于Looper没有在handler之前准备好而导致的runtimeException .

    Handler handler = new Handler(Looper.getMainLooper()); 
    
    handler.postDelayed(new Runnable() {
      @override
      void run() {
      // Run your task here
      }
    }, 1000 );
    
  • 4

    要在线程中显示对话框或烤面包机,最简洁的方法是使用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()
    ...
    
  • 21

    你是从工作线程调用它 . 您需要从主线程中调用 Toast.makeText() (以及处理UI的大多数其他函数) . 例如,您可以使用处理程序 .

    在文档中查找Communicating with the UI Thread . 简而言之:

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

    Other options:

    你可以使用AsyncTask,它适用于在后台运行的大多数事情 . 它有一些钩子,您可以调用它来指示进度,以及何时完成 .

    你也可以使用Activity.runOnUiThread() .

  • 0

    当从任何后台线程调用主线程上的某些内容时,通常会发生这种情况 . 让我们看一个例子 .

    private class MyTask extends AsyncTask<Void, Void, Void> {
    
    
    @Override
    protected Void doInBackground(Void... voids) {
            textView.setText("Any Text");
            return null;
        }
    }
    

    在上面的示例中,我们在textInview上设置文本,该文本视图位于doInBackground()方法的主UI线程中,该方法仅在工作线程上运行 .

  • 577

    这就是我做的 .

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

    可视组件被“锁定”到来自外部线程的更改 . 因此,由于toast在主屏幕上显示由主线程管理的内容,因此您需要在该线程上运行此代码 . 希望有所帮助:)

  • 8

    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)类中使用它 .

  • 1

    当我的回调尝试显示对话框时,我遇到了同样的问题 .

    我用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();
            }
        });
    }
    
  • -2

    对于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();
                });
    }
    
  • 0
    runOnUiThread(new Runnable() {
                public void run() {
                    Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
                }
            });
    
  • 0

    我使用以下代码来显示来自非主线程“context”的消息,

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

    ChicoBird的答案为我工作 . 我所做的唯一改变是创建了我必须做的UIHandler

    HandlerThread uiThread = new HandlerThread("UIHandler");
    

    Eclipse拒绝接受任何其他内容 . 我想是有道理的 .

    此外 uiHandler 显然是某个全局定义的类 . 我仍然没有声称理解Android是如何做到这一点以及发生了什么,但我很高兴它有效 . 现在我将继续研究它,看看我是否能理解Android正在做什么以及为什么必须经历所有这些环节和循环 . 感谢ChicoBird的帮助 .

相关问题