ChatGPT解决这个技术问题 Extra ChatGPT

无法在未调用 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)
检查这个库 compile 'com.shamanland:xdroid-toaster:0.0.5',它不需要 runOnUiThread()Context 变量,所有例程都消失了!只需调用 Toaster.toast(R.string.my_msg); 这是示例:github.com/shamanland/xdroid-toaster-example
多么愚蠢的错误信息!它可能很简单 - 不能像从非 UI 线程触摸视图时那样从非 UI 线程调用它。
对于那些从不同代码中得到相同异常消息的人:异常消息的意思是您正在通过没有准备 Looper 的线程调用代码。通常这意味着您没有从 UI 线程调用 if 但您应该(OP 的情况) - 普通线程不会准备 Looper,但 UI 线程总是这样做。
@OleksiiKropachov 您提到的库的实现与执行 runOnUiThread() 非常相似。
是的,但它是一个非常有用的包装器

C
Community

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

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

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


很好的答案。这让我困惑了一段时间。请注意,我不需要该活动。在 runOnUiThread 之前。
@Cen92 实际上你需要>_<。 runOnUiThread 是一种活动方法。
这是一个很好的解决方案,它节省了我的时间!
它在没有“活动”的情况下工作,并且在我使用过 MainActivity.this 的祝酒词中 >_< 是什么?
E
EboMike

您从工作线程中调用它。您需要从主线程中调用 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();
}

其他选项:

您可以使用 Activity.runOnUiThread()。如果您有 Activity,则直截了当:

@WorkerThread
void workerThread() {
    myActivity.runOnUiThread(() -> {
        // This is where your UI code goes.
    }
}

您也可以发布到主循环器。如果您只有一个 Context,这将非常有用。

@WorkerThread
void workerThread() {
    ContextCompat.getMainExecutor(context).execute(()  -> {
        // This is where your UI code goes.
    }
}

已弃用:

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

这很方便,但如果使用不当,可能会泄漏上下文。它已被正式弃用,您不应再使用它。


原来的问题呢(不是关于 AlertDialog 的)?
只需将我的两分钱加到 Cleggy 所说的内容中即可。最好提供一个简短的演示来说明您的意思(无论多么做作),因为编码示例通常可以说明一切。
如需完整的技术答案,请参阅此prasanta-paul.blogspot.kr/2013/09/…
在几乎所有支持 GUI 的编程语言 AFAIK 中,如果您直接更新/更改/显示/与 GUI 交互,则应该在程序的主线程上完成。
(and most other functions dealing with the UI) 可从后台使用的 UI 函数的一个示例是 android.support.design.widget.Snackbar - 当不从 UI 线程调用时,它的功能不会减弱。
m
mihirjoshi

更新 - 2016

最好的替代方法是为 MVP 中的 P 使用 RxAndroidRxJava 的特定绑定)来负责数据。

首先从现有方法返回 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 线程和后台线程。根据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 允许您在用户界面上执行异步工作。它在工作线程中执行阻塞操作,然后在 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;
    }
});

这正是我想要的。尤其是第一个带有 runOnUiThread 的示例
谢谢,5 年的 Android 编程,我从来不知道 View 也有方法 post(Runnable)postDelayed(Runnable, long)!这么多的处理程序是徒劳的。 :)
对于那些对 Handler 示例感到困惑的人:“new Handler(callback)”绑定到的线程是什么?它绑定到创建处理程序的线程。
为什么这是最好的选择?
我使用 doInBackground,我想取回一个 ArrayList,但我总是收到错误:无法在未调用 Looper.prepare() 的线程内创建处理程序。看到这是我的问题stackoverflow.com/questions/45562615/…,但我无法从这里的答案中得到解决方案
A
Ayaz Alifov

Toast.makeText() 只能从 Main/UI 线程调用。 Looper.getMainLooper() 帮助您实现它:

JAVA

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // write your code here
    }
});

科特林

Handler(Looper.getMainLooper()).post {
        // write your code here
}

这种方法的一个优点是您可以在没有 Activity 或 Context 的情况下运行 UI 代码。


感谢其他答案对我不起作用。我正在使用库糖记录来管理持久性。在里面我没有活动。但这非常有效
@dcarl661,感谢您的关注。修正完成
@AyazAlifov您说“它不需要上下文”,那么 mContext 指的是什么?
嗨@akhilnair。无需上下文即可通过 Main/UI 线程运行 android 代码。 Main/UI 线程可以包含任何代码。在这个具体的例子中,有一个 Toast 方法,它的实现需要 Context。
P
Pratik Butani

试试这个,当你看到 runtimeException 由于 Looper 在处理程序之前没有准备好。

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

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

Handler 是一个抽象类。这不编译
@StealthRabbi 从正确的命名空间导入处理程序,即 android.os.Handler
这可能不是问题。调用类期间可能不存在循环器。
C
ChicoBird

我遇到了同样的问题,这是我解决它的方法:

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 调用,请您发布整个代码以了解事情的工作原理吗?
最后一行不应该是 uiHandler = new UIHandler(uiThread.getLooper()); 吗?
M
My God

错误原因:

