Launcher启动过程

Launcher启动过程

八归少年 188 2024-02-24

Android 系统启动系列文章:

首语

系统启动的最后一步就是启动一个程序来显示系统中已经安装的应用程序,这个程序就是Launcher,Launcher在启动过程中会请求PackageManagerService返回系统中已经安装的应用程序信息,并将这些信息封装成一个快捷图标显示在系统屏幕上,这样用户就可以通过点击这些快捷图标来启动相应的应用程序。

Launcher启动过程

SystemServer进程在启动的过程中会启动PackageManagerService,PackageManagerService启动后会将系统中的应用程序安装完成,在此前启动的AMS会将Launcher启动。

启动Launcher的入口为AMS的systemReady方法,它在SystemServer的startOtherServices方法中调用。

源码路径:frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    ...
    // We now tell the activity manager it is okay to run third party
    // code.  It will call back into us once it has gotten to the state
    // where third party code can really run (but before it has actually
    // started launching the initial applications), for us to complete our
    // initialization.
    //启动Launcher入口
    mActivityManagerService.systemReady(() -> {
            Slog.i(TAG, "Making services ready");
            t.traceBegin("StartActivityManagerReadyPhase");
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
    ...
}

systemReady方法中调用了ActivityTaskManagerInternal类的startHomeOnAllDisplays方法,ActivityTaskManagerInternal它是一个抽象类,startHomeOnAllDisplays方法的实现都是在ActivityTaskManagerService中,它继承于ActivityTaskManagerInternal。

源码路径:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
     ...
     mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 }
public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
    synchronized(this) {
            if (mSystemReady) {
                // If we're done calling all the receivers, run the next "boot phase" passed in
                // by the SystemServer
                if (goingCallback != null) {
                    goingCallback.run();
                }
                t.traceEnd(); // PhaseActivityManagerReady
                return;
            }
    ...
    if (bootingSystemUser) {
                t.traceBegin("startHomeOnAllDisplays");
                mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
                t.traceEnd();
    }
    ...
}

ActivityTaskManagerInternal是在start方法中添加的,startHomeOnAllDisplays方法中调用RootWindowContainer的startHomeOnAllDisplays方法。

源码路径:
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

private void start() {
    //添加ActivityTaskManagerInternal
    LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@Override
public boolean startHomeOnAllDisplays(int userId, String reason) {
    synchronized (mGlobalLock) {
        return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
    }
}

RootWindowContainer类的startHomeOnAllDisplays方法核心实现在startHomeOnTaskDisplayArea方法中,通过ActivityTaskManagerService类的getHomeIntent方法获取启动信息。

源码路径:
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

boolean startHomeOnAllDisplays(int userId, String reason) {
        boolean homeStarted = false;
        for (int i = getChildCount() - 1; i >= 0; i--) {
            final int displayId = getChildAt(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 */);
    }

boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
            boolean fromHomeKey) {
        // Fallback to top focused display or default display if the displayId is invalid.
        if (displayId == INVALID_DISPLAY) {
            final Task rootTask = getTopDisplayFocusedRootTask();
            displayId = rootTask != null ? rootTask.getDisplayId() : DEFAULT_DISPLAY;
        }

        final DisplayContent display = getDisplayContent(displayId);
        return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
                        result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                                allowInstrumenting, fromHomeKey),
                false /* initValue */);
    }
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
    if (taskDisplayArea == getDefaultTaskDisplayArea()) {
        	//获取启动信息
            homeIntent = mService.getHomeIntent();
            aInfo = resolveHomeActivity(userId, homeIntent);
        } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
            Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);
            aInfo = info.first;
            homeIntent = info.second;
        }
        if (aInfo == null || homeIntent == null) {
            return false;
        }

        if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {
            return false;
        }
    final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
                aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
    //启动Launcher
    mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                taskDisplayArea);
}

getHomeIntent的action为mTopAction=Intent.ACTION_MAIN。FactoryTest代表系统的运行模式,共有三种,非工厂模式(FACTORY_TEST_OFF),低级工厂模式(FACTORY_TEST_LOW_LEVEL),高级工厂模式(FACTORY_TEST_HIGH_LEVEL )。如果非低级工厂模式则添加category为Intent.CATEGORY_HOME,标志着是设备启动的第一个Activity。

