如何在 Android 上显示来自后台线程的 Toast?

Posted

技术标签:

【中文标题】如何在 Android 上显示来自后台线程的 Toast?【英文标题】:How do you display a Toast from a background thread on Android? 【发布时间】:2011-03-09 06:14:51 【问题描述】:

如何显示来自线程的Toast 消息?

【问题讨论】:

This详细回答问题。 这个答案提供了最短的解决方案:***.com/a/18280318/1891118 【参考方案1】:

您可以通过从您的线程中调用ActivityrunOnUiThread 方法来做到这一点:

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

【讨论】:

我不确定我是否理解如何执行此操作。我有我现有的公共无效运行()。我试着把这段代码放在那里。我知道这是不对的,因为它不起作用,但我真的被卡住了。 “活动”是否在其构造函数中传递给非 UI 线程?从单独的线程中获取您正在使用的活动对象的正确方法是什么? Thread 对象的引用设置为ActivityonResume 中的Activity。在ActivityonPause 中取消设置。在ActivityThread 都尊重的synchronized 锁定下进行。 有时无法访问Activity 实例,您可以使用简单的辅助类代替,请参见此处:***.com/a/18280318/1891118 我通常发现MyActivity.this.runOnUiThread() 在内部Thread/AsyncTask 中工作得很好。【参考方案2】:

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

public void showToast(final String toast)

    runOnUiThread(() -> Toast.makeText(MyActivity.this, toast, Toast.LENGTH_SHORT).show());

然后我最常在MyActivity 这样的任何线程上调用它...

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

【讨论】:

谢谢,我现在正在添加这个大多数活动。 对于 TOAST,始终使用 Application Context,而不是 Activity Context! @YoushaAleayoub 为什么? @OneWorld,证明:1- 对于 toast 消息,Google 开发指南使用应用程序上下文并明确表示要使用它。 2- ***.com/a/4128799/1429432 3- ***.com/a/10347346/1429432 4- groups.google.com/d/msg/android-developers/3i8M6-wAIwM/… @YoushaAleayoub 您提供的链接中有很多讨论和猜测。例如。 RomainGuy 说你的证明没有内存泄漏。 4. 一些链接来自 2009 年早期的 Android。也有人在其他链接中说您可以使用这两种上下文。活动和应用程序。也许你有一个更新的基于真实证据的证据?有 1 的链接吗?【参考方案3】:

这与其他答案类似,但已针对新的可用 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();
            
        );
    

【讨论】:

当您所拥有的上下文不是完美答案的活动时。非常感谢!【参考方案4】:

一种适用于几乎任何地方的方法,包括在没有ActivityView 的地方,将Handler 抓取到主线程并显示吐司:

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 会显示吗?我很好奇当应用程序后台运行时主应用程序线程会发生什么。它会暂停,而不是在事件循环中执行任务吗?然后继续?【参考方案5】:

如this 或this,带有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】:

有时,您必须将消息从另一个 Thread 发送到 UI 线程。当您无法在 UI 线程上执行网络/IO 操作时,就会出现这种情况。

以下示例处理该场景。

    你有 UI 线程 您必须启动 IO 操作,因此您不能在 UI 线程上运行 Runnable。因此,将您的Runnable 发布到HandlerThread 上的处理程序 从Runnable 获取结果并将其发送回UI 线程并显示Toast 消息。

解决方案:

    创建HandlerThread 并启动它 用Looper从HandlerThread:requestHandler创建一个Handler 从主线程创建一个带有 Looper 的处理程序:responseHandler 并覆盖 handleMessage 方法 postRunnablerequestHandler 上的任务 在Runnable 任务中,在responseHandler 上调用sendMessagesendMessage 导致在responseHandler 中调用handleMessage。 从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-and-why-you-should-be-using-them-in-your-android-apps

android-looper-handler-handlerthread-i

【讨论】:

【参考方案7】:
    获取 UI Thread Handler 实例并使用handler.sendMessage(); 调用post()方法handler.post(); runOnUiThread() view.post()

【讨论】:

【参考方案8】:

您可以使用Looper 发送Toast 消息。通过此 link 了解更多详细信息。

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

它在你的线程中被调用。上下文可能是Activity.getContext()Activity 得到的,你必须展示toast。

【讨论】:

【参考方案9】:

我根据 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();
        
    );

对我来说效果很好。

【讨论】:

【参考方案10】:

带有 runOnUiThread 的 Kotlin 代码

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

【讨论】:

【参考方案11】:

我也遇到了同样的问题:

E/AndroidRuntime: FATAL EXCEPTION: Thread-4
              Process: com.example.languoguang.welcomeapp, PID: 4724
              java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
                  at android.widget.Toast$TN.<init>(Toast.java:393)
                  at android.widget.Toast.<init>(Toast.java:117)
                  at android.widget.Toast.makeText(Toast.java:280)
                  at android.widget.Toast.makeText(Toast.java:270)
                  at com.example.languoguang.welcomeapp.MainActivity$1.run(MainActivity.java:51)
                  at java.lang.Thread.run(Thread.java:764)
I/Process: Sending signal. PID: 4724 SIG: 9
Application terminated.

之前:onCreate 函数

Thread thread = new Thread(new Runnable() 
    @Override
    public void run() 
        Toast.makeText(getBaseContext(), "Thread", Toast.LENGTH_LONG).show();
    
);
thread.start();

之后:onCreate 函数

runOnUiThread(new Runnable() 
    @Override
    public void run() 
        Toast.makeText(getBaseContext(), "Thread", Toast.LENGTH_LONG).show();
    
);

成功了。

【讨论】:

【参考方案12】:

Java 11:

var handler = new Handler(Looper.getMainLooper);
handler.post(() -> Toast.makeText(your_context, "Hi!", Toast.LENGTH_SHORT).show());

不过,Lambda 可在 java 8 中使用。 var 在 java 11 中引入。

【讨论】:

以上是关于如何在 Android 上显示来自后台线程的 Toast?的主要内容,如果未能解决你的问题,请参考以下文章

在主 ui 线程上处理来自其他线程的太多动画请求 - android

如何在后台线程中从 NSFetchedResultsController 加载大数据

通过AsyncTask访问后台后得到的返回数据在Android端显示

在后台在 Recyclerview 中显示 Coverart(来自 id3)

如何在 Android 上显示来自 URL 的图像

iOS:如何让后台线程在主线程上重绘视图