工作线程用于执行后台任务,除非您调用 runOnUiThread 之类的方法,否则您无法在工作线程内的 UI 上显示任何内容。如果您尝试在 UI 线程上显示任何内容而不调用 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 中显示 Toast 消息,因为您在 runOnUiThread 方法中调用它。所以没有更多的java.lang.RuntimeException


e
eiran

这就是我所做的。

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

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


我使用了同样的方法。但是,这是否留下了泄漏的可能性,因为 Runnable 的匿名内部类将持有对 Activity 的隐式引用?
这是一个很好的点:) 只需使用 getApplicationContext() 或类似的东西,以确保安全。尽管我知道的代码从来没有遇到过任何问题
E
EpicPandaForce

在我执行以下操作之前,我收到了这个错误。

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

}

你在哪里使用烤面包机?在您的第一个片段中,它没有被使用......
这是我像 Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah)); 一样使用的便利类(我知道很长!我们稍后减少了它),虽然我有一段时间没有使用 toasts
那么 ApplicationHolder.INSTANCE 的评估结果是什么?
CustomApplication.onCreate()中设置的CustomApplication的静态变量,考虑到应用程序总是存在而进程存在,这个上下文可以全局使用
C
Coldfin Lab
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });

这对我有用,我使用 lambda runOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
j
jungledev

精彩的 Kotlin 解决方案:

runOnUiThread {
    // Add your ui thread code here
}

runOnUiThread 是 Activity 的一部分,即 activity?.runOnUiThread { ... }
H
Hasan A Yousef

先调用 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()

为什么这个答案被低估了?
我不知道当前的使用情况,但这在使用包含内部活动的可运行文件中的 jar 文件时救了我。
B
Biswajit Karmakar

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

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

E
Emre Yazici

ChicoBird 的回答对我有用。我所做的唯一更改是创建 UIHandler 我必须做的

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse 拒绝接受其他任何东西。我想是有道理的。

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


G
Geng Jiawen

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

那些括号是不必要的
A
Abdelrahman Tareq

协程会做的很完美

CoroutineScope(Job() + Dispatchers.Main).launch {
                        Toast.makeText(context, "yourmessage",Toast.LENGTH_LONG).show()}

G
Gene Bo

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

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

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

现在 handler2 将使用与主线程不同的线程来处理消息。


J
Joshua Pinter

爪哇 8

new Handler(Looper.getMainLooper()).post(() -> {
    // Work in the UI thread

}); 

科特林

Handler(Looper.getMainLooper()).post{
    // Work in the UI thread
}

总帐


L
Liwen Zhao

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

F
Faisal Shaikh

使用 lambda:

activity.runOnUiThread(() -> Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show());

G
GoRoS

Toast,AlertDialogs 需要运行在 UI 线程上,可以使用 Asynctask 在android开发中正常使用。但是有些情况我们需要自定义超时,所以我们使用 Threads,但是在线程中我们不能使用 Toast,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();
}

在上面的例子中,我想在 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);
       }
};

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


P
Prashant Paliwal

这通常发生在从任何后台线程调用主线程上的某些内容时。例如,让我们看一个例子。

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


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

在上面的示例中,我们通过 doInBackground() 方法在主 UI 线程中的 textview 上设置文本,该方法仅在工作线程上运行。


a
alibabaei12

我遇到了同样的问题,我只是通过将 Toast 放入 Asynctask<> 的 onPostExecute() 覆盖函数来解决它,并且它起作用了。


D
Darshana Chorat

您需要在 UI 线程上创建 toast。找到下面的例子。

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

要显示 Toast 消息,请参阅此article


M
Mehdi Satei

这是 Kotlin 使用 Coroutine 的解决方案:

通过 MainScope() 使用 CoroutineScope 扩展您的类:

class BootstrapActivity :  CoroutineScope by MainScope() {}

然后只需执行以下操作:

launch {
        // whatever you want to do in the main thread
    }

不要忘记为协程添加依赖项:

org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}
org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinCoroutines}

或者只是 launch(Dispatchers.Main) { ... }
M
Mudassir Ahmed

在线程外创建处理程序

final Handler handler = new Handler();

        new Thread(new Runnable() {
            @Override
            public void run() {
            try{
                 handler.post(new Runnable() {
                        @Override
                        public void run() {
                            showAlertDialog(p.getProviderName(), Token, p.getProviderId(), Amount);
                        }
                    });

                }
            }
            catch (Exception e){
                Log.d("ProvidersNullExp", e.getMessage());
            }
        }
    }).start();

S
Santers

最近,我遇到了这个问题 - 发生这种情况是因为我试图调用一个函数来从构造函数中执行一些 UI 工作。从构造函数中删除初始化为我解决了这个问题。


z
zcoop98

我遇到了同样的问题,这段代码现在对我来说很好。例如,这是我在后台和 UI 线程中执行任务的代码。观察looper是如何使用的:

new Thread(new Runnable() {
    @Override
    public void run() {
    Looper.prepare();
                                
    // your Background Task here

    runOnUiThread(new Runnable() {
        @Override
        public void run() {

        // update your UI here      
                                
        Looper.loop();
        }
    });
    }
}).start();

M
Mohamed.Abdo

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

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

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