安卓Activity Dialog和Toast 的Window 创建过程

一:Activity的Window创建过程

在Activity 的创建过程中,最终会由ActivityThread 的

performLaunchActivity() 来完成整个启动过程,该方法内部会通过类加载器创建

Activity 的实例对象,并调用attach 方法关联一系列上下文环境变量。在

Activity 的attach 方法里,系统会创建所属的Window 对象并设置回调接口,

然后在Activity 的setContentView 方法中将视图附属在Window 上:

Activity.java

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor
voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode !=
WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
···
}
···
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow.java
@Overridepublic void setContentView(int layoutResID) {
if (mContentParent == null) { // 如果没有DecorView,就创建
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
// 回调Activity 的onContentChanged 方法通知Activity 视图已经发生改变
cb.onContentChanged();
}
}

这个时候DecorView 还没有被WindowManager 正式添加。在ActivityThread

的handleResumeActivity 方法中,首先会调用Activity 的onResume 方法,

接着调用Activity 的makeVisible(),完成DecorView 的添加和显示过程:

Activity.java

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}

二: Dialog 的Window 创建过程

Dialog 的Window 的创建过程和Activity 类似,创建同样是通过

PolicyManager 的makeNewWindow 方法完成的,创建后的对象实际就是

PhoneWindow。当Dialog 被关闭时,会通过WindowManager 来移除

DecorView:mWindowManager.removeViewImmediate(mDecor)。

Dialog.java

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean
createContextThemeWrapper) {
···
mWindowManager = (WindowManager)
context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}

普通Dialog 必须采用Activity 的Context,采用Application 的Context 就

会报错,是因为应用token 所导致,应用token 一般只有Activity 拥有。系

统Window 比较特殊,不需要token。

三: Toast 的Window 创建过程

Toast 属于系统Window ,由于其具有定时取消功能,所以系统采用了Handler。

Toast 的内部有两类IPC 过程,第一类是Toast 访问

NotificationManagerService,第二类是NotificationManagerService 回调

Toast 里的TN 接口。

Toast 内部的视图由两种方式,一种是系统默认的样式,另一种是setView 指

定一个自定义View,它们都对应Toast 的一个内部成员mNextView。

Toast.java

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();
}
NotificationManagerService.java
private void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" +
record.callback);
try {
record.callback.show();
scheduleTimeoutLocked(record, false);
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);
}
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
}
···private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
{
Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY :
SHORT_DELAY);
mHandler.removeCallbacksAndMessages(r);
mHandler.sendMessageDelayed(m, delay);
}

猜你喜欢

转载自blog.csdn.net/yzwfeng/article/details/129744283