Androidアプリケーション起動インタフェース分析(Starting Window)
アプリケーションを開くと、起動インタフェースが表示されます.異なる携帯電話では、起動インタフェースが異なり、一部の携帯電話では黒い画面が表示されます.出現時間の長さは起動の速度と関係があります.
起動画面は何ですか?
アプリケーションが起動すると、起動画面が表示されます.表示されている内容はどこに配置されていますか.一体何なのでしょうか?
構成内容
私たちはアプリケーションの構成リストAndroidManifestにいます.xmlのアプリケーション、activityノードで構成されているandroid:themeプロパティは、アプリケーションの起動時に構成情報を表示し、themeを構成しない場合はシステムのデフォルトプロパティを取得します.このプロパティの下には、次のノード構成が表示されます.
トピックのThemeでウィンドウに関するプロパティを設定できることがわかります.windowBackgroundノードでは、アプリケーションが起動するとそのノードの構成に関する情報が表示されます.
どうやって書きますか.
システムのデフォルトの起動インタフェースは一般的に白または黒の画面です.そのため、製品の色が白で、起動インタフェースが黒であれば、起動中に黒から白になるプロセスがあります.アプリケーションの起動が非常に速いと、キラキラして体験が悪くなります.アプリケーションが読み込みを開始したのはwindowBackgroundノードであることがわかりますが、ノードを直接複写すればいいです.
私达は1つのテーマをカスタマイズすることができて、windowBackgroundノードを复写して、このようにアプリケーションがスタートする时あなたの配置のインタフェースを表示することができて、このインタフェースはあなたのアプリケーションのlogoなどの情报を表示してブランドを强化することができて、最も主要なのはアプリケーションの体験を高めるので、しかし更に多くのメーカーはこれを1つの収入の源として、すべて広告をスタートさせました...
プロシージャの表示
起動画面を自分の画面に変更すれば、書き方を学ぶだけでOKだということは、上から知っています.しかし、レンガを運ぶ人として、私たちはそれを知るだけでなく、その理由を知る必要があります.そこで、コードの観点から彼の表示プロセスを見てみましょう.
ホームインタフェースのアプリケーションアイコンをクリックすると、ホームアプリケーションでstartActivity(intent)が呼び出されていることがわかります.intentにはパッケージ名とクラス名が対応しています.ここでのクラスは私たちの主activityです.主acitivityは主に以下の情報で区別されています.
Homeアプリケーションではどのようにしてクラス名を取得しますか?前のAndroidアプリケーションパッケージの解析プロセスの概要では、Android Manifestの解析プロセスについて説明しています.解析の内容はActivity Management ServiceでactionをMAIN、categoryはLAUNCHERのactivityでインタフェースに表示されます.startActivityにフォローしてみましょう
startActivityは2つのパラメータのstartActivityを呼び出し、その後最終的にstartActivity ForResult関数を呼び出し、ここでmInstrumentationが最終的に呼び出されたことがわかります.execStartActivity、execStartActivityに行って何が実行されているか見てみましょう.
まず、同じ名前のactivityが既に存在するか否かを判断し、requestCodeが0より大きいか否かを判断し、requestCodeが0より大きいことは、ActivityManagerNativeを呼び出す必要があることを示す.getDefault().startActivity,ActivityManagerNative.getDefault()で取得したものは何ですか?ActivityManagerNative.getDefault()のコードは次のとおりです.
getはServiceManagerからnameを取得して「activity」からサービスを取得し、IActivityManagerサービスを返します.プロセス間通信のクライアントであることがわかります.そのリモートは誰ですか.
前のAndroidアプリケーション管理サービス起動プロセスの概要(PackageManagerServices)では、initプロセスforkはzygoteプロセスであり、zygoteプロセスでSystemServerを呼び出し、SystemServer呼び出しstartBootstrapServicesはActivity ManagerServicesを含む多くのサービスを起動し、その後Activity Management Servicesを呼び出したと述べています.setSystemProcess関数は、サービスをServiceManagerに追加し、名前がacitivityのActivity Management Serviceを追加します.したがって、ここで取得したActivityManagerServiceのリモートエージェントは、ActivityManagerServiceで実行されます.
そこで、ActivityManagerServiceのstartActivityを見てみると、startActivityAsUserが直接呼び出され、startActivityAsUserでmStackSupervisorが呼び出されていることがわかります.startActivity MayWait、実際に実行されている場所を見てみましょうstartActivity MayWait:
一部のコードを削除し、startActivity Locked関数を呼び出し続けているのを見て、他のコードを無視して、startActivity UncheckedLocked関数を呼び出しているのを見て、この関数を見てみましょう.
まずlunchModeを取得します.これはあなたがactivity構成に関連しています.その後resultToがnullの場合、ホームイニシエータは戻り結果を待つ必要がないことを示します.そのためfindActivity Lockedが実行され、最初にここを実行すると空に戻り、その後newTaskがtrueに設定され、activityを処理するために新しいtaskが作成されることを示します.その後もtargetStackが実行する.startActivityLocked:
他の情報を無視し、一連の判断条件を経てmWindowManagerが実行されたことがわかります.setAppStartingWindow、曙光が目の前にあるような気がしますか?
関数実行の最後にADD_が送信されたことがわかります.STARTINGのメッセージ、handleMessageのADDへSTARTINGのブランチは呼び出しの内容を見て、ここのコードは貼っていないで、ここがWindowManagerPolicyを呼び出したことを見ます.addStartingWindowはviewを返します.WindowManagerPolicyとは何ですか.WindowManagerPolicyはinterfaceであることがわかりますが、その実装クラスは何ですか?実装クラスはPhoneWindowManagerなので、実際に呼び出されたのはPhoneWindowManagerです.addStartingWindow関数:
これはまず、表示ウィンドウとしてPhoneWindowを作成し、setisStartingWindowをtrueに設定し、starting Windowsであることを示し、その後windowのtitle,type,flagsなどの属性を設定し、layoutの属性の幅をMATCH_に設定した.PARENTは、その後ウィンドウに表示されるアニメーションを設定し、最後にgetDecorViewを呼び出して現在のviewを取得し、各PhoneWindowにはDecorViewがあり、DecorViewにはinstallDecorが呼び出され、installDecorはDecorViewを作成します.
他のコードを無視して、やっとWindowの解析を見ました.WindowBackgroundプロパティで、このプロパティをDecorViewのバックグラウンドに設定し、最後にWindowManagerServiceを取得して、そのviewをインタフェースに表示します.ここからstarting windowが表示されます.アプリケーション全体が本当に起動すると、そのviewにremoveされます.これにより、起動プロセス全体がシームレスに切り替えられます.
まとめ
多くのコードを無視していますが、上記のプロセスはアプリケーションの起動プロセスと見なすことができます.starting windowはその一歩にすぎません.起動プロセス全体では、上記よりも多くのコードが実行されますが、論理は一致しているので、ソースコードを自分で見ることができます.read the fuck source code!
起動画面は何ですか?
アプリケーションが起動すると、起動画面が表示されます.表示されている内容はどこに配置されていますか.一体何なのでしょうか?
構成内容
私たちはアプリケーションの構成リストAndroidManifestにいます.xmlのアプリケーション、activityノードで構成されているandroid:themeプロパティは、アプリケーションの起動時に構成情報を表示し、themeを構成しない場合はシステムのデフォルトプロパティを取得します.このプロパティの下には、次のノード構成が表示されます.
<style name="Theme">
<!-- Window attributes -->
<item name="windowBackground">@drawable/screen_background_selector_dark</item>
</style>
トピックのThemeでウィンドウに関するプロパティを設定できることがわかります.windowBackgroundノードでは、アプリケーションが起動するとそのノードの構成に関する情報が表示されます.
どうやって書きますか.
システムのデフォルトの起動インタフェースは一般的に白または黒の画面です.そのため、製品の色が白で、起動インタフェースが黒であれば、起動中に黒から白になるプロセスがあります.アプリケーションの起動が非常に速いと、キラキラして体験が悪くなります.アプリケーションが読み込みを開始したのはwindowBackgroundノードであることがわかりますが、ノードを直接複写すればいいです.
>
<style name="WelcomeTheme" parent="@android:style/Theme.Light.NoTitleBar.Fullscreen">
<item name="colorPrimary">@color/white</item>
<item name="colorPrimaryDark">@color/color_ffcccccc</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@drawable/splash_bg</item>
</style>
私达は1つのテーマをカスタマイズすることができて、windowBackgroundノードを复写して、このようにアプリケーションがスタートする时あなたの配置のインタフェースを表示することができて、このインタフェースはあなたのアプリケーションのlogoなどの情报を表示してブランドを强化することができて、最も主要なのはアプリケーションの体験を高めるので、しかし更に多くのメーカーはこれを1つの収入の源として、すべて広告をスタートさせました...
プロシージャの表示
起動画面を自分の画面に変更すれば、書き方を学ぶだけでOKだということは、上から知っています.しかし、レンガを運ぶ人として、私たちはそれを知るだけでなく、その理由を知る必要があります.そこで、コードの観点から彼の表示プロセスを見てみましょう.
ホームインタフェースのアプリケーションアイコンをクリックすると、ホームアプリケーションでstartActivity(intent)が呼び出されていることがわかります.intentにはパッケージ名とクラス名が対応しています.ここでのクラスは私たちの主activityです.主acitivityは主に以下の情報で区別されています.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Homeアプリケーションではどのようにしてクラス名を取得しますか?前のAndroidアプリケーションパッケージの解析プロセスの概要では、Android Manifestの解析プロセスについて説明しています.解析の内容はActivity Management ServiceでactionをMAIN、categoryはLAUNCHERのactivityでインタフェースに表示されます.startActivityにフォローしてみましょう
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
startActivityは2つのパラメータのstartActivityを呼び出し、その後最終的にstartActivity ForResult関数を呼び出し、ここでmInstrumentationが最終的に呼び出されたことがわかります.execStartActivity、execStartActivityに行って何が実行されているか見てみましょう.
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
まず、同じ名前のactivityが既に存在するか否かを判断し、requestCodeが0より大きいか否かを判断し、requestCodeが0より大きいことは、ActivityManagerNativeを呼び出す必要があることを示す.getDefault().startActivity,ActivityManagerNative.getDefault()で取得したものは何ですか?ActivityManagerNative.getDefault()のコードは次のとおりです.
/** * Retrieve the system's default/global activity manager. */
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
getはServiceManagerからnameを取得して「activity」からサービスを取得し、IActivityManagerサービスを返します.プロセス間通信のクライアントであることがわかります.そのリモートは誰ですか.
前のAndroidアプリケーション管理サービス起動プロセスの概要(PackageManagerServices)では、initプロセスforkはzygoteプロセスであり、zygoteプロセスでSystemServerを呼び出し、SystemServer呼び出しstartBootstrapServicesはActivity ManagerServicesを含む多くのサービスを起動し、その後Activity Management Servicesを呼び出したと述べています.setSystemProcess関数は、サービスをServiceManagerに追加し、名前がacitivityのActivity Management Serviceを追加します.したがって、ここで取得したActivityManagerServiceのリモートエージェントは、ActivityManagerServiceで実行されます.
そこで、ActivityManagerServiceのstartActivityを見てみると、startActivityAsUserが直接呼び出され、startActivityAsUserでmStackSupervisorが呼び出されていることがわかります.startActivity MayWait、実際に実行されている場所を見てみましょうstartActivity MayWait:
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
Bundle options, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask) {
..............
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage,
realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
componentSpecified, null, container, inTask);
.........
}
一部のコードを削除し、startActivity Locked関数を呼び出し続けているのを見て、他のコードを無視して、startActivity UncheckedLocked関数を呼び出しているのを見て、この関数を見てみましょう.
final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
boolean doResume, Bundle options, TaskRecord inTask) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
int launchFlags = intent.getFlags();
final boolean launchTaskBehind = r.mLaunchTaskBehind
&& !launchSingleTask && !launchSingleInstance
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
...............
boolean movedHome = false;
ActivityStack targetStack;
intent.setFlags(launchFlags);
final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
// us to still place it in a new task: multi task, always doc mode, or being asked to
// launch this as a new task behind the current one.
if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| launchSingleInstance || launchSingleTask) {
// If bring to front is requested, and no result is requested and we have not
// been given an explicit task to launch in to, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
if (inTask == null && r.resultTo == null) {
// unique task, so we do a special search.
ActivityRecord intentActivity = !launchSingleInstance ?
findTaskLocked(r) : findActivityLocked(intent, r.info);
............
}
}
...........
boolean newTask = false;
boolean keepCurTransition = false;
TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
sourceRecord.task : null;
// Should this be considered a new task?
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
targetStack = computeStackFocus(r, newTask);
targetStack.moveToFront("startingNewTask");
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
taskToAffiliate);
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
"Starting new activity " + r + " in new task " + r.task);
} else {
r.setTask(reuseTask, taskToAffiliate);
}
}
.........
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
}
ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
targetStack.mLastPausedActivity = null;
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
if (!launchTaskBehind) {
// Don't set focus on an activity that's going to the back.
mService.setFocusedActivityLocked(r, "startedActivity");
}
return ActivityManager.START_SUCCESS;
}
まずlunchModeを取得します.これはあなたがactivity構成に関連しています.その後resultToがnullの場合、ホームイニシエータは戻り結果を待つ必要がないことを示します.そのためfindActivity Lockedが実行され、最初にここを実行すると空に戻り、その後newTaskがtrueに設定され、activityを処理するために新しいtaskが作成されることを示します.その後もtargetStackが実行する.startActivityLocked:
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
TaskRecord rTask = r.task;
.......
r.putInHistory();
if (!isHomeStack() || numActivities() > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
// not currently running.
mWindowManager.addAppToken(task.mActivities.indexOf(r),
r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
boolean doShow = true;
if (newTask) {
// Even though this activity is starting fresh, we still need
// to reset it to make sure we apply affinities to move any
// existing activities from other tasks in to it.
// If the caller has requested that the target task be
// reset, then do so.
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
} else if (options != null && new ActivityOptions(options).getAnimationType()
== ActivityOptions.ANIM_SCENE_TRANSITION) {
doShow = false;
}
if (r.mLaunchTaskBehind) {
// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
// tell WindowManager that r is visible even though it is at the back of the stack.
mWindowManager.setAppVisibility(r.appToken, true);
ensureActivitiesVisibleLocked(null, 0);
} else if (SHOW_APP_STARTING_PREVIEW && doShow) {
// Figure out if we are transitioning from another activity that is
// "has the same starting icon" as the next one. This allows the
// window manager to keep the previous window it had previously
// created, if it still had one.
ActivityRecord prev = mResumedActivity;
if (prev != null) {
// We don't want to reuse the previous starting preview if:
// (1) The current activity is in a different task.
if (prev.task != r.task) {
prev = null;
}
// (2) The current activity is already displayed.
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);
r.mStartingWindowShown = true;
}
}
if (doResume) {
mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
}
}
他の情報を無視し、一連の判断条件を経てmWindowManagerが実行されたことがわかります.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) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppStartingWindow()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Creating StartingData");
wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
labelRes, icon, logo, windowFlags);
Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
// Note: we really want to do sendMessageAtFrontOfQueue() because we
// want to process the message ASAP, before any other queued
// messages.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
mH.sendMessageAtFrontOfQueue(m);
}
}
関数実行の最後にADD_が送信されたことがわかります.STARTINGのメッセージ、handleMessageのADDへSTARTINGのブランチは呼び出しの内容を見て、ここのコードは貼っていないで、ここがWindowManagerPolicyを呼び出したことを見ます.addStartingWindowはviewを返します.WindowManagerPolicyとは何ですか.WindowManagerPolicyはinterfaceであることがわかりますが、その実装クラスは何ですか?実装クラスはPhoneWindowManagerなので、実際に呼び出されたのはPhoneWindowManagerです.addStartingWindow関数:
@Override
public View addStartingWindow(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags) {
PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
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)) {
return null;
}
Resources r = context.getResources();
win.setTitle(r.getText(labelRes, nonLocalizedLabel));
win.setType(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
// Assumes it's safe to show starting windows of launched apps while
// the keyguard is being hidden. This is okay because starting windows never show
// secret information.
if (mKeyguardHidden) {
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
}
}
// Force the window flags: this is a fake window, so it is not really
// touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
// flag because we do know that the next window will take input
// focus, so we want to get the IME window up on top of us right away.
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);
params.setTitle("Starting " + packageName);
wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
view = win.getDecorView();
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 ? view : null;
これはまず、表示ウィンドウとしてPhoneWindowを作成し、setisStartingWindowをtrueに設定し、starting Windowsであることを示し、その後windowのtitle,type,flagsなどの属性を設定し、layoutの属性の幅をMATCH_に設定した.PARENTは、その後ウィンドウに表示されるアニメーションを設定し、最後にgetDecorViewを呼び出して現在のviewを取得し、各PhoneWindowにはDecorViewがあり、DecorViewにはinstallDecorが呼び出され、installDecorはDecorViewを作成します.
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
// The rest are only done if this window is not embedded; otherwise,
// the values are inherited from our container.
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mBackgroundResource == 0) {
mBackgroundResource = a.getResourceId(
R.styleable.Window_windowBackground, 0);
}
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
return contentParent;
}
他のコードを無視して、やっとWindowの解析を見ました.WindowBackgroundプロパティで、このプロパティをDecorViewのバックグラウンドに設定し、最後にWindowManagerServiceを取得して、そのviewをインタフェースに表示します.ここからstarting windowが表示されます.アプリケーション全体が本当に起動すると、そのviewにremoveされます.これにより、起動プロセス全体がシームレスに切り替えられます.
まとめ
多くのコードを無視していますが、上記のプロセスはアプリケーションの起動プロセスと見なすことができます.starting windowはその一歩にすぎません.起動プロセス全体では、上記よりも多くのコードが実行されますが、論理は一致しているので、ソースコードを自分で見ることができます.read the fuck source code!