English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이전에는 Android6.0 시스템이 시작할 때 설치된 애플리케이션의 과정, 이 애플리케이션이 설치되면 Launcher 애플리케이션이 그들을 데스크톱에 표시하는 책임을 진습니다.
1. AMS가 Launcher를 시작
Launcher 애플리케이션은 AMS의 systemReady 메서드에서 직접 startHomeActivityLocked를 호출하여 시작되며, 아래는 systemReady가 Launcher를 시작하는 코드입니다.
startHomeActivityLocked(mCurrentUserId, "systemReady");我们来看下这个函数,先调用了getHomeIntent方法来获取Intent,然后也是调用resolveActivityInfo函数从PKMS获取ActivityInfo,接着当进程没有启动的话,调用ActivityStackSupervisor的startHomeActivity函数
boolean startHomeActivityLocked(int userId, String reason) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) { // We are running in factory test mode, but unable to find // the factory test app, so just sit around displaying the // error message and don't try to start anything. return false; } Intent intent = getHomeIntent();//获取intent ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//获取ActivityInfo if (aInfo != null) { intent.setComponent(new ComponentName( aInfo.applicationInfo.packageName, aInfo.name)); // Don't do this if the home app is currently being // instrumented. aInfo = new ActivityInfo(aInfo); aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId); ProcessRecord app = getProcessRecordLocked(aInfo.processName, aInfo.applicationInfo.uid, true); if (app == null || app.instrumentationClass == null) {//프로세스가 시작되지 않았습니다. EventLog.writeEvent(EventLogTags.AM_PROC_START,"AMS -> startHomeActivityLocked startHomeActivity then startActivityLock : "+ aInfo.processName); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mStackSupervisor.startHomeActivity(intent, aInfo, reason); } } return true; }
먼저 getHomeIntent 함수를 보겠습니다.
Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null63; Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } return intent; }
그런 다음 ActivityStackSupervisor의 startHomeActivity 함수를 보겠습니다. 이 함수는 startActivityLocked를 호출하여 Activity를 시작합니다. 이 함수에 대한 이전 블로그 분석에서는 설명하지 않았습니다.
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason); startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo, null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */, null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */, null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */, 0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */, false /* componentSpecified */, null /* outActivity */, null /* container */, null /* inTask */); if (inResumeTopActivity) { // 이미 실행 섹션에 있으면, 홈 액티비티가 초기화될 것입니다만 // resumed (재귀적 실행을 방지하기 위해) 그리고 이 상태가 유지될直到 something pokes it // 다시 실행할 필요가 있습니다. 또 다른 실행을 일정에 추가해야 합니다. scheduleResumeTopActivities(); } }
2. Launcher 시작
앞서Launcher의AndroidManifest.xml을 보면, 주 Activity가android.intent.category.HOME로 설정된category를 가지고 있다는 것을 확인할 수 있습니다.
<application android:name="com.android.launcher2.LauncherApplication" android:label="@string/application_name" android:icon="@mipmap/ic_launcher_home" android:hardwareAccelerated="true" android:largeHeap="@bool/config_largeHeap" android:supportsRtl="true"> <activity android:name="com.android.launcher2.Launcher" android:launchMode="singleTask"> android:clearTaskOnLaunch="true"> android:stateNotNeeded="true"> android:resumeWhilePausing="true"> android:theme="@style/Theme android:windowSoftInputMode="adjustPan"> android:screenOrientation="nosensor"> <intent-filter> <action android:name="android.intent.action.MAIN" /> /> <category android:name="android.intent.category.HOME" /> /> <category android:name="android.intent.category.DEFAULT" /> /> <category android:name="android.intent.category.MONKEY" />/> </intent-filter> </activity> ......
在Launcher.java의 onCreate 함수에서 mModel.startLoader 함수를 호출했습니다
protected void onCreate(Bundle savedInstanceState) { ...... if (!mRestoring) { if (sPausedFromUserAction) { // If the user leaves launcher, then we should just load items asynchronously when // they return. mModel.startLoader(true, -1); } // We only load the page synchronously if the user rotates (or triggers a // configuration change) while launcher is in the foreground mModel.startLoader(true, mWorkspace.getCurrentPage()); } } ......
startLoader 함수는 Runnable 메시지를 post하며, 그의 run 메서드를 확인해 봅시다.
public void startLoader(boolean isLaunching, int synchronousBindPage) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); } // 모든 미지정 바인딩을 지우세요-동기화 로드 과정에서의 런넬 // 이를 전에 로드해야 합니다./바인딩은 아래에 예약되었습니다. mDeferredBindRunnables.clear(); // 그것이 아무 일도 하지 않을 것이라면 스레드를 시작하는 것을 고려하지 마세요. if (mCallbacks != null && mCallbacks.get() != null) { // 이미 실행 중이면, 그를 중지하도록 지시합니다. // 또한, 이미 실행 중이면 isLaunching를 하향 조정하지 마세요. isLaunching = isLaunching || stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp, isLaunching); if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } } } }
그의 run 메서드에서 loadAndBindAllApps 함수를 호출하고, loadAndBindAllApps 함수에서는 또 loadAllAppsByBatch 함수를 호출합니다.
public void run() { synchronized (mLock) { mIsLoaderTaskRunning = true; } final Callbacks cbk = mCallbacks.get() final boolean loadWorkspaceFirst = cbk != null63; (!cbk.isAllAppsVisible()) : true; keep_running: { // Elevate priority when Home launches for the first time to avoid // starving at boot time. Staring at a blank home is not cool. synchronized (mLock) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } // First step. Load workspace first, this is necessary since adding of apps from // managed profile in all apps is deferred until onResume. See http://b/17336902. if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "step" 1: loading workspace"); loadAndBindWorkspace(); } Log.d(TAG, "step" 1: special: loading all apps"); loadAndBindAllApps(); }
우리는 먼저 loadAndBindAllApps 함수를 보도록 하겠습니다. 이 함수는 먼저 while 루프에 진입한 후 LauncherApps의 getActivityList 함수를 호출하고, 그 다음 callbacks의 bindAllApplications을 호출합니다.
private void loadAllAppsByBatch() {}} final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; ...... mBgAllAppsList.clear(); final int profileCount = profiles.size(); for (int p = 0; p < profileCount; p++) { ...... while (i < N && !mStopped) { if (i == 0) { final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; apps = mLauncherApps.getActivityList(null, user); ...... mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (firstProfile) { callbacks.bindAllApplications(added); } callbacks.bindAppsAdded(added); } if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - t) + "ms" } } Log.i(TAG, "not binding apps: no Launcher activity"); } } }); ......
우리는 LauncherApps의 getActivityList 함수를 보겠습니다. 이 함수는 먼저 mService 멤버 변수를 사용하여 getLauncherActivities 함수를 호출하여 list<ResolveInfo>를 얻고, 그것을 ArrayList<LauncherActivityInfo>에 포장합니다.
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { List<ResolveInfo> activities = null; try { activities = mService.getLauncherActivities(packageName, user); } catch (RemoteException re) { RuntimeException("LauncherAppsService 호출 실패)을 throw합니다; } if (activities == null) { Collections.EMPTY_LIST를 반환합니다; } ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>(); final int count = activities.size(); for (int i = 0; i < count; i++) { ResolveInfo ri = activities.get(i); long firstInstallTime = 0; try { firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName, PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime; } catch (NameNotFoundException nnfe) { // 죄송합니다, 패키지를 찾을 수 없습니다 } LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user, firstInstallTime); if (DEBUG) { Log.v(TAG, "프로필에 대한 반환하는 액티비티 " + user + ": " + lai.getComponentName()); } lais.add(lai); } lais를 반환합니다; }
그 service는 class LauncherAppsImpl extends ILauncherApps.Stub입니다. 여기서 getLauncherActivities 함수는 PKMS를 통해 관련 Activity의 ResolveInfo를 가져오도록 합니다.
@Override public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); if (!isUserEnabled(user)) { return new ArrayList<ResolveInfo>(); } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mainIntent.setPackage(packageName); long ident = Binder.clearCallingIdentity(); try { List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */, return apps; finally { } Binder.restoreCallingIdentity(ident); } }
최종적으로 Launcher.java의 bindAllApplications 함수를 호출하고, 이 함수에서 바탕 화면에 시스템에서 모든 애플리케이션을 표시할 수 있습니다.
public void bindAllApplications(final ArrayList<ApplicationInfo> apps) { Runnable setAllAppsRunnable = new Runnable() { public void run() { if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); } } ; // 전체 진행 표시줄을 완전히 제거하세요; GONE로 만들 수도 있습니다 // 하지만 사용되지 않을 것이라는 것을 알면 제거하는 것이 더 좋습니다. View progressBar = mAppsCustomizeTabHost. findViewById(R.id.apps_customize_progress_bar); if (progressBar != null) { ((ViewGroup)progressBar.getParent()).removeView(progressBar); // 우리는 setApps 호출을 게시하여 사용자가 진행 표시줄을 볼 수 있도록 합니다 // 사라지게 합니다-- 그렇지 않으면, 진행 표시줄이 멈춘 것처럼 보입니다 // 그러나 좋지 않게 보입니다 mAppsCustomizeTabHost.post(setAllAppsRunnable); } // onCreate에서 스피너를 초기화하지 않았다면, 직접 설정할 수 있습니다. // 진행 표시줄 뷰가 숨겨지기 전에 대기하지 않고 애플리케이션 목록을 설정합니다. setAllAppsRunnable.run(); } }
3. 애플리케이션 아이콘 표시
Launcher의 onClick 함수를 다시 보겠습니다. showWorkspace를 호출하면 모든 애플리케이션 아이콘을 표시할 수 있습니다.
public void onClick(View v) { // allapps가 실행 중이거나, 실행 후에도 기어스 클릭이 통과하지 않도록 하세요. // 뷰가 분리되었음을 확인하세요 (터치 중에 뷰가 제거되면 이 일어날 수 있습니다). if (v.getWindowToken() == null) { return; } if (!mWorkspace.isFinishedSwitchingState()) { return; } Object tag = v.getTag(); if (tag instanceof ShortcutInfo) { // 오픈 단축키 final Intent intent = ((ShortcutInfo) tag).intent; int[] pos = new int[2]); v.getLocationOnScreen(pos); intent.setSourceBounds(new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight())); boolean success = startActivitySafely(v, intent, tag); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; mWaitingForResume.setStayPressed(true); } } if (v instanceof FolderIcon) { FolderIcon fi = (FolderIcon) v; handleFolderClick(fi); } } if (isAllAppsVisible()) { showWorkspace(true); } onClickAllAppsButton(v); } } }
showWorkspace에서 모든 아이콘을 표시합니다
void showWorkspace(boolean animated, Runnable onCompleteRunnable) { if (mState != State.WORKSPACE) { boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED); mWorkspace.setVisibility(View.VISIBLE); hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable); // Show the search bar (only animate if we were showing the drop target bar in spring // loaded mode) if (mSearchDropTargetBar != null) { mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode); } // We only need to animate in the dock divider if we're going from spring loaded mode showDockDivider(animated && wasInSpringLoadedMode); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { mAllAppsButton.requestFocus(); } } mWorkspace.flashScrollingIndicator(animated); // Change the state *after* we've called all the transition code mState = State.WORKSPACE; // Resume the auto-advance of widgets mUserPresent = true; updateRunning(); // Send an accessibility event to announce the context change getWindow().getDecorView() .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); }
알림: 애플리케이션 아이콘을 클릭하면, 결국 Launcher.java의 startActivitySafely를 호출하여 애플리케이션을 시작합니다. 여기서 호출되는 startActivity는 Activity의 startActivity 함수입니다.
boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; try { success = startActivity(v, intent, tag); } catch (ActivityNotFoundException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e); } return success; }
이것이 본 문서의 모든 내용입니다. 많은 도움이 되길 바라며, 많은 지지를 부탁드립니다.呐喊 교재를 많이 지지해 주세요.
선언: 본 문서의 내용은 인터넷에서 가져왔으며, 저작권은 원 저자에게 있으며, 인터넷 사용자가 자발적으로 기여하고 업로드한 내용입니다. 본 웹사이트는 소유권을 가지지 않으며, 인공적인 편집 처리를 하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해가 의심되는 내용을 발견한 경우, notice#w 이메일 주소로 이메일을 보내 주세요.3codebox.com(이메일을 보내는 경우, #을 @으로 변경하십시오. 신고를 하시고 관련 증거를 제공하시면, 사실 여부를 확인한 후 즉시 해당 저작권 침해 내용을 삭제하겠습니다。)