简体   繁体   English

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

[英]How do you display a Toast from a background thread on Android?

How can I display Toast messages from a thread?如何显示来自线程的Toast消息?

You can do it by calling an Activity 's runOnUiThread method from your thread:您可以通过从您的线程调用ActivityrunOnUiThread方法来实现:

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

I like to have a method in my activity called showToast which I can call from anywhere...我喜欢在我的活动中有一个名为showToast的方法,我可以从任何地方调用它...

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

I then most frequently call it from within MyActivity on any thread like this...然后,我最常从MyActivity在任何像这样的线程上调用它......

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

This is similar to other answers, however updated for new available apis and much cleaner.这与其他答案类似,但是针对新的可用 API 进行了更新,并且更加清晰。 Also, does not assume you're in an Activity Context.此外,不假设您处于活动上下文中。

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

One approach that works from pretty much anywhere, including from places where you don't have an Activity or View , is to grab a Handler to the main thread and show the toast:一种几乎适用于任何地方(包括没有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();
    }
  });
}

The advantage of this approach is that it works with any Context , including Service and Application .这种方法的优点是它适用于任何Context ,包括ServiceApplication

Like this or this , with a Runnable that shows the Toast .这样这样,带有一个显示ToastRunnable Namely,即,

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

Sometimes, you have to send message from another Thread to UI thread.有时,您必须从另一个Thread向 UI 线程发送消息。 This type of scenario occurs when you can't execute Network/IO operations on UI thread.当您无法在 UI 线程上执行网络/IO 操作时,就会发生这种情况。

Below example handles that scenario.下面的示例处理这种情况。

  1. You have UI Thread你有 UI 线程
  2. You have to start IO operation and hence you can't run Runnable on UI thread.您必须启动 IO 操作,因此您无法在 UI 线程上运行Runnable So post your Runnable to handler on HandlerThread因此,将您的RunnableHandlerThread上的处理程序
  3. Get the result from Runnable and send it back to UI thread and show a Toast message.Runnable获取结果并将其发送回 UI 线程并显示Toast消息。

Solution:解决方案:

  1. Create a HandlerThread and start it创建一个HandlerThread并启动它
  2. Create aHandler with Looper from HandlerThread : requestHandlerHandlerThread使用Looper创建一个处理程序requestHandler
  3. Create a Handler with Looper from Main Thread: responseHandler and override handleMessage method从主线程使用 Looper 创建一个处理程序: responseHandler并覆盖handleMessage方法
  4. post a Runnable task on requestHandlerrequestHandlerpost一个Runnable任务
  5. Inside Runnable task, call sendMessage on responseHandlerRunnable任务中,在responseHandler上调用sendMessage
  6. This sendMessage result invocation of handleMessage in responseHandler .这个sendMessage结果在responseHandler调用handleMessage
  7. Get attributes from the Message and process it, update UIMessage获取属性并进行处理,更新 UI

Sample code:示例代码:

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

Useful articles:有用的文章:

handlerthreads-and-why-you-should-be-using-them-in-your-android-apps handlerthreads-and-why-you-should-be-using-them-in-your-android-apps

android-looper-handler-handlerthread-i android-looper-handler-handlerthread-i

  1. Get UI Thread Handler instance and use handler.sendMessage();获取 UI Thread Handler 实例并使用handler.sendMessage();
  2. Call post() method handler.post();调用post()方法handler.post();
  3. runOnUiThread()
  4. view.post()

You can use Looper to send Toast message.您可以使用Looper发送Toast消息。 Go through this link for more details.通过此链接了解更多详情。

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

and it is called in your thread.它在您的线程中被调用。 Context may be Activity.getContext() getting from the Activity you have to show the toast. Context 可能是Activity.getContext()从你必须展示 toast 的Activity获取。

I made this approach based on mjaggard answer:我根据 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();
        }
    });
}

Worked well for me.对我来说效果很好。

Kotlin Code with runOnUiThread带有 runOnUiThread 的 Kotlin 代码

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

I encountered the same problem:我遇到了同样的问题:

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.

Before: onCreate function之前:onCreate 函数

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

After: onCreate function之后:onCreate 函数

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

it worked.有效。

java 11:爪哇11:

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

Lambdas are available in java 8 though.不过,在 Java 8 中可以使用 Lambda。 var is introduced in java 11. var是在 Java 11 中引入的。

Contrary to almost every answer here, Toast#makeText and Toast#show do NOT have to run on the UI thread.与此处几乎所有答案相反, Toast#makeTextToast#show不必在 UI 线程上运行。 The only requirement is that it runs on a thread that has called Looper#prepare .唯一的要求是它在一个名为Looper#prepare的线程上运行。

The reasons for this is because toasts are handled and rendered by the OS, not the application.这是因为 toast 是由操作系统处理和呈现的,而不是应用程序。 Internally, Toast#show makes a call to a system service to enqueue the toast.在内部, Toast#show调用系统服务以将 toast 排队。

This means the following code is valid这意味着下面的代码是有效的

private static class MyThread extends Thread {
    public Handler handler;

    @Override
    public void run() {
        Looper.prepare();

        handler = new Handler(Looper.myLooper()) {
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

        Looper.loop()
    }
}

final private MyThread t = new MyThread();
// start and wait for t to start looping

private void onClick() {
    t.handler.post(() -> Toast.makeText(this, "this works", Toast.LENGTH_SHORT).show());
}

Method in onCreate: onCreate 中的方法:

private void toastPublic(final String message){
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
       public void run() {
          Toast.makeText(getBaseContext(),""+message, 
             4 /*Toast.LENGTH_SHORT*/).show();
    }});
}

Next: use in inside Thread下一步:在线程内部使用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM