android Toast为何必须在主线程或者初始化了Looper的线程中执行show操作

Toast的经典使用方式:

Toast.makeText(context, msg, Toast.LENGTH_LONG).show();

但上面代码在工作线程中执行的话,是无法弹出Toast提示的。除非在其前后分别执行Looper.prepare()和Looper.loop()。

Looper.prepare();
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
Looper.loop(); 
至于原因,之前一直未想明白。今天看了下Toast的源码实现,

/**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
             //将该Toast的tn加入到NotificationManagerService的ToastQueue中,NotificationManagerService负责Toast的管理工作
             service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }

当NotificationManagerService需要显示该Toast时,将会执行Toast.TN的show()和hide()回调。而这两个回调的实现为通过Handler来post一个Runnable

 /**
         * schedule handleShow into the right thread
         */
        @Override
        public void show() {
            if (localLOGV) Log.v(TAG, "SHOW: " + this);
            mHandler.post(mShow);
        }

        /**
         * schedule handleHide into the right thread
         */
        @Override
        public void hide() {
            if (localLOGV) Log.v(TAG, "HIDE: " + this);
            mHandler.post(mHide);
        }
而mHandler定义如下,即   使用当前线程的Looper作为Handler的mLooper。
final Handler mHandler = new Handler(); 

这样就可以一目了然,为何Toast无法在工作线程正常使用了。因为工作线程若没有初始化其Looper,也就没有MessageQueue来管理Handler消息,Handler的post()或sendMessage()方法将会返回失败(false),消息得不到处理。


        记得以前一个项目,一直存在一个疑问:Activity的onCreate()中创建一个Handler,但使用Handler发送的消息,只会在onResume()之后才会被执行(多线程的缘故,工作线程执行的比onResume早)。当时只是发现这个现象,但一直没有细究原因。

         首先,主线程的Looper初始化是在ActivityThread.main()方法中执行的

public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        AsyncTask.init();

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

          而启动一个Activity的方式就是在ActivityThread中就是发送和处理LAUNCH_ACTIVITY消息。

// we use token to identify this activity without having to send the
        // activity itself back to the activity manager. (matters more with ipc)
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
                Bundle state, List<ResultInfo> pendingResults,
                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
            ActivityClientRecord r = new ActivityClientRecord();

            。。。。。。

            queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
        }
            LAUNCH_ACTIVITY消息处理中就是加载该自定义Activity类,顺序初始化并回调Activity的onCreate、onStart、onResume回调。

            也就是说Activity的onCreate、onStart、onResume三个生命周期回调都是LAUNCH_ACTIVITY消息处理,而在这三个生命周期期间发送的Handler消息自然只能排在LAUNCH_ACTIVITY消息之后执行了。



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值