前面提到getHomeIntent方法返回的homeIntent传递到startHomeActivity方法中。startHomeActivity方法实现定义在ActivityStartController类中。

源码路径:
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

String mTopAction = Intent.ACTION_MAIN;
Intent getHomeIntent() {
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    	//FACTORY_TEST_LOW_LEVEL 低级工厂模式
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
}
ActivityStartController getActivityStartController() {
    return mActivityStartController;
}

到这里就和StartActivity逻辑类似了。启动了一个Activity,action为Intent.ACTION_MAIN,category为Intent.CATEGORY_HOME,是系统启动的第一个Activity。

源码路径:
frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
            TaskDisplayArea taskDisplayArea) {
    ...
    if (rootHomeTask.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();
        }
}

这个被启动的应用程序就是Launcher。Launcher模块的源码路径为:packages/apps/Launcher3。查看Launcher的AndroidManifest.xml可以发现有一个Activity—com.android.launcher3.Launcher,action为"android.intent.action.MAIN",category为"android.intent.category.HOME",到这里Launcher就完成了启动。

源码路径:packages/apps/Launcher3/AndroidManifest.xml

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher3">
    <uses-sdk android:targetSdkVersion="33" android:minSdkVersion="26"/>
    <!--
    Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
    Refer comments around specific entries on how to extend individual components.
    -->

    <application
        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
        android:fullBackupOnly="true"
        android:fullBackupContent="@xml/backupscheme"
        android:hardwareAccelerated="true"
        android:debuggable="true"
        android:icon="@drawable/ic_launcher_home"
        android:label="@string/derived_app_name"
        android:theme="@style/AppTheme"
        android:largeHeap="@bool/config_largeHeap"
        android:restoreAnyVersion="true"
        android:supportsRtl="true" >

        <!--
        Main launcher activity. When extending only change the name, and keep all the
        attributes and intent filters the same
        -->
        <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:exported="true"
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.SHOW_WORK_APPS" />
                <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>

    </application>
</manifest>

应用图标显示过程

Launcher启动完成后会做很多工作,作为桌面它会显示应用程序图标,应用程序图标是用户进入应用程序的入口,那么有必要了解下Launcher是如何显示应用程序图标的。

首先从启动的Activity Launcher看起,在onCreate方法中通过LauncherAppState获取了LauncherModelmModel实例,然后通过addCallbacksAndLoad方法添加回调来接收更新。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

import com.android.launcher3.model.BgDataModel.Callbacks;
public class Launcher extends StatefulActivity<LauncherState>
        implements LauncherExterns, Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
        PluginListener<LauncherOverlayPlugin>, LauncherOverlayCallbacks {
            
    private LauncherModel Model; 
            
	@Override
	@TargetApi(Build.VERSION_CODES.S)
	protected void onCreate(Bundle savedInstanceState) {
     	 ...
    	 LauncherAppState app = LauncherAppState.getInstance(this);
    	 mOldConfig = new Configuration(getResources().getConfiguration());
    	 mModel = app.getModel();
   	  ...
   	  if (!mModel.addCallbacksAndLoad(this)) {
            if (!internalStateHandled) {
                Log.d(BAD_STATE, "Launcher onCreate not binding sync, prevent drawing");
                // If we are not binding synchronously, pause drawing until initial bind complete,
                // so that the system could continue to show the device loading prompt
                mOnInitialBindListener = Boolean.FALSE::booleanValue;
            }
        }
	}
}

然后执行startLoader方法,mLoaderTask如果不为空则通过stopLoader方法LoaderTask,LoaderTask类实现了Runnable接口,MODEL_EXECUTOR执行mLoaderTask任务。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java

import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;

