Toast源码分析

常规使用

Toast.makeText(mActivity, "hi", Toast.LENGTH_SHORT).show();

Toast.make(

    public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        return makeText(context, null, text, duration);
    }

    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        // 创建Toast类
        Toast result = new Toast(context, looper); 
 		// 布局加载器 加载 transient_notification
        LayoutInflater inflate = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
        TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
        tv.setText(text);
        // Toast类中包含有 Toast的View ,mDuration 字段
        result.mNextView = v; 
        result.mDuration = duration;
        return result;
    }

  new Toast(context, looper)

    public Toast(@NonNull Context context, @Nullable Looper looper) {
        mContext = context;
		// 创建 ToastNotification 传入 looper 
        mTN = new TN(context.getPackageName(), looper);
        // 获取尺寸
        mTN.mY = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.toast_y_offset);
        //设置布局相对位置
        mTN.mGravity = context.getResources().getInteger(
                com.android.internal.R.integer.config_toastDefaultGravity);
    }

new Tn(context.getPackageName(),looper) 

	// TN 继承代理接口 主要 show hide cancel 
	private static class TN extends ITransientNotification.Stub {
		...

        private static final int SHOW = 0;
        private static final int HIDE = 1;
        private static final int CANCEL = 2;
        final Handler mHandler;
		...
        View mView;
        View mNextView;
        int mDuration;

        static final long SHORT_DURATION_TIMEOUT = 4000;
        static final long LONG_DURATION_TIMEOUT = 7000;

        TN(String packageName, @Nullable Looper 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()");
                }
            }
            // 根据传入looper 创建处理handler 
            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;
                        }
                    }
                }
            };
        }

        /**
         * schedule handleShow into the right thread
         */
        @Override
        public void show(IBinder windowToken) {
            // 发送 SHOW 消息
            mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
        }

        /**
         * schedule handleHide into the right thread
         */
        @Override
        public void hide() {
            // 发送隐藏消息
            mHandler.obtainMessage(HIDE).sendToTarget();
        }

        public void cancel() {
           	//发送取消消息
            mHandler.obtainMessage(CANCEL).sendToTarget();
        }

       

show方法

    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }
		// 获取 NotificationManagerService 通知管理服务
        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            // 执行 NotificationManagerService.enqueueToast 进行消息转发
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }

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

NotificationManagerService.enqueueToast

	// 这里的callback  ITransientNotification ,TN继承的就是 ITransientNotification.stub
	@Override
        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
        {
       		..   
            final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
            final boolean isPackageSuspended =
                    isPackageSuspendedForUser(pkg, Binder.getCallingUid());
			// areNotificationsEnabledForPackage  判断是否有开启通知
            if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
                    (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
                            || isPackageSuspended)) {
                return;
            }
            
            synchronized (mToastQueue) {
                int callingPid = Binder.getCallingPid();
                long callingId = Binder.clearCallingIdentity();
                try {
                    ToastRecord record;
                    //取出与当前包名匹配的 ToastRecord 
                    int index = indexOfToastLocked(pkg, callback);
                    //从队列中获取 Toast信息
                    if (index >= 0) {
                        record = mToastQueue.get(index);
                        record.update(duration);
                    } else { // 找不到与当前包名匹配的ToastRecord 
                        // 创建一个新的加入队列 
                        if (!isSystemToast) {
                            int count = 0;
                            final int N = mToastQueue.size();
                            for (int i=0; i<N; i++) {
                                 final ToastRecord r = mToastQueue.get(i);
                                 if (r.pkg.equals(pkg)) {
                                     count++;
                                     // 匹配数量>50 后续不再处理
                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
                                         return;
                                     }
                                 }
                            }
                        }
						// 将新ToastRecord添加到队列 
                        Binder token = new Binder();
                        mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
                        record = new ToastRecord(callingPid, pkg, callback, duration, token);
                        mToastQueue.add(record);
                        index = mToastQueue.size() - 1;
                        keepProcessAliveIfNeededLocked(callingPid);
                    }
                    //index=0 表示是当前需要执行的Toast
                    if (index == 0) {
                        // 处理下一条 Toast
                        showNextToastLocked();
                    }
                } finally {
                    Binder.restoreCallingIdentity(callingId);
                }
            }
        }

showNextToastLocked

     @GuardedBy("mToastQueue")
    void showNextToastLocked() {
        ToastRecord record = mToastQueue.get(0);
        while (record != null) {
  			    // 调用TN的 show 进行展示Toast
                record.callback.show(record.token);
                // 发送延时消息 进行关闭Toast 
                scheduleTimeoutLocked(record);
        		return;
        }
    }

scheduleTimeoutLocked 

    @GuardedBy("mToastQueue")
    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);  //发送延时消息 根据长短标签
    }
    
	@Override
        public void handleMessage(Message msg)
        {
            switch (msg.what)
            {
    			//消息延时后处理 
                case MESSAGE_TIMEOUT:
                    handleTimeout((ToastRecord)msg.obj);
                    break;
          ...
              
                    
     // timeout 后处理       
     private void handleTimeout(ToastRecord record)
    {
        synchronized (mToastQueue) {
            int index = indexOfToastLocked(record.pkg, record.callback);
            if (index >= 0) {
                cancelToastLocked(index); //取消toast
		... 
   
       void cancelToastLocked(int index) {
        ToastRecord record = mToastQueue.get(index);
        try {
			// 调用TN的隐藏 
            record.callback.hide();
      
                    

TN  show hide 

  private static class TN extends ITransientNotification.Stub {
      ...
         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();
           ...
  
        @Override
        public void show(IBinder windowToken) { 
            mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
        }         
              @Override
        public void hide() {
            mHandler.obtainMessage(HIDE).sendToTarget();
        }                     
                            
             // 添加view到wm              
        public void handleShow(IBinder windowToken) {
				...
            if (mView != mNextView) {
                handleHide();
        		... 
                mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
                if (mView.getParent() != null) {
                    mWM.removeView(mView);
                }
     			    mWM.addView(mView, mParams);
                    trySendAccessibilityEvent();
            }
        }
  			// 从wm移除view
            public void handleHide() {
            if (mView != null) {
                if (mView.getParent() != null) {
                    mWM.removeViewImmediate(mView);
                }

                mView = null;
            }
        }                     

总结

Toast

     提供统一对外api方法,实际执行由Tn,或其他组件执行

     show操作执行时,调用 NotificationManagerService.enqueueToast 

Tn 消息具体实现代理类   

    内部维持handler 绑定外部传入looper (系统隐藏了 构造,默认主线程looper)

    show hide cancel 具体实现 再NotificationManagerService调用Tn show hide时通过handler引导进正确的线程进行处理Toast

NotificationManagerService

    内部维持      ArrayList<ToastRecord> mToastQueue 队列,Toast.show 时封装ToastRecord加入队列,并从Queue中获取ToastRecord,调用Tn.show执行,

    内部维持handler 进行消息延时处理

    通知发送延时消息,延时调用Tn.hide 移除Toast

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值