Toast 显示流程

110 阅读2分钟

最近遇到一个 Toast 不消失的问题,于是就去跟了一下 Toast 的流程。

一般的应用侧会先调用 makeText 来把想要显示的 Toast 内容设置好。

Toast.makeText(context, text, duration).show();

然后去去调用 Show( ) 方法

Toast.makeText(mActivity.getApplicationContext(), msg, Toast.LENGTH_SHORT).show();


frameworks/base/core/java/android/widget/Toast.java 
public void show() {
	... 
	INotificationManager service = getService();
	...
	service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
	...
}

这里可以看到创建好 Toast 之后就放到队列中排队了。这里同时也用到了跨进程的方法。客户端调用服务端的服务,然后服务端把结果返回给客户端。

先看 makeText( ) 方法,加载好布局,设置好显示内容以及显示的时长设置,返回这个对象。

public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        Toast result = new Toast(context, looper);

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

        result.mNextView = v;
        result.mDuration = duration;

        return result;
    }

Toast result = new Toast(context, looper);中初始化了一个TN对象mTN

public Toast(@NonNull Context context, @Nullable Looper looper) {
        mContext = context;
        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);
    }

这里把纵向坐标和重力方向设置到了mTN中。 然后就是调用show()方法显示了:

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
        }
    }

这里getService()取到了服务,然后调用service.enqueueToast(pkg, tn, mDuration);去排队。

这里的getService()实际上是拿到了一个Service的本地代理:

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

***.Stub.asInterface就是AIDL中的一个典型的写法。

先查看本地有没有对象,如果有就说明没有跨进程,直接返回本地对象。 如果没有就返回一个代理。这里可以看作是服务端为客户端准备一个“假”自己,让客户端看起来就像拥有一个真正的服务端对象。

Toast 显示流程




NoticicationManagerService.java
|
enqueueToast
showNextToastLocked
scheduleDurationReachedLocked
MESSAGE_DURATION_REACHED
cancelToastLocked() // Toast 消失

参考链接: juejin.cn/post/684490…