public boolean addCallbacksAndLoad(Callbacks callbacks) {
        synchronized (mLock) {
            addCallbacks(callbacks);
            return startLoader(new Callbacks[] { callbacks });
        }
}
public void addCallbacks(Callbacks callbacks) {
            mCallbacksList.add(callbacks)
}
public Callbacks[] getCallbacks() {
        return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
}
private boolean startLoader(Callbacks[] newCallbacks) {
    	   ...
           // If there is already one running, tell it to stop.
           boolean wasRunning = stopLoader();
		   final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
           LoaderResults loaderResults = new LoaderResults(
                        mApp, mBgDataModel, mBgAllAppsList, callbacksList);
           if (bindDirectly) {
                 // Divide the set of loaded items into those that we are binding synchronously,
                 // and everything else that is to be bound normally (asynchronously).
                 loaderResults.bindWorkspace(bindAllCallbacks);
                 // For now, continue posting the binding of AllApps as there are other
                 // issues that arise from that.
                 loaderResults.bindAllApps();
                 loaderResults.bindDeepShortcuts();
                 loaderResults.bindWidgets();
                 return true;
             } else {
                 stopLoader();
                 mLoaderTask = new LoaderTask(
                         mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
                 // Always post the loader task, instead of running directly
                 // (even on same thread) so that we exit any nested synchronized blocks
                 MODEL_EXECUTOR.post(mLoaderTask);
             }    
}
private boolean stopLoader() {
        synchronized (mLock) {
            LoaderTask oldTask = mLoaderTask;
            mLoaderTask = null;
            if (oldTask != null) {
                oldTask.stopLocked();
                return true;
            }
            return false;
        }
    }
 public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
        return new LoaderTransaction(task);
    }
public class LoaderTransaction implements AutoCloseable {
        private final LoaderTask mTask;
        private LoaderTransaction(LoaderTask task) throws CancellationException {
            synchronized (mLock) {
                mTask = task;
                mIsLoaderTaskRunning = true;
                mModelLoaded = false;
            }
        }
        public void commit() {
            synchronized (mLock) {
                // Everything loaded bind the data.
                mModelLoaded = true;
            }
        }
    }

run方法中首先调用LauncherModel的beginLoader方法创建LoaderTransaction。Launcher是用工作区的形式来显示系统安装的应用程序的快捷图标,每一个工作区用来描述一个抽象桌面,它由n个屏幕组成,每个屏幕分为n个单元格,每个单元格用来显示一个应用程序的快捷图标。loadWorkspace方法去加载工作区信息,bindWorkspace方法去绑定工作区信息,loadAllApps方法加载系统已经安装的应用程序信息,bindAllApps方法去绑定索引应用程序信息。LoaderResults继承与BaseLoaderResults,bindAllApps方法实现在BaseLoaderResults类中。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java

public class LoaderTask implements Runnable {
    private final LoaderResults mResults;
	public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
            ModelDelegate modelDelegate, LoaderResults results) {
        mApp = app;
        mBgAllAppsList = bgAllAppsList;
        mBgDataModel = dataModel;
        mModelDelegate = modelDelegate;
        mResults = results;

        mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
        mUserManager = mApp.getContext().getSystemService(UserManager.class);
        mUserCache = UserCache.INSTANCE.get(mApp.getContext());
        mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
        mIconCache = mApp.getIconCache();
	}
	public void run() {
	 try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
    	 try {
             //加载工作区信息
              loadWorkspace(allShortcuts, memoryLogger);
         } finally {
               Trace.endSection();
      	}
         //绑定工作区信息
         mResults.bindWorkspace(true /* incrementBindId */);
		 // Notify the installer packages of packages with active installs on the first screen.
         sendFirstScreenActiveInstallsBroadcast();
    	 try {
             //加载系统已经安装的应用程序信息
          	allActivityList = loadAllApps();
         } finally {
             Trace.endSection();
         }
         mResults.bindAllApps();
         mResults.bindDeepShortcuts();
         mResults.bindWidgets();
         transaction.commit();
    }
} 

bindAllApps主要做了通过遍历mCallbacksList来执行bindAllApplications方法,从前面代码我们就知道addCallbacksAndLoad参数为Callbacks,Launcher类中传入this,也就是代表Callbacks指向Launcher,那我们去Launcher类里看bindAllApplications的实现。

源码路径:
packages/apps/Launcher3/src_shortcuts_overrides/com/android/launcher3/model/BaseLoaderResults.java

public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
            AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) {
        mUiExecutor = uiExecutor;
        mApp = app;
        mBgDataModel = dataModel;
        mBgAllAppsList = allAppsList;
        mCallbacksList = callbacksList;
    }
