English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Ранее мы分析了 процесс установки приложений в системе Android 6.0 при запуске, после установки этих приложений приложение Launcher отвечает за их показ на рабочем столе.
1. Запуск Launcher AMS
Приложение Launcher запускается напрямую через функцию startHomeActivityLocked в методе systemReady AMS, ниже приведен код запуска Launcher через systemReady
startHomeActivityLocked(mCurrentUserId, "systemReady"); мы видим эту функцию, сначала вызывается метод getHomeIntent для получения Intent, затем вызывается функция resolveActivityInfo от PKMS для получения ActivityInfo, а также, если процесс не запущен, вызывается функция startHomeActivity от ActivityStackSupervisor
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 // сообщение об ошибке и не пытайтесь запустить что-либо. возвращает 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)); // Не делайте этого, если приложение homescreen в настоящее время // 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); } } возвращает true; }
Нам сначала посмотрим на функцию getHomeIntent.
Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } return intent; }
Затем давайте рассмотрим функцию startHomeActivity класса ActivityStackSupervisor, которая также использует 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) { // Если мы уже в разделе восстановления, домашняя активность будет инициализирована, но не // уже был восстановлен (чтобы избежать рекурсивного восстановления) и останется таким до тех пор, пока что-то не протолкнет его // снова. Нам нужно запланировать другой запуск. scheduleResumeTopActivities(); } }
第二节:Запуск Launcher
Давайте рассмотрим AndroidManifest.xml Launcher и посмотрим, что у его главного Activity есть категория android.intent.category.HOME
<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> ......
В функции onCreate класса Launcher.java был вызван функцией mModel.startLoader
protected void onCreate(Bundle savedInstanceState) { ...... if (!mRestoring) { if (sPausedFromUserAction) { // Если пользователь покидает лаунчер, то мы должны просто загружать элементы асинхронно когда // они возвращаются. mModel.startLoader(true, -1); } // Мы загружаем страницу синхронно только если пользователь поворачивает (или инициирует) // при изменении конфигурации) пока лаунчер на переднем плане mModel.startLoader(true, mWorkspace.getCurrentPage()); } } ......
Функция startLoader отправляет сообщение Runnable, посмотрим на её метод run
public void startLoader(boolean isLaunching, int synchronousBindPage) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); } // Удалите все отложенные bind-runnables из процесса синхронной загрузки // Мы должны сделать это до того, как запланировано любое загрузку/связывание ниже. 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 != null ? (!cbk.isAllAppsVisible()) : true; keep_running: { // Увеличить приоритет при запуске Home в первый раз, чтобы избежать // голодание при запуске. Глядеть на пустую домашнюю страницу не cool. synchronized (mLock) { if (DEBUG_LOADERS) Log.d(TAG, "Установка приоритета потока до " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } // Первый шаг. Сначала загрузите рабочую область, это необходимо, так как добавление приложений из // управляемый профайл во всех приложениях откладывается до onResume. См. http://b/17336902. if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "шаг 1: загрузка рабочей области"); loadAndBindWorkspace(); } Log.d(TAG, "шаг 1: особый: загрузка всех приложений"); loadAndBindAllApps(); }
Нам нужно сначала看一下 функцию loadAndBindAllApps, которая сначала входит в цикл while, затем вызывает функцию getActivityList из LauncherApps, а затем вызывает bindAllApplications из callbacks
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, "привязано " + added.size() + " приложений за " + (SystemClock.uptimeMillis() - t) + "ms"); } } Log.i(TAG, "не привязанные приложения: нет активности Launcher"); } } }); ......
Давайте сначала рассмотрим функцию getActivityList класса LauncherApps, которая сначала использует переменную члена 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) { throw new RuntimeException("Не удалось вызвать LauncherAppsService"); } if (activities == null) { return 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, "Возвращаем activity для профиля " + user + " : " + lai.getComponentName()); } lais.add(lai); } return lais; }
Его сервис является классом LauncherAppsImpl, который наследуется от ILauncherApps.Stub. Вот функция getLauncherActivities, которая также, вероятно, использует PKMS для получения связанных ResolveInfo Activity.
@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 */, user.getIdentifier()); return apps; } Binder.restoreCallingIdentity(ident); } }
В конце вызывается функция bindAllApplications из Launcher.java, где в этом функционале можно отображать все приложения системы на рабочем столе.
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); // We just post the call to setApps so the user sees the progress bar // disappear-- otherwise, it just looks like the progress bar froze // which doesn't look great mAppsCustomizeTabHost.post(setAllAppsRunnable); } // If we did not initialize the spinner in onCreate, then we can directly set the // list of applications without waiting for any progress bars views to be hidden. setAllAppsRunnable.run(); } }
3. Display application icons
Let's take a look at the onClick function of Launcher, when calling showWorkspace, it can display the icons of all applications.
public void onClick(View v) { // Make sure that rogue clicks don't get through while allapps is launching, or after the // view has detached (it's possible for this to happen if the view is removed mid touch). 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); // Показать строку поиска (анимировать только если мы показывали панель целевой точки в режиме spring loaded) // loaded mode) if (mSearchDropTargetBar != null) { mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode); } // Необходимо анимировать разделитель док-панели только если мы переходим из режима spring loaded showDockDivider(animated && wasInSpringLoadedMode); // Установить фокус на кнопку настройки приложений if (mAllAppsButton != null) { mAllAppsButton.requestFocus(); } } mWorkspace.flashScrollingIndicator(animated); // Изменить состояние *после* вызова всех кодов перехода mState = State.WORKSPACE; // Восстановить автоматическое обновление виджетов mUserPresent = true; updateRunning(); // Отправить событие доступности для объявления изменения контекста getWindow().getDecorView(); .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); }
Нажав на иконку приложения, в конечном итоге вызывается метод startActivitySafely из файла Launcher.java для запуска приложения. Здесь вызывается функция startActivity Activity.
boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; try { success = startActivity(v, intent, tag); } Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Не удалось запустить. tag=" + tag + " intent=" + intent, e); } return success; }
Это все содержимое статьи, надеюсь, это поможет вам в изучении, также希望大家多多支持呐喊教程。
Заявление: содержимое статьи взято из Интернета, авторские права принадлежат авторам, материал предоставлен пользователями Интернета, сайт не имеет права собственности, материал не был отредактирован вручную, и сайт не несет ответственности за связанные с этим法律责任. Если вы обнаружите материалы,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @) для сообщения о нарушении и предоставьте соответствующие доказательства. При подтверждении факта нарушения сайт незамедлительно удаляет涉嫌侵权的内容.