一、分析文件介绍
- ActivityManagerService.java
- ActivityTaskManagerInternal.java
- ActivityTaskManagerService.java
- RootActivityContainer.java
- ActivityStartController.java
- LauncherProvider.java
- Launcher.java
- InvariantDeviceProfile.java
- MultiModeController.java
- LauncherProvider.java
- MultiModeUtilities.java
- LauncherSettingsExtension.java
- FeatureOption.java
- config_ext.xml
- device_profiles.xml
二、相关文件介绍
ActivityManagerService.java
Activity会调用startHomeActivityLocked方法,此方法会创建一个Intent,mTopAction和mTopData传给Intent,其中mTopAction为Intent.ACTION_MAIN,Intent的category为android.intent.category.Home。而Launcher的AndroidMainfest.xml文件里面给Launcher定义的category也是Home,根据匹配原则,这样就会启动这个Launcher。
源码位置:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { public ActivityTaskManagerInternal mAtmInternal; public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) { .... //启动Launcher mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); ..... } }
ActivityTaskManagerInternal.java和ActivityTaskManagerService.java
ActivityTaskManagerInternal.java是一个抽象类,里面定义了启动Luancher的方法,具体实现是由LocalService类,它定义在了ActivityTaskManagerService.java文件中
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
/** @return The intent used to launch the home activity. */ public abstract Intent getHomeIntent(); public abstract boolean startHomeActivity(int userId, String reason); public abstract boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey); /** Start home activities on all displays that support system decorations. */ public abstract boolean startHomeOnAllDisplays(int userId, String reason);
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
内部类LocalService
public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private ActivityStartController mActivityStartController; //Activity启动控制器 ActivityStartController getActivityStartController() { return mActivityStartController; } //注意看intent.addCategory(Intent.CATEGORY_HOME),这个代表的就是要启动Activity的意图,通常来说,整个系统的只会有一个应用会在清单文件中配置CATEGORY_HOME,如果配置了多个,系统在启动的时候就会要求用户手动去选择哪个作为启动应用,如果在系统设置应用中进行配置了,就会选择配置的那个应用启动。 Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } return intent; } final class LocalService extends ActivityTaskManagerInternal { RootActivityContainer mRootActivityContainer; //实际上调用了RootActivityContainer 中的方法 @Override public Intent getHomeIntent() { synchronized (mGlobalLock) { return ActivityTaskManagerService.this.getHomeIntent(); } } @Override public boolean startHomeActivity(int userId, String reason) { synchronized (mGlobalLock) { return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY); } } @Override public boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey) { synchronized (mGlobalLock) { return mRootActivityContainer.startHomeOnDisplay(userId, reason, displayId, allowInstrumenting, fromHomeKey); } } @Override public boolean startHomeOnAllDisplays(int userId, String reason) { synchronized (mGlobalLock) { return mRootActivityContainer.startHomeOnAllDisplays(userId, reason); } } } ... }
RootActivityContainer.java
frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
class RootActivityContainer extends ConfigurationContainer implements DisplayManager.DisplayListener { ActivityTaskManagerService mService; //在所有显示设备上启动Home boolean startHomeOnAllDisplays(int userId, String reason) { boolean homeStarted = false; for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { final int displayId = mActivityDisplays.get(i).mDisplayId; homeStarted |= startHomeOnDisplay(userId, reason, displayId); } return homeStarted; } boolean startHomeOnDisplay(int userId, String reason, int displayId) { return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */, false /* fromHomeKey */); } //这将在可以基于 display 的系统装饰的显示器上启动主页活动Id 默认显示器始终使用主要主页组件 //对于辅助显示,主页活动必须具有类别SECONDARY_HOME然后解析根据下面列出的优先级。 // -如果未设置默认主页,请始终使用配置中定义的辅助主页 //-使用当前选定的主要活动 //-使用与当前选定的主要家庭活动相同的套餐中的活动,如果有多个匹配的活动,请使用第一个活动。 //-使用配置中定义的辅助主页 boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey) { // Fallback to top focused display if the displayId is invalid. if (displayId == INVALID_DISPLAY) { displayId = getTopDisplayFocusedStack().mDisplayId; } Intent homeIntent = null; ActivityInfo aInfo = null; if (displayId == DEFAULT_DISPLAY) { homeIntent = mService.getHomeIntent(); //获取到需要启动的intent aInfo = resolveHomeActivity(userId, homeIntent); //解析出需要启动Activity的信息 } else if (shouldPlaceSecondaryHomeOnDisplay(displayId)) { Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId); aInfo = info.first; homeIntent = info.second; } if (aInfo == null || homeIntent == null) { return false; } if (!canStartHomeOnDisplay(aInfo, displayId, allowInstrumenting)) { return false; } // 更新home Intent homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); // Updates the extra information of the intent. if (fromHomeKey) { homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true); } // Update the reason for ANR debugging to verify if the user activity is the one that //开始启动Launcher final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( aInfo.applicationInfo.uid) + ":" + displayId; mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, displayId); return true; } }
ActivityStartController.java
frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
用于控制委派启动的Activity
public class ActivityStartController { void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); if (!ActivityRecord.isResolverActivity(aInfo.name)) { // The resolver activity shouldn't be put in home stack because when the foreground is // standard type activity, the resolver activity should be put on the top of current // foreground instead of bring home stack to front. options.setLaunchActivityType(ACTIVITY_TYPE_HOME); } options.setLaunchDisplayId(displayId); //此处执行启动Launcher,intent中包含了ACTION:"android.intent.action.MAIN"和category:"android.intent.category.HOME",这样就直接启动了拥有这两个属性的Activity mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) .setOutActivity(tmpOutRecord) .setCallingUid(0) .setActivityInfo(aInfo) .setActivityOptions(options.toBundle()) .execute(); mLastHomeActivityStartRecord = tmpOutRecord[0]; final ActivityDisplay display = mService.mRootActivityContainer.getActivityDisplay(displayId); final ActivityStack homeStack = display != null ? display.getHomeStack() : null; if (homeStack != null && homeStack.mInResumeTopActivity) { // If we are in resume section already, home activity will be initialized, but not // resumed (to avoid recursive resume) and will stay that way until something pokes it // again. We need to schedule another resume. mSupervisor.scheduleResumeTopActivities(); } } }
Laucnher3的AndroidManifest.xml
可以看到com.android.launcher3.Launcher这个Activity定义了作为Launcher的ACTION和CATEGORY
<activity android:name="com.android.launcher3.Launcher" android:launchMode="singleTask" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:windowSoftInputMode="adjustPan" android:screenOrientation="unspecified" android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" android:resizeableActivity="true" android:resumeWhilePausing="true" android:taskAffinity="" android:enabled="true"> <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"/> <category android:name="android.intent.category.LAUNCHER_APP" /> </intent-filter> <meta-data android:name="com.android.launcher3.grid.control" android:value="${packageName}.grid_control" /> </activity>
LauncherProvider.java
packages/apps/Launcher3/src/com/android/launcher3/LauncherProvider.java
public class LauncherProvider extends ContentProvider { public static final int SCHEMA_VERSION = 28; //数据库版本号 @Override public boolean onCreate() { if (LOGD) LogUtils.d(TAG, "Launcher process started"); mListenerHandler = new Handler(mListenerWrapper); // 内容提供程序在启动器主进程的整个持续时间内都存在,并且是要创建的第一个组件。 MainProcessInitializer.initialize(getContext().getApplicationContext()); return true; } }
Launcher.java
public class Launcher extends BaseDraggingActivity implements LauncherExterns, LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate, protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "bianjb onCreate"); RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER); //debug时启用严格模式 if (DEBUG_STRICT_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() // or .detectAll() for all detectable problems .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } TraceHelper.beginSection("Launcher-onCreate"); //Launcher监听器,展锐新加,用于监听并重新控制Launcher的行为 mAppMonitor = LauncherAppMonitor.getInstance(this); mAppMonitor.onLauncherPreCreate(this); }
LauncherAppMonitor.java(展锐)
packages/apps/Launcher3/src/com/sprd/ext/LauncherAppMonitor.java
InvariantDeviceProfile.java
packages/apps/Launcher3/src/com/android/launcher3/InvariantDeviceProfile.java
顾名思义,InvariantDeviceProfile是把显示相关的常量在这里初始化
调用构造方法
它有多个构造方法,调用的是下面这个
//程序加载时就创建了一个静态MainThreadInitializedObject对象,并创建InvariantDeviceProfile作为构造参数传给MainThreadInitializedObject。双冒号为java8 lambda表达式 public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE = new MainThreadInitializedObject<>(InvariantDeviceProfile::new); //gridname的key,通过它获取gridname值 public static final String KEY_IDP_GRID_NAME = "idp_grid_name"; private InvariantDeviceProfile(Context context) { Log.d(TAG, "bianjb Constructor1"); try{ throw new RuntimeException("打印栈调用测试~!"); }catch(Exception e){ e.printStackTrace(); } //初始化LauncherAppMonitor,下面有介绍 mMonitor = LauncherAppMonitor.getInstance(context); mIdpGridKey = MultiModeController.getKeyByMode(context, KEY_IDP_GRID_NAME); //调用初始化方法,getDefaultGridName获取是否有默认的GridName配置,即几x几 //Utilities.getPrefs(context).getString(mIdpGridKey, getDefaultGridName(context))最终是在sharepref中获取的值,key="idp_grid_name",value=3_by_3,对应的sharepref.xml见下面 initGrid(context, Utilities.getPrefs(context).getString(mIdpGridKey, getDefaultGridName(context))); //屏幕配置监听器 mConfigMonitor = new ConfigMonitor(context, APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess); // mOverlayMonitor = new OverlayMonitor(context); }
initGrid初始化显示选项参数常量
//参数gridName,获取指定gridName的显示选项,该值在device_profile.xml中定义 private String initGrid(Context context, String gridName) { //获取系统窗口管理器 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); //获取默认的Display,里面有屏幕相关的信息 //描述有关显示器的常规信息(如其大小、密度和字体缩放)的结构体 DisplayMetrics dm = new DisplayMetrics(); display.getMetrics(dm); Point smallestSize = new Point(); Point largestSize = new Point(); //返回应用程序在正常操作下可能遇到的显示大小范围,只要屏幕大小没有物理变化。这基本上是随着方向的变化而看到的大小,考虑到每次旋转中的任何屏幕装饰。例如,状态栏始终位于屏幕顶部,因此它将降低横向和纵向的高度,此处返回的最小高度将是两者中较小的高度。这旨在让应用程序了解它们在经历设备旋转时将遇到的大小范围,以便通过旋转提供稳定的 UI。此处的尺寸考虑了所有标准系统装饰,这些装饰减小了应用程序实际可用的尺寸:状态栏,导航栏,系统栏等。它没有考虑更多瞬态元素,如 IME 软键盘。 //例如在240*320的屏幕机器上,打印如下smallestSize=Point(240, 219), largestSize=Point(320, 299),也就是高度被状态栏占了一部分21像素,所以为219和299 display.getCurrentSizeRange(smallestSize, largestSize); //加载并解析device_profiles.xml文件,见下面"二" ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName); // This guarantees that width < height float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm); // 对DiaplayOption进行排序,依据从窗口管理器中获取的最小宽高和每个DisplayOption中的最小宽高进行Math.hypot计算,选出最合适的显示选项 Collections.sort(allOptions, (a, b) -> Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps), dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps))); //依据算法纠正DiaplayOption数据,返回最合适的DiaplayOption DisplayOption interpolatedDisplayOption = invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions); //上面已经做过排序,那么最接近的显示选项位于第0个位置 GridOption closestProfile = allOptions.get(0).grid; //获取行列,hotseat设置 numRows = closestProfile.numRows; //行数 numColumns = closestProfile.numColumns; //列数 numHotseatIcons = closestProfile.numHotseatIcons; //hotseat图标个数 defaultLayoutId = closestProfile.defaultLayoutId; //hotseat图标定义文件 demoModeLayoutId = closestProfile.demoModeLayoutId; numFolderRows = closestProfile.numFolderRows; //图标文件夹行数 numFolderColumns = closestProfile.numFolderColumns; //图标文件夹列数 mExtraAttrs = closestProfile.extraAttrs; //再次判断closestProfile是否与gridName对应 if (!closestProfile.name.equals(gridName)) { //如果gridname发生变化,则重新保存到shareprefs Utilities.getPrefs(context).edit() .putString(mIdpGridKey, closestProfile.name).apply(); } if (mMonitor.getSRController() != null) { mMonitor.getSRController().saveGridNameIntoStorage(context, closestProfile.name); } iconSize = interpolatedDisplayOption.iconSize; //图标尺寸 iconShapePath = getIconShapePath(context); // landscapeIconSize = interpolatedDisplayOption.landscapeIconSize; //横屏图标尺寸 iconBitmapSize = ResourceUtils.pxFromDp(iconSize, dm); iconTextSize = interpolatedDisplayOption.iconTextSize; //字体大小 fillResIconDpi = getLauncherIconDensity(iconBitmapSize); // 如果有APK包含grid参数设置,在这里可以应用,支持覆盖的参数有: numRows, numColumns, iconSize applyPartnerDeviceProfileOverrides(context, dm); Point realSize = new Point(); display.getRealSize(realSize); //获取屏幕实际尺寸,例如还是240*320的屏幕,获取到的是240*320 // 实际大小永远不会改变。小边和大边在任何方向上都将保持不变。 int smallSide = Math.min(realSize.x, realSize.y); int largeSide = Math.max(realSize.x, realSize.y); //横屏参数 landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize, largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */); //竖屏参数 portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize, smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */); FolderIconController fic = mMonitor.getFolderIconController(); if (fic != null) { fic.backupOriginalFolderRowAndColumns(numFolderRows, numFolderColumns); fic.updateFolderRowAndColumns(this); } //我们需要确保壁纸中有足够的额外空间用于预期的视差效果 if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) { defaultWallpaperSize = new Point( (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)), largeSide); } else { defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide); } ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName()); defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null); mDisplayOptionName = interpolatedDisplayOption.name; mGridName = closestProfile.name; return closestProfile.name; }
getPredefinedDeviceProfiles
//参数gridName,显示是要获取指定gridName的显示参数 static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) { ArrayList<DisplayOption> profiles = new ArrayList<>(); try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { final int depth = parser.getDepth(); int type; while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { if ((type == XmlPullParser.START_TAG) && GridOption.TAG_NAME.equals(parser.getName())) { GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser)); final int displayDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > displayDepth) && type != XmlPullParser.END_DOCUMENT) { if ((type == XmlPullParser.START_TAG) && "display-option".equals( parser.getName())) { profiles.add(new DisplayOption( gridOption, context, Xml.asAttributeSet(parser))); } } } } } catch (IOException|XmlPullParserException e) { throw new RuntimeException(e); } //根据gridName进行过滤 ArrayList<DisplayOption> filteredProfiles = new ArrayList<>(); if (!TextUtils.isEmpty(gridName)) { for (DisplayOption option : profiles) { if (gridName.equals(option.grid.name)) { filteredProfiles.add(option); } } } if (filteredProfiles.isEmpty()) { // No grid found, use the default options for (DisplayOption option : profiles) { if (option.canBeDefault) { filteredProfiles.add(option); } } } if (filteredProfiles.isEmpty()) { throw new RuntimeException("No display option with canBeDefault=true"); } return filteredProfiles; }