public void bindAllApps() {
        // shallow copy
        AppInfo[] apps = mBgAllAppsList.copyData();
        int flags = mBgAllAppsList.getFlags();
        executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
    }
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
        executor.execute(() -> {
            if (mMyBindingId != mBgDataModel.lastBindId) {
                Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
                return;
            }
            for (Callbacks cb : mCallbacksList) {
                task.execute(cb);
            }
        });
    }

bindAllApplications方法中调用AllAppsStore类的setApps方法,apps就是应用信息列表。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

ActivityAllAppsContainerView<Launcher> mAppsView;

@Override
    @TargetApi(Build.VERSION_CODES.S)
        public void bindAllApplications(AppInfo[] apps, int flags) {
        mAppsView.getAppsStore().setApps(apps, flags);
        PopupContainerWithArrow.dismissInvalidPopup(this);
        if (Utilities.ATLEAST_S) {
            Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
                    DISPLAY_ALL_APPS_TRACE_COOKIE);
        }
    }

ActivityAllAppsContainerView类继承于BaseAllAppsContainerView,调用getAppsStore方法获取AllAppsStore实例。

源码路径:
packages/apps/Launcher3/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java

public AllAppsStore getAppsStore() {
        return mAllAppsStore;
}
private void onAppsUpdated() {
        mHasWorkApps = Stream.of(mAllAppsStore.getApps()).anyMatch(mWorkManager.getMatcher());
        if (!isSearching()) {
            rebindAdapters();
            if (mHasWorkApps) {
                mWorkManager.reset();
            }
        }
    }

setApps方法就去调用notifyUpdate方法通知UI更新。监听实现方法onAppsUpdated中在BaseAllAppsContainerView类中,调用rebindAdapters来更新UI。rebindAdapters在ActivityAllAppsContainerView中有覆盖实现。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsStore.java

public void setApps(AppInfo[] apps, int flags) {
        mApps = apps;
        mModelFlags = flags;
        notifyUpdate();
    }
private void notifyUpdate() {
        if (mDeferUpdatesFlags != 0) {
            mUpdatePending = true;
            return;
        }
        for (OnUpdateListener listener : mUpdateListeners) {
            listener.onAppsUpdated();
        }
    }

rebindAdapters方法进行数据差异更新,Recycleview为AllAppsRecyclerView。AdapterHolder定义了三种数据类型,SEARCH表示桌面搜索数据,WORK表示在Android for work PO模式下的数据,MAIN就是一般情况下的数据。mAdapter为AllAppsGridAdapter。至此应用程序快捷图标就显示在屏幕上了。

源码路径:
packages/apps/Launcher3/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java

protected BaseAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
     mAllAppsStore.addUpdateListener(this::onAppsUpdated);
}
	@Override
    protected void rebindAdapters(boolean force) {
        super.rebindAdapters(force);
        if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
                || getMainAdapterProvider().getDecorator() == null) {
            return;
        }

        RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
        mAH.stream()
                .map(adapterHolder -> adapterHolder.mRecyclerView)
                .filter(Objects::nonNull)
                .forEach(v -> {
                    v.removeItemDecoration(decoration); // Remove in case it is already added.
                    v.addItemDecoration(decoration);
                });
    }
//packages/apps/Launcher3/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
 public class AdapterHolder {
        public static final int MAIN = 0;
        public static final int WORK = 1;
        public static final int SEARCH = 2;

        private final int mType;
        public final BaseAllAppsAdapter<T> mAdapter;
        final RecyclerView.LayoutManager mLayoutManager;
        final AlphabeticalAppsList<T> mAppsList;
        final Rect mPadding = new Rect();
        AllAppsRecyclerView mRecyclerView;

        AdapterHolder(int type) {
            mType = type;
            mAppsList = new AlphabeticalAppsList<>(mActivityContext,
                    isSearch() ? null : mAllAppsStore,
                    isWork() ? mWorkManager.getAdapterProvider() : null);

            BaseAdapterProvider[] adapterProviders =
                    isWork() ? new BaseAdapterProvider[]{mMainAdapterProvider,
                            mWorkManager.getAdapterProvider()}
                            : new BaseAdapterProvider[]{mMainAdapterProvider};

            mAdapter = createAdapter(mAppsList, adapterProviders);
            mAppsList.setAdapter(mAdapter);
            mLayoutManager = mAdapter.getLayoutManager();
        }
	@Override
    protected BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> appsList,
            BaseAdapterProvider[] adapterProviders) {
        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
                adapterProviders);
    }

