Android Activity 启动窗口源码分析

原文链接:https://blog.csdn.net/qq_34211365/article/details/103978529

ActivityStack.startActivityLocked

void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
			......
			//创建正在启动的Activity的AppWindowToken,并添加到Task中
			r.createAppWindowToken();
			......
			//是否显示启动窗口
		boolean doShow = true;
            if (newTask) {
            	//如果一个Activity被设置了FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
            	//并且是在一个新的task中启动,那么这个Activity就会被当作
            	//task的首个Activity来启动,即可能会显示启动窗口
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    resetTaskIfNeededLocked(r, r);
                    
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                }
            } else if (options != null && options.getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                ....
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {【SHOW_APP_STARTING_PREVIEW 和doShow都为true,SHOW_APP_STARTING_PREVIEW这个值是定义在ActivityStack中写死了为true,所以控制启动窗口是否显示依靠doShow】
            	//当前要启动的	Activity所在的task
                TaskRecord prevTask = r.getTaskRecord();
                //此方法是寻找当前task中设置了STARTING_WINDOW_SHOWN
                //并且不是正在finishing中并且可以显示的Activity,STARTING_WINDOW_SHOWN
                //是在下面要分析的showStartingWindow方法中设置的,
                //其实也就是获取task中已经添加了启动窗口的那个Activity,所以
                //如果是第一次启动的应用,则它的task中是没有设置了STARTING_WINDOW_SHOWN
                //的Activity的,则最终获取的prev为null
                ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
                //如果prev不为空则说明,此Activity所在的task中已经有
                //其他Activity添加过了启动窗口
                if (prev != null) {
  					//如果当前正在启动的Activity和已经添加了启动窗口的Activity
  					//不属于同一个task,则让prev为null
                    if (prev.getTaskRecord() != prevTask) {
                        prev = null;
                    }
                    //当前正在启动的Activity的nowVisible为true则prev置为null
                    else if (prev.nowVisible) {
                        prev = null;
                    }
                }
                //isTaskSwitch方法判断(r,focusedTopActivity)是否在同一个task
                //focusedTopActivity是当前系统最顶层的那个Activity
                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
            }
            ......
	}

 SHOW_APP_STARTING_PREVIEW 和doShow都为true,SHOW_APP_STARTING_PREVIEW这个值是定义在ActivityStack中写死了为true,所以控制启动窗口是否显示依靠doShow。

依靠doShow

Launcher启动的应用都会添加这个flag

launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                     | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

topRunningNonDelayedActivityLocked

这段代码将会遍历当前系统的所以task,从最顶层的task开始,再遍历每个task中的Activity,也从最顶层的Activity开始,直到返回满足(!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked())这几个条件的Activity。
 

   ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
   		//mTaskHistory存储了当前系统的所有task
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            //mActivities存储了task中的所有Activity
            final ArrayList<ActivityRecord> activities = task.mActivities;
            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                ActivityRecord r = activities.get(activityNdx);
                //如果此Activity不是正在结束中,并且不是延迟显示,并且
                //不为null,并且是要显示的,则返回此Activity
                if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
                    return r;
                }
            }
        }
        return null;
    }

showStartingWindow

	//按第一次启动应用则prev = null,newTask = true,taskSwitch = true
	void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
        showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */);
    }

    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
            boolean fromRecents) {
        	......
        				//添加启动窗口
        final boolean shown = addStartingWindow(packageName, theme,
                compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                allowTaskSnapshot(),
                mState.ordinal() >= RESUMED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
                fromRecents);
                //如果启动窗口成功显示则将状态置为STARTING_WINDOW_SHOWN
        if (shown) {
            mStartingWindowState = STARTING_WINDOW_SHOWN;
        }
    }
boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
        
        	......
        return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
                labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
                processRunning, allowTaskSnapshot, activityCreated, fromRecents);
    }
boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
        
        if (!okToDisplay()) {
            return false;
        }

        if (mStartingData != null) {
            return false;
        }
		//找到主窗口,等下分析
        final WindowState mainWin = findMainWindow();
        //如果能够找到一个mainWin,并且正在显示动画
        if (mainWin != null && mainWin.mWinAnimator.getShown()) {
            // 就说明当前App已经有一个可见窗口了,则不需要启动窗口
            return false;
        }
		//创建任务快照类型窗口,和启动窗口类似的作用
        final ActivityManager.TaskSnapshot snapshot =
                mWmService.mTaskSnapshotController.getSnapshot(
                        getTask().mTaskId, getTask().mUserId,
                        false /* restoreFromDisk */, false /* reducedResolution */);
                        //获取启动窗口类型以判断是否添加启动窗口,等下分析
        final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
                allowTaskSnapshot, activityCreated, fromRecents, snapshot);
		//如果是任务快照类型窗口则走createSnapshot,我们主要分析启动窗口,就不看了
        if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
            return createSnapshot(snapshot);
        }

        //启动窗口数据封装SplashScreenStartingData
        mStartingData = new SplashScreenStartingData(mWmService, pkg,
                theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                getMergedOverrideConfiguration());
       //如果获取的不是STARTING_WINDOW_TYPE_SPLASH_SCREEN则不会添加
       if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
            return false;
        }
         //准备添加启动窗口
        scheduleAddStartingWindow();
        return true;
    }

