Window的创建过程
Window是View的直接管理者
Activity的Window创建过程
Activity的启动最终会通过ActivityThread#performLaunchActivity()来完成。
ActivityThread#performLaunchActivity()
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...
//通过类加载器创建Activity实例
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//...
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
//关联运行过程中所依赖的一系列上下文环境变量
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);
mActivities.put(r.token, r);
return activity;
}
在attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口。
Activity#attach()
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, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
//创建Window对象
mWindow = PolicyManager.makeNewWindow(this);
//设置回调 比如我们熟悉的onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent
//[重要] 当Window接收到外界的状态改变时就会调用Activity实现的回调
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
//赋值
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
//...
//设置WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
再来看 PolicyManager.makeNewWindow(this),PilicyManager的真正实现类是Policy类。
位于source\frameworks\base\policy\src\com\android\internal\policy\impl\Policy.java
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
可以发现Window的实现类确实是PhoneWindow。
Window初始完毕后,再来看Activity的视图是如何依附在Window上的。Activity的视图通过setContent方法提供。
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
可以发现,Activity的setContentView将具体实现交由Window处理。
phoneWindow#setContentView()
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
//1.创建DecorView (如果没有创建的话)
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
//2.将View添加到DecorView的mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
//3.回调Activity的onContentChanged()通知Activity视图已经发生改变
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
经过setContentView方法,DecorView已经被创建并初始化完毕,Activity的布局文件也已经成功添加到DecorView的mContentParent中,但这个时候DecorView还没有被WindowManager添加到Window中。 (在添加之前)
#
- Activity的attach()中,Window被创建并初始化
- 在Activity的setContentView中 (PhoneWindow#setContentView),DecorView被创建 (如果没被创建的话)
- 而在ActivityThread#handleResumeActivity首先会调用Activity的onResume方法中,接着会先将DecorView设为不可见(INVISIBLE),然后会调用Activity的makeVisible(),在makeVisible()中,将DecorView添加到Window并置为Visible。
#
void makeVisible() {
//将DecorView添加到Window
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
//将Activity的显示置为Visible
mDecor.setVisibility(View.VISIBLE);
}
Dialog的window创建过程
Dialog的Window创建过程和Activity类似
Dialog#构造方法
Dialog(Context context, int theme, boolean createContextThemeWrapper) {
//...
mContext = context;
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
//创建Window对象
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
再来看Dialog#setContentView()
public void setContentView(int layoutResID) {
mWindow.setContentView(layoutResID);
}
同样是交由Window处理。
当Dialog dismiss时,会通过WindowManager来移除DecorView
@Override
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}
void dismissDialog() {
//...
//移除DecorView
mWindowManager.removeViewImmediate(mDecor);
//...
}
注意
普通Dialog需要使用Activity的Content
因为Window需要应用token,而应用token一般只有Activity才拥有。
系统Window比较特殊,它可以不需要token。
Toast的Window创建过程
Toast属于系统Window
Toast内部有两类IPC过程
- Toast访问NotifationManagerService
- NotificationManagerService回调Toast的TN接口
Toast提供了show和cancel分别用于显示和隐藏Toast [都是IPC过程]
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.hide();
try {
getService().cancelToast(mContext.getPackageName(), mTN);
} catch (RemoteException e) {
// Empty
}
}
主要来看enqueueToast()
//参数一:当前应用的包名
//参数二:远程回调
//参数三:Toast的时长
//enqueueToast首先将Toast封装为ToastRecord对象并将其添加到一个名为mToastQueue的队列中
service.enqueueToast(pkg, tn, mDuration);
当ToastRecord被添加到mToastQueue中后,Inotifacationmanager就会通过showNextToastLacked方法来显示当前的Toast。
NotificationManagerService#showNextToastLocked()
void showNextToastLocked() {
//获取下一个ToastRecord
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
//record.callback就是Toast.java类中的TN (Binder) 对象
record.callback.show();
//发送延迟消息来移除toast
//scheduleTimeoutLocked -> mHandler.sendMessageDelayed -> cancelToastLocked -> record.callback.hide();
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);
}
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
}
可以发现,Toast的显示和移除都是通过Toast的TN类(Binder对象)来完成的。
Toast内部类TN
private static class TN extends ITransientNotification.Stub {
final Runnable mShow = new Runnable() {
@Override
public void run() {
handleShow();
}
};
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
// Don't do this in handleHide() because it is also invoked by handleShow()
mNextView = null;
}
};
@Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
//...
}
真正的显示
public void handleShow() {
//...
mWM = (WindowManager)context.getSystemService
//...
if (mView.getParent() != null) {
mWM.removeView(mView);
}
mWM.addView(mView, mParams);
//...
}
}
真正的隐藏
public void handleHide() {
if (mView != null) {
if (mView.getParent() != null) {
mWM.removeView(mView);
}
mView = null;
}
}