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

83

我有一个Activity,在它里面我有一个类。

text=new Dynamictext(...);
text.setText("txt");

在我的DynamicText Java代码中,我有以下代码:

public void setText(String text) {
    this.text=text;
    new asyncCreateText().execute();
    //this.createText(text);
}

//private Handler handler = new Handler();

private class asyncCreateText extends AsyncTask<Void, Void, Void> 
{
    @Override
    protected Void doInBackground(Void... unused) {
        return null;
    }

    @Override
    protected void onPostExecute(Void unused) {

    }
}

我得到的错误信息如下:

ERROR/AndroidRuntime(5176): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

我该如何处理这个错误?
ERROR/AndroidRuntime(5370): java.lang.ExceptionInInitializerError
ERROR/AndroidRuntime(5370):     at com.l.start.DynamicText.setText(DynamicText.java:125)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.initfonts(OpenGLRenderer.java:168)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.init(OpenGLRenderer.java:119)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.onSurfaceChanged(OpenGLRenderer.java:90)
ERROR/AndroidRuntime(5370):     at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1120)
ERROR/AndroidRuntime(5370):     at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:975)

ERROR/AndroidRuntime(5370): Caused by: java.lang.RuntimeException: 
    Can't create handler inside thread that has not called Looper.prepare()
ERROR/AndroidRuntime(5370):     at android.os.Handler.<init>(Handler.java:121)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
ERROR/AndroidRuntime(5370):     ... 6 more

你能贴出完整的堆栈跟踪吗?似乎你正在从后台线程或其他地方调用一些UI方法。 - alexanderblom
1
这里会详细解释这个问题。 - mihirjoshi
7个回答

147
错误信息是不言自明的... doInBackground() 运行在后台线程上,由于它并不打算循环,因此没有连接到Looper
你很可能根本不想直接实例化Handler ... 无论你的doInBackground()实现返回什么数据,都将传递给在UI线程上运行的onPostExecute()
   mActivity = ThisActivity.this; 

    mActivity.runOnUiThread(new Runnable() {
     public void run() {
     new asyncCreateText().execute();
     }
   });

在问题的堆栈跟踪后添加以下内容:

看起来你正在尝试从GL渲染线程启动 AsyncTask ... 不要这样做,因为它们永远不会调用 Looper.loop()。 AsyncTasks 真正设计的目的是仅从 UI 线程运行。

最小的干扰修复可能是使用一个 Runnable 调用 Activity.runOnUiThread(),然后启动你的 AsyncTask


1
答案完全正确。然而,在我的情况下,我只需要从我的自定义FtpUpload AsyncTask类中删除一些Toast(尝试在UI线程上运行),然后就万事大吉了。 - Josh
我遇到了相同的错误,我很困惑该如何解决它。我已经阅读了描述,但是不知道如何操作。有人可以给我提供代码片段吗? - Android is everything for me
我本来想给这个回答点赞的,但是除了“错误是不言自明的”这句话之外。 - undefined

32

以上所有答案都是正确的,但我认为以下是可能最简单的示例:

public class ExampleActivity extends Activity {
    private Handler handler;
    private ProgressBar progress;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        progress = (ProgressBar) findViewById(R.id.progressBar1);
        handler = new Handler();
    }

    public void clickAButton(View view) {
        // Do something that takes a while
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() { // This thread runs in the UI
                    @Override
                    public void run() {
                        progress.setProgress("anything"); // Update the UI
                    }
                });
            }
        };
        new Thread(runnable).start();
    }
}

这个代码会通过在活动(activity)中声明的handler的post()方法,将来自于完全不同线程的进度条更新到UI线程。

希望对你有所帮助!


23

您可以通过以下方式在后台线程中创建处理程序

private void createHandler() {
        Thread thread = new Thread() {
          public void run() {
               Looper.prepare();

               final Handler handler = new Handler();
               handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                       // Do Work
                        handler.removeCallbacks(this);
                        Looper.myLooper().quit();
                   }
                }, 2000);

                Looper.loop();
            }
        };
        thread.start();
    }

不仅解决了问题,而且解释得非常清楚。只有一个小问题。如果希望在Work完成时移除回调和退出,但没有达到延迟,该结构将如何更改?因为工作可能会持续比预定的延迟时间更长。 - Farid

12

Activity.runOnUiThread() 对我无效。我通过以下方式解决了这个问题:创建一个普通的线程:

public class PullTasksThread extends Thread {
   public void run () {
      Log.d(Prefs.TAG, "Thread run...");
   }
}

并通过此方式从GL update中调用它:

new PullTasksThread().start();

这个解决方案对我来说在从GL中调用时有效,但你必须确定你没有在run方法中调用Toast!从GL调用AsyncTask不起作用。谢谢! - Alberto M.
对我来说非常好用,可以取代 AsyncTask。谢谢! - IgorGanapolsky

5
尝试从UI线程运行您的异步任务。当我没有这样做时,我遇到了这个问题!

4

试试这个

    Handler mHandler = new Handler(Looper.getMainLooper());
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
 //your code here that talks with the UI level widgets/ try to access the UI 
//elements from this block because this piece of snippet will run in the UI/MainThread.                                     
                            }
                        });

1
尝试添加一些解释。仅提供代码答案在SO上并不受欢迎。 - Alexei - check Codidact

0

对于所有的Kotlin爱好者,这里有一些代码供你参考

Handler(Looper.getMainLooper()).post {
      // any UI update here
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接