点击桌面图标启动应用过程

AllAppsGridAdapter的核心实现在基类BaseAllAppsAdapter中,viewType是在mApps中设置的,实现在AlphabeticalAppsList的updateAdapterItems方法中。点击事件为mOnIconClickListener,实现在Launch的getItemOnClickListener中。最终调用startActivitySafely方法来启动应用程序。

源码路径:
packages/apps/Launcher3/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java

public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
            AlphabeticalAppsList<T> apps, BaseAdapterProvider[] adapterProviders) {
        Resources res = activityContext.getResources();
        mActivityContext = activityContext;
        mApps = apps;
        mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
        mLayoutInflater = inflater;

        mOnIconClickListener = mActivityContext.getItemOnClickListener();

        mAdapterProviders = adapterProviders;
        mExtraHeight = res.getDimensionPixelSize(R.dimen.all_apps_height_extra);
    }	
	@Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case VIEW_TYPE_ICON:
                int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
                        : R.layout.all_apps_icon_twoline;
                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                        layout, parent, false);
                icon.setLongPressTimeoutFactor(1f);
                icon.setOnFocusChangeListener(mIconFocusListener);
                icon.setOnClickListener(mOnIconClickListener);
                icon.setOnLongClickListener(mOnIconLongClickListener);
                // Ensure the all apps icon height matches the workspace icons in portrait mode.
                icon.getLayoutParams().height =
                        mActivityContext.getDeviceProfile().allAppsCellHeightPx;
                if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
                    icon.getLayoutParams().height += mExtraHeight;
                }
                return new ViewHolder(icon);
            case VIEW_TYPE_EMPTY_SEARCH:
                return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
                        parent, false));
            case VIEW_TYPE_SEARCH_MARKET:
                View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
                        parent, false);
                searchMarketView.setOnClickListener(mMarketSearchClickListener);
                return new ViewHolder(searchMarketView);
            case VIEW_TYPE_ALL_APPS_DIVIDER:
                return new ViewHolder(mLayoutInflater.inflate(
                        R.layout.all_apps_divider, parent, false));
            default:
                BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
                if (adapterProvider != null) {
                    return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
                }
                throw new RuntimeException("Unexpected view type" + viewType);
        }
    }
	@Override
    public int getItemViewType(int position) {
        AdapterItem item = mApps.getAdapterItems().get(position);
        return item.viewType;
    }

源码路径:packages/apps/Launcher3/src/com/android/launcher3/BaseDraggingActivity.java

	@Override
    public View.OnClickListener getItemOnClickListener() {
        return ItemClickHandler.INSTANCE;
    }

源码路径:
packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java

public static final OnClickListener INSTANCE = ItemClickHandler::onClick;

    private static 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;

        Launcher launcher = Launcher.getLauncher(v.getContext());
        if (!launcher.getWorkspace().isFinishedSwitchingState()) return;

        Object tag = v.getTag();
        //tag instance 为AppInfo
        if (tag instanceof WorkspaceItemInfo) {
            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
        } else if (tag instanceof FolderInfo) {
            if (v instanceof FolderIcon) {
                onClickFolderIcon(v);
            }
        } else if (tag instanceof AppInfo) {
            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
        } else if (tag instanceof LauncherAppWidgetInfo) {
            if (v instanceof PendingAppWidgetHostView) {
                onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
            }
        } else if (tag instanceof SearchActionItemInfo) {
            onClickSearchAction(launcher, (SearchActionItemInfo) tag);
        }
    }
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
        TestLogging.recordEvent(
                TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
        Intent intent;
        if (item instanceof ItemInfoWithIcon
                && (((ItemInfoWithIcon) item).runtimeStatusFlags
                & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
            intent = new PackageManagerHelper(launcher)
                    .getMarketIntent(appInfo.getTargetComponent().getPackageName());
        } else {
            intent = item.getIntent();
        }
        if (intent == null) {
            throw new IllegalArgumentException("Input must have a valid intent");
        }
        if (item instanceof WorkspaceItemInfo) {
            WorkspaceItemInfo si = (WorkspaceItemInfo) item;
            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)
                    && Intent.ACTION_VIEW.equals(intent.getAction())) {
                // make a copy of the intent that has the package set to null
                // we do this because the platform sometimes disables instant
                // apps temporarily (triggered by the user) and fallbacks to the
                // web ui. This only works though if the package isn't set
                intent = new Intent(intent);
                intent.setPackage(null);
            }
            if ((si.options & WorkspaceItemInfo.FLAG_START_FOR_RESULT) != 0) {
                launcher.startActivityForResult(item.getIntent(), 0);
                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                launcher.logAppLaunch(launcher.getStatsLogManager(), item, instanceId);
                return;
            }
        }
        if (v != null && launcher.supportsAdaptiveIconAnimation(v)) {
            // Preload the icon to reduce latency b/w swapping the floating view with the original.
            FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
        }
    	//启动应用程序
        launcher.startActivitySafely(v, intent, item);
    }

