2

I used HandlerThread and then used its looper to create a new Handler so that it can run operations on a non-UI thread. In the runnable which is posted to the handler, I added Toast messages to be displayed. I expected that to cause a problem as you can't touch UI components from the non-UI thread, but it still works and that toast is still being shown. Can anyone explain why toast is being displayed from the non-UI thread?

 //Inside a Fragment class
    private Handler handler;
    private HandlerThread mHandlerThread = null;

    public void startHandlerThread() {
        mHandlerThread = new HandlerThread("HandlerThread");
        mHandlerThread.start();
        handler = new Handler(mHandlerThread.getLooper());
    }


    private Runnable submitRunnable = new Runnable() {
        @Override
        public void run() {
            //do some long running operations here
            //Thread.sleep(2000);

            //Check whether currentLooper is the Main thread looper
            boolean isUiThread = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                    ? Looper.getMainLooper().isCurrentThread()
                    : Thread.currentThread() == Looper.getMainLooper().getThread();

            if (isUiThread) {
                // You are on the UI thread
                Log.d("Thread", "Main thread");
            } else {
                // You are on the non-UI thread
                Log.d("Thread", "Not Main thread"); //This will be printed
            }

            Toast.makeText(getContext(), "toast is shown", Toast.LENGTH_SHORT).show();
        }
    };

    submitButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                handler.post(submitRunnable);
            }
        });

I checked Toast.java and saw that the looper initializes itself with Looper.myLooper().

 if (looper == null) {
            // Use Looper.myLooper() if looper is not specified.
            looper = Looper.myLooper();
        }

From the doc:

myLooper(): Return the Looper object associated with the current thread.

And the currentThread is the HandlerThread, not the main thread. Hence, I am unable to understand how the toast is being displayed from the non-UI thread, or if it is something plain simple I am missing to see.

Richa
  • 700
  • 8
  • 12
  • I agree with @GParekar. If you dig deeper into source code of Toast, It is relying on the `Context` param that you passed to `makeText` method. It uses that `Context` to inflate a default view and get the `WindowManager` to add the inflated view to it. In docs no where it is stated that `Can't display Toast using a background thread`. It uses the `Context` to display toast and does not depend on that thread that is being shown. This also applicable if you set custom view to a Toast. Because, Toast messages never receives focus (As per docs). – Uma Sankar Mar 04 '19 at 06:24

1 Answers1

-1

For showing Toast you used getContext() as a context.

getContext() - Returns the context the view is currently running in. Usually the currently active Activity.

While you are using fragment so it will take activity context where fragment will resides in activity.

That's why Toast shown.

GParekar
  • 1,209
  • 1
  • 8
  • 15
  • 1
    Yes, `getContext()` returns the Context this fragment is currently associated with. But I don't think that is the reason. When you check the code inside Toast.java, where Looper is initialized for toast, I see the `HandlerThread` in its name, which I used while creating the background thread. Also, forget toast, even if I do a `setText()` on a textView by doing `textView.setText("something")`, even that is being run and new text is set from the `submitRunnable`. How would you explain that? – Richa Mar 04 '19 at 05:47
  • 1
    This is not a Promising answer. By looking at source code of `Toast` `Looper` is created by `Looper.getMyLooper()` not `Looper.getMainLooper` .. So for me its still a mystery . – ADM Mar 04 '19 at 06:01
  • Looks like `Toasts` can run on a background `Thread`, as long as this `Thread` has a `Looper` (which is the case for `HandlerThreads`) – Florian Walther May 11 '19 at 19:15