scheduleAddStartingWindow

void scheduleAddStartingWindow() {
        if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
            mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
        }
    }
private final Runnable mAddStartingWindow = new Runnable() {

        @Override
        public void run() {
        
            final StartingData startingData;
            synchronized (mWmService.mGlobalLock) {
                ......

            WindowManagerPolicy.StartingSurface surface = null;
            try {
                surface = startingData.createStartingSurface(AppWindowToken.this);
            } catch (Exception e) {
               
            }
           	......
        }
    };

StartingData.createStartingSurface

调用的StartingData实现类SplashScreenStartingData的方法

@Override
    StartingSurface createStartingSurface(AppWindowToken atoken) {
        return mService.mPolicy.addSplashScreen(atoken.token, mPkg, mTheme, mCompatInfo,
                mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
                mMergedOverrideConfiguration, atoken.getDisplayContent().getDisplayId());
    }

 直接调到WindowManagerPolicy的实现类PhoneWindowManager的addSplashScreen方法,此方法中会窗口启动窗口并添加到WMS中,启动窗口的创建和我们之前分析的Dialog的创建流程差不多,Android的窗口创建添加流程其实都差不多的,认真分析几个类型窗口的创建差不多就能明白窗口机制了。


 

@Override
    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
            int logo, int windowFlags, Configuration overrideConfig, int displayId) {
        if (!SHOW_SPLASH_SCREENS) {
            return null;
        }
        if (packageName == null) {
            return null;
        }

        WindowManager wm = null;
        View view = null;

        try {
            Context context = mContext;
            
            final Context displayContext = getDisplayContext(context, displayId);
            ......
			//创建PhoneWindow
            final PhoneWindow win = new PhoneWindow(context);
            //告诉PhoneWindow它是一个启动窗口类型
            win.setIsStartingWindow(true);

            CharSequence label = context.getResources().getText(labelRes, null);
            // Only change the accessibility title if the label is localized
            if (label != null) {
                win.setTitle(label, true);
            } else {
                win.setTitle(nonLocalizedLabel, false);
            }
			//设置窗口类型为TYPE_APPLICATION_STARTING
            win.setType(
                WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);

            synchronized (mWindowManagerFuncs.getWindowManagerLock()) {

                if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {
                    windowFlags |= FLAG_SHOW_WHEN_LOCKED;
                }
            }

			//设置各种flag,不能获取焦点,不能接收事件等
            win.setFlags(
                windowFlags|
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
                windowFlags|
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
			//设置图标
            win.setDefaultIcon(icon);
            win.setDefaultLogo(logo);
			//设置宽高为MATCH_PARENT
            win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                    WindowManager.LayoutParams.MATCH_PARENT);
			
            final WindowManager.LayoutParams params = win.getAttributes();
            //重点注意,启动窗口用的是当前这个Activity的tAppWindowToken
            params.token = appToken;
            params.packageName = packageName;
            params.windowAnimations = win.getWindowStyle().getResourceId(
                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
            params.privateFlags |=
                    WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
            params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;

            if (!compatInfo.supportsScreen()) {
                params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
            }
			//设置title为Splash Screen+包名,经常看log的应该对这句log很熟悉
			//当我们从launcher启动Activity时log中很容易发现这句话,这就是启动窗口
			//的标志
            params.setTitle("Splash Screen " + packageName);
            //此方法中会给启动窗口设置资源文件,并调用setContentView设置到
            //PhoneWindow的DecorView中
            addSplashscreenContent(win, context);

            wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
            view = win.getDecorView();
            //添加到WMS中
            wm.addView(view, params);

            // Only return the view if it was successfully added to the
            // window manager... which we can tell by it having a parent.
            return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
        } catch (WindowManager.BadTokenException e) {
            .....
        }
        return null;
    }

wm.addView(view, params);

public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {
            //Activity在启动时就创建了AppWindowToken,并将AppWindowToken
            //添加到了displayContent,前面说了启动窗口用的当前Activity的
            //AppWindowToken,所以此处token能够获取到
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
			if (token == null) {
			
						......
						
			}else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
				//当前Activity的AppWindowToken
                atoken = token.asAppWindowToken();
                if (atoken == null) {
                    	.....
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                  		.....
                    return WindowManagerGlobal.ADD_APP_EXITING;
                    //如果是启动窗口类型,并且当前Activity的AppWindowToken
                    //的成员变量startingWindow不为空则说明启动窗口是重复添加
                } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
                   			.....
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
				//创建启动窗口的WindowState,在WindowState构造函数中会
				//获取启动窗口的mBaseLayer用作Z-order排序
				final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
					//添加到mWindowMap
					mWindowMap.put(client.asBinder(), win);
					......
 			final AppWindowToken aToken = token.asAppWindowToken();
 			//这里才给当前Activity的AppWindowToken的startingWindow赋值,
 			//所以当aToken.startingWindow,不为空则一定是添加过了启动窗口
            if (type == TYPE_APPLICATION_STARTING && aToken != null) {
                aToken.startingWindow = win;
            }
			//将启动窗口添加到当前AppWindowToken中,并且会按照自定义规则排序
			win.mToken.addWindow(win);
			......
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值