前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Toast 的 Window 创建过程

Toast 的 Window 创建过程

作者头像
Yif
发布2019-12-26 15:29:13
4570
发布2019-12-26 15:29:13
举报
文章被收录于专栏:Android 进阶Android 进阶
undefined
undefined

Toast 的 Window 创建过程

Toast也是基于Window实现,可以定时取消,内部采用了Handler

两种IPC进程方式,分别是NotificationManagerServiceNotificationManagerService回调内部的TN接口实现。 它可以自定义view,也可以使用默认的view,默认的就是mNextView。当前view的显示与取消都是一次跨进程通信。也就是show与hide方法。

    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }
 
        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;
 
        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }
    public void cancel() {
        mTN.cancel();
    }

TN一个跨进程通信,在ToastNotificationManagerService进行进程通信时候,回调TN方法运行在Binder线程池中,所有需要handler进行切换到当前线程,而handler切换线程操作必须通过looper才能够完成,所有首先进行非空判断操作,再进行切换线程。

 if (looper == null) {
                // Use Looper.myLooper() if looper is not specified.
                looper = Looper.myLooper();
                if (looper == null) {
                    throw new RuntimeException(
                            "Can't toast on a thread that has not called Looper.prepare()");
                }
            }
            mHandler = new Handler(looper, null) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case SHOW: {
                            IBinder token = (IBinder) msg.obj;
                            handleShow(token);
                            break;
                        }
                        case HIDE: {
                            handleHide();
                            // Don't do this in handleHide() because it is also invoked by
                            // handleShow()
                            mNextView = null;
                            break;
                        }
                        case CANCEL: {
                            handleHide();
                            // Don't do this in handleHide() because it is also invoked by
                            // handleShow()
                            mNextView = null;
                            try {
                                getService().cancelToast(mPackageName, TN.this);
                            } catch (RemoteException e) {
                            }
                            break;
                        }
                    }
                }
            };

Toast的show方法中调用了service.enqueueToast(pkg, tn, mDuration);,这里的Service实质上是NotificationManagerService,也就是调用了NotificationManagerService中的enqueueToast方法,这个方法运行在服务端中,然后再Toast类中通过getService来获取NotificationManagerService实例对象。

static private INotificationManager getService() {
        if (sService != null) {
            return sService;
        }
        sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
        return sService;
    }

public void enqueueToast(String pkg, ITransientNotification callback, int duration)第一个参数表示当前包名,第二个参数表示远程回调,第三个参数表示Toast时长。 方法内部将Toast对象封装成ToastRecord对象,然后添加到mToastQueue队列中,队列中最多存储50个ToastRecord对象,主要为了防止DDOS。

  record = new ToastRecord(callingPid, pkg, callback, duration, token);
  mToastQueue.add(record);

添加到队列中,通过showNextToastLocked来。

void showNextToastLocked() {
        ToastRecord record = mToastQueue.get(0);
        while (record != null) {
            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
            try {
            //通过callback来进行展示,这里是一个远程进程通信,通过callback来访问TN中的方法
                record.callback.show(record.token);
           //内部还会发送一个延迟消息
                scheduleTimeoutLocked(record);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Object died trying to show notification " + record.callback
                        + " in package " + record.pkg);
                // remove it from the list and let the process die
                int index = mToastQueue.indexOf(record);
                if (index >= 0) {
                    mToastQueue.remove(index);
                }
                keepProcessAliveIfNeededLocked(record.pid);
                if (mToastQueue.size() > 0) {
                    record = mToastQueue.get(0);
                } else {
                    record = null;
                }
            }
        }
    }

延迟消息scheduleTimeoutLocked,有两种一种是LONG_DELAY:3.5秒,SHORT_DELAY:2秒。

private void scheduleTimeoutLocked(ToastRecord r)
    {
        mHandler.removeCallbacksAndMessages(r);
        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
        mHandler.sendMessageDelayed(m, delay);
    }

Toast隐藏也是通过callback实现跨进程通信,访问TN中的hide方法。

void cancelToastLocked(int index) {
        ToastRecord record = mToastQueue.get(index);
        try {
            record.callback.hide();
        } catch (RemoteException e) {
            Slog.w(TAG, "Object died trying to hide notification " + record.callback
                    + " in package " + record.pkg);
        }
 
        ToastRecord lastToast = mToastQueue.remove(index);
        mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
 
        keepProcessAliveIfNeededLocked(record.pid);
        if (mToastQueue.size() > 0) {
            showNextToastLocked();
        }
    }

TN内部两个方法show与hide方法

 @Override
        public void show(IBinder windowToken) {
            if (localLOGV) Log.v(TAG, "SHOW: " + this);
            mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
        }
@Override
        public void hide() {
            if (localLOGV) Log.v(TAG, "HIDE: " + this);
            mHandler.obtainMessage(HIDE).sendToTarget();
        }

都是通过handler发送一个消息,再切换到当前线程中分别调用handleShowhandleHidehandleShow将Toast添加到Window中 mWM.addView(mView, mParams); handleHide将Toast从Window中移除 mWM.removeViewImmediate(mView);

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年9月18日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Toast 的 Window 创建过程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档