如何在Android的后台线程中显示Toast?

127

我该如何在线程中显示Toast消息?


这篇回答详细地解答了问题。 - mihirjoshi
此答案提供了最短的解决方案:https://dev59.com/tXXYa4cB1Zd3GeqP-MYG#18280318 - Oleksii K.
14个回答

261

你可以通过调用ActivityrunOnUiThread方法来实现:

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

1
我不确定我理解如何完成这个任务。我有现成的public void run()函数。我试着把代码放到那里面,但是我知道那样做是不对的,因为它没有起作用,但是我真的陷入了困境。 - SwimBikeRun
14
在它的构造函数中,"activity" 是否被传递到了非UI线程?如何正确地从分离的线程中获取正在使用的Activity对象? - snapfractalpop
5
有时无法访问Activity实例,你可以使用简单的辅助类代替,参见这里:https://dev59.com/tXXYa4cB1Zd3GeqP-MYG#18280318。 - Oleksii K.
6
通常情况下,我发现在内部的Thread/AsyncTask中使用MyActivity.this.runOnUiThread()可以正常工作。 - Anthony Atkinson
在较新版本的Android API中似乎没有提供runOnUiThread方法。 - Ryan
显示剩余2条评论

72

我喜欢在我的活动中有一个名为showToast的方法,我可以从任何地方调用它...

public void showToast(final String toast)
{
    runOnUiThread(() -> Toast.makeText(MyActivity.this, toast, Toast.LENGTH_SHORT).show());
}

然后我最频繁地在任何线程中从MyActivity中调用它,像这样...

showToast(getString(R.string.MyMessage));

3
谢谢,我现在正在将这个添加到大多数活动中。 - Gene Myers
1
对于 TOAST,始终使用 Application Context,而不是 Activity Context! - Yousha Aleayoub
1
@YoushaAleayoub 为什么? - OneWorld
1
@OneWorld,证明:1-对于toast消息,Google Dev Guide使用应用程序上下文并明确表示要使用它。2-https://dev59.com/A2855IYBdhLWcg3w0H53#4128799 3-https://dev59.com/EWkv5IYBdhLWcg3wxz2A#10347346 4-https://groups.google.com/d/msg/android-developers/3i8M6-wAIwM/EQOdAZKxsQYJ - Yousha Aleayoub
如果Google建议我们使用ApplicationContext,那么他们应该让它更容易访问,并且在任何情况下都停止返回null。例如:fragment.getApplicationContext()。 - Kimi Chiu
显示剩余2条评论

34

这与其他答案类似,但更新了可用的新API并且更加简洁。另外,不假设您处于Activity上下文中。

public class MyService extends AnyContextSubclass {

    public void postToastMessage(final String message) {
        Handler handler = new Handler(Looper.getMainLooper());

        handler.post(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
            }
        });
    }
}

当你所拥有的上下文不是一个活动时,这就是完美的答案。非常感谢! - francas

26

一种适用于几乎任何地方的方法,包括你没有ActivityView的地方,是获取一个指向主线程的Handler,然后显示toast:

public void toast(final Context context, final String text) {
  Handler handler = new Handler(Looper.getMainLooper());
  handler.post(new Runnable() {
    public void run() {
      Toast.makeText(context, text, Toast.LENGTH_LONG).show();
    }
  });
}

这种方法的优势在于它适用于任何Context,包括ServiceApplication


如果您的应用程序在后台运行,而服务线程调用此代码,当应用程序重新回到前台时,Toast是否会显示?我很好奇当应用程序被置于后台时,主应用程序线程会发生什么。它会暂停并不执行事件循环中的任务吗?然后再恢复吗? - the_prole

10

这个或者这个,使用一个Runnable展示Toast

Activity activity = // reference to an Activity
// or
View view = // reference to a View

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        showToast(activity);
    }
});
// or
view.post(new Runnable() {
    @Override
    public void run() {
        showToast(view.getContext());
    }
});

private void showToast(Context ctx) {
    Toast.makeText(ctx, "Hi!", Toast.LENGTH_SHORT).show();
}

6
有时候,你需要从另一个线程向UI线程发送消息。这种情况发生在你不能在UI线程上执行网络/IO操作的时候。
下面的例子处理了这种情况:
1. 你有UI线程。 2. 你必须开始IO操作,因此无法在UI线程上运行Runnable。所以将你的Runnable发布到HandlerThread上的handler中。 3. 从Runnable中获取结果并将其发送回UI线程,并显示Toast消息。
解决方案:
  1. 创建一个HandlerThread并启动它
  2. 使用LooperHandlerThread创建一个HandlerrequestHandler
  3. 使用主线程的Looper创建一个Handler:responseHandler,并重写handleMessage方法
  4. post一个Runnable任务到requestHandler
  5. Runnable任务中,在responseHandler上调用sendMessage
  6. 这个sendMessage结果会调用responseHandler中的handleMessage
  7. Message中获取属性并处理它,更新UI

示例代码:

    /* Handler thread */

    HandlerThread handlerThread = new HandlerThread("HandlerThread");
    handlerThread.start();
    Handler requestHandler = new Handler(handlerThread.getLooper());

    final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Runnable on HandlerThread is completed and got result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

    for ( int i=0; i<5; i++) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                try {

                    /* Add your business logic here and construct the 
                       Messgae which should be handled in UI thread. For 
                       example sake, just sending a simple Text here*/

                    String text = "" + (++rId);
                    Message msg = new Message();

                    msg.obj = text.toString();
                    responseHandler.sendMessage(msg);
                    System.out.println(text.toString());

                } catch (Exception err) {
                    err.printStackTrace();
                }
            }
        };
        requestHandler.post(myRunnable);
    }

有用的文章:

HandlerThreads和为什么你应该在你的Android应用程序中使用它们

Android Looper、Handler、HandlerThread(上)


5
  1. 获取UI线程的Handler实例并使用handler.sendMessage();
  2. 调用post()方法handler.post();
  3. runOnUiThread()
  4. view.post()

3

您可以使用 Looper 发送 Toast 消息。请访问此链接了解更多详情。

public void showToastInThread(final Context context,final String str){
    Looper.prepare();
    MessageQueue queue = Looper.myQueue();
    queue.addIdleHandler(new IdleHandler() {
         int mReqCount = 0;

         @Override
         public boolean queueIdle() {
             if (++mReqCount == 2) {
                  Looper.myLooper().quit();
                  return false;
             } else
                  return true;
         }
    });
    Toast.makeText(context, str,Toast.LENGTH_LONG).show();      
    Looper.loop();
}

它被称为线程中的上下文。可以从要显示toast的Activity中获取Activity.getContext()


2

我基于mjaggard的回答提出了以下方法:

public static void toastAnywhere(final String text) {
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
        public void run() {
            Toast.makeText(SuperApplication.getInstance().getApplicationContext(), text, 
                    Toast.LENGTH_LONG).show();
        }
    });
}

对我很有帮助。


2

使用runOnUiThread的Kotlin代码

runOnUiThread(
        object : Runnable {
            override fun run() {
                Toast.makeText(applicationContext, "Calling from runOnUiThread()", Toast.LENGTH_SHORT)  
            }
        }
)

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