最近遇到一个 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…