super.startActivitySafely的实现在AppLauncher中。分析Launcher类继承关系,发现它最终继承于BaseActivity,而BaseActivity实现AppLauncher。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

public class Launcher extends StatefulActivity<LauncherState>
        implements LauncherExterns, Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
        PluginListener<LauncherOverlayPlugin>, LauncherOverlayCallbacks {
            
	@Override
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
        if (!hasBeenResumed()) {
            // Workaround an issue where the WM launch animation is clobbered when finishing the
            // recents animation into launcher. Defer launching the activity until Launcher is
            // next resumed.
            addOnResumeCallback(() -> startActivitySafely(v, intent, item));
            if (mOnDeferredActivityLaunchCallback != null) {
                mOnDeferredActivityLaunchCallback.run();
                mOnDeferredActivityLaunchCallback = null;
            }
            return true;
        }

        boolean success = super.startActivitySafely(v, intent, item);
        if (success && v instanceof BubbleTextView) {
            // This is set to the view that launched the activity that navigated the user away
            // from launcher. Since there is no callback for when the activity has finished
            // launching, enable the press state and keep this reference to reset the press
            // state when we return to launcher.
            BubbleTextView btv = (BubbleTextView) v;
            btv.setStayPressed(true);
            addOnResumeCallback(() -> btv.setStayPressed(false));
        }
        return success;
    }        
}

首先设置FLAG_ACTIVITY_NEW_TASK,添加一个新的任务栈,item类型为AppInfo,类型为ITEM_TYPE_APPLICATION,因此调用Context的startActivity方法。这里启动的是应用程序的根Activity,相当于启动应用程序。

源码路径:packages/apps/Launcher3/src/com/android/launcher3/views/AppLauncher.java

default boolean startActivitySafely( View v, Intent intent, @Nullable ItemInfo item) {
        Context context = (Context) this;
        if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
            Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
            return false;
        }
        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
        UserHandle user = item == null ? null : item.user;
        // Prepare intent
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (v != null) {
            intent.setSourceBounds(Utilities.getViewBounds(v));
        }
        try {
            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                    && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
                    || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                    && !((WorkspaceItemInfo) item).isPromise();
            if (isShortcut) {
                // Shortcuts need some special checks due to legacy reasons.
                startShortcutIntentSafely(intent, optsBundle, item);
            } else if (user == null || user.equals(Process.myUserHandle())) {
                // Could be launching some bookkeeping activity
                //启动应用程序
                context.startActivity(intent, optsBundle);
            } else {
                context.getSystemService(LauncherApps.class).startMainActivity(
                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
            }
            if (item != null) {
                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                logAppLaunch(getStatsLogManager(), item, instanceId);
            }
            return true;
        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
            Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
        }
        return false;
    }

总结

SystemServer进程启动AMS,AMS启动了一个action为"android.intent.action.MAIN",category为"android.intent.category.HOME"为的Activity,这个Activity在Launcher中注册,Launcher也被启动,接下来分析了桌面应用图标的显示过程,以及点击桌面图标启动应用程序的过程。


Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.yanghujun.com/archives/launcherstart