1. 启动窗口的添加
当一个新的Activity启动时系统可能会先显示一个启动窗口,这个窗口会等到Activity的主界面显示出来以后才消失。这个窗口的启动和销毁是由系统来管理的,涉及了AMS和WMS###
当AMS在处理startActivity时,它会调用内部的ActivityStack.startActivityLocked,如果NH>0,说明我们正在切换到一个新的task或者另一个进程,这时就会准备启动一个PreviewWindow,不过会受到以下两个因素的制约
- SHOW_APP_STARTING_PREVIEW
ActivityStack中一个Boolean变量,代表是否要启动预览窗口,默认是true - doShow
这个变量默认是true,如果要启动的Activity是一个新的Task中,且设置了FLAG_activity_rest_task_if_needed,那么我们就要在必要的时候执行reset(resetTaskIfNeeded),然后判断这个activity是否在mHistory最顶端,只有这样doShow才会是true,否则不需要添加任何启动窗口
1.1 startActivityLocked
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
boolean doShow = true;
if (newTask) {
if ((r.intent.getFlags()
&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
}
if (SHOW_APP_STARTING_PREVIEW && doShow) {
ActivityRecord prev = mResumedActivity;
if (prev != null) {
if (prev.task != r.task) {
prev = null;
}
else if (prev.nowVisible) {
prev = null;
}
}
mWindowManager.setAppStartingWindow(
r.appToken, r.packageName, r.theme,
mService.compatibilityInfoForPackageLocked(
r.info.applicationInfo), r.nonLocalizedLabel,
r.labelRes, r.icon, r.logo, r.windowFlags,
prev != null ? prev.appToken : null, showStartingIcon);
}
}
1.2 setAppStartingWindow
@Override
public void setAppStartingWindow(IBinder token, String pkg,
int theme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo,
int windowFlags, IBinder transferFrom, boolean createIfNeeded) {
synchronized(mWindowMap) {
//再次验证是否需要显示启动窗口,因为是activity的启动窗口,那么AppWindowToken一定不能为空
AppWindowToken wtoken = findAppWindowToken(token);
if (wtoken == null) {
return;
}
//屏幕的状态是否允许显示,如果frozen或者disable那么就直接返回
if (!okToDisplay()) {
return;
}
//用于存储与启动窗口相关的信息,如果这个信息不为空,说明已经处理过启动窗口,这种情况就不要继续往下执行
if (wtoken.startingData != null) {
return;
}
//加入不存在关联的启动窗口,且调用者又认为没有必要创建一个新的,那么就直接返回
if (!createIfNeeded) {
return;
}
if (theme != 0) {
//Activity的主题不能为空
}
mStartingIconInTransition = true;
//与启动窗口相关的数据
wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
labelRes, icon, logo, windowFlags);
Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
//投递消息 msg里包含token,token的startingData包含启动窗口信息
mH.sendMessageAtFrontOfQueue(m);
}
}
1.3 消息处理
case ADD_STARTING: {
//从msg取出token,再从token中取出startingData
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
final StartingData sd = wtoken.startingData;
if (sd == null) { //数据为空,直接返回
return;
}
View view = null;
try {
//核心
view = mPolicy.addStartingWindow(
wtoken.token, sd.pkg, sd.theme, sd.compatInfo,
sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo, sd.windowFlags);
} catch (Exception e) {
}
if (view != null) {
boolean abort = false;
synchronized(mWindowMap) {
if (wtoken.removed || wtoken.startingData == null) {
if (wtoken.startingWindow != null) {
removeStartingWindowTimeout(wtoken);
wtoken.startingWindow = null;
wtoken.startingData = null;
abort = true;
}
} else {
wtoken.startingView = view;
}
}
if (abort) {
try {
mPolicy.removeStartingWindow(wtoken.token, view);
} catch (Exception e) {
}
}
}
}
1.4
@Override
public View addStartingWindow(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int windowFlags) {
WindowManager wm = null;
View view = null;
try {
Context context = mContext;
//生成一个Window对象
Window win = PolicyManager.makeNewWindow(context);
final TypedArray ta = win.getWindowStyle();
if (ta.getBoolean(
com.android.internal.R.styleable.Window_windowDisablePreview, false)
|| ta.getBoolean(
com.android.internal.R.styleable.Window_windowShowWallpaper,false)) {
//如果设置了这两个参数,返回null
return null;
}
Resources r = context.getResources();
//标题
win.setTitle(r.getText(labelRes, nonLocalizedLabel));
//类型
win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
//设置各种窗口标志,启动窗口不接受touch事件
win.setFlags();
//设置窗口大小
win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
final WindowManager.LayoutParams params = win.getAttributes();
params.token = appToken;
params.packageName = packageName;
params.windowAnimations = win.getWindowStyle().getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
//params的title
params.setTitle("Starting " + packageName);
wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
view = win.getDecorView();
//addView
wm.addView(view, params);
return view.getParent() != null ? view : null;
} catch (WindowManager.BadTokenException e) {
} catch (RuntimeException e) {
} finally {
wm.removeViewImmediate(view);
}
}
return null;
}
和普通Activity窗口一样,我们需要将启动窗口注册到WMS中,然后由WMS在SurfaceFlinger中申请一个Surface来承载UI数据,以使得窗口真正的显示在屏幕上,接下来的步骤就和Activity窗口的添加流程一样
2.启动窗口的销毁
一旦应用程序的主窗口显示出来,与之相关联的启动窗口就会被销毁。
2.1
case REMOVE_STARTING: {
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
IBinder token = null;
View view = null;
synchronized (mWindowMap) {
if (wtoken.startingWindow != null) {
view = wtoken.startingView;
token = wtoken.token;
//清理工作
wtoken.startingData = null;
wtoken.startingView = null;
wtoken.startingWindow = null;
wtoken.startingDisplayed = false;
}
}
if (view != null) {
try {
mPolicy.removeStartingWindow(token, view);
} catch (Exception e) {
}
}
}
2.2 removeStartingWindow
public void removeStartingWindow(IBinder appToken, View window) {
if (window != null) {
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
wm.removeView(window);
}
}
2.3 removeView
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
public void removeView(View view, boolean immediate) {
synchronized (mLock) {
int index = findViewLocked(view, true);//找到index
View curView = mRoots.get(index).getView();//根据index找到view
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);//根据index找到ViewRootImpl
View view = root.getView();
//die方法
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
2.3
//如果immediate为true,那么直接执行doDie,如果为false通过消息队列还是执行到doDie
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
mHandler.sendEmptyMessage(MSG_DIE); // case MSG_DIE: doDie();
return true;
}
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
try {
//relayoutWindow方法NOTICE。。。
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;//状态位
}
WindowManagerGlobal.getInstance().doRemoveView(this);//
}
2.4
void dispatchDetachedFromWindow() {
mSurface.release();
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
unscheduleTraversals();
}
public void release() {//释放这个窗口相关的Surface
synchronized (mLock) {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
setNativeObjectLocked(0);
}
}
}
static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) {
sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
sur->decStrong(&sRefBaseOwner);
}
//三个数组中的对应对象都删除掉
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
}
2.5
//WindowState是用来记录窗口状态的,所以当移除窗口的时候WMS中的WindowList里相应的WindowState也是需要更新的
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
网友评论