Launcher启动过程

Android系统启动流程(八)——Launcher启动 Launcher在SystemServer进程初始化完成之后,通过AMS的systemReady方法启动首先会找到一个可显示的设备,然后在其显示区域中显示构造一个Intent,Action为ACTION_MAIN,category为CATEGORY_HOME,然后在PMS中查询对应的Intent,然后返回一个ActivityInfo根据Intent和ActivityInfo来启动Activity,最终调用到startSpecificActivity。 阅读详情

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也被启动,接下来分析了桌面应用图标的显示过程,以及点击桌面图标启动应用程序的过程。

Android Launcher启动过程 开机启动概述: step 1: 上电开机 长按power键后引导芯片开始从固化在ROM的预设代码处执行,加载引导程序(BootLoader)到RAM. step 2: BootLoader启动 跳转到BootLoader的入口函数,开始执行BootLoader的代码. 硬件初始化工作(硬件时钟、手机的主板等) 完成初始化uart端口的操作 arch_init target_init apps_init aboot_init 跳转到内核入口函数start_kernel,启动内核. step 3: kerne 阅读详情

相关推荐

Android8.1 Launcher3启动源码解析之AMS启动过程

本次将讲解Launcher启动过程,将从AMS中启动Launcher开始说起一、SystemServiceManager启动服务过程在说AMS启动之前有必要说一下SSM是怎样启动service启动的,这个之前关系后面理解AMS启动过程中一些操作。首先看一个在SM中是怎样启动服务的 traceBeginAndSlog("StartInstaller"); Installer install...

懒懒散散看技术 2099

云上攻防-云服务篇&弹性计算&云数据库&实例元数据&控制角色&AK控制台接管

元数据解释:实例元数据(metadata)包含了弹性计算云服务器实例在阿里云系统中的信息,您可以在运行中的实例内方便地查看实例元数据,并基于实例元数据配置或管理实例。(基本信息:实例ID、IP地址、网卡MAC地址、操作系统类型等信息。实例标识包括实例标识文档和实例标识签名,所有信息均实时生成,常用于快速辨别实例身份。各大云元数据地址:阿里云元数据地址:http://100.100.100.200/腾讯云元数据地址:http://metadata.tencentyun.com/

今天是几号的博客 2334

Android Framework分析Launcher启动过程

主Activity是指Launcher应用程序中的MainActivity,它是应用程序的入口点。Launcher是Android系统中默认的桌面应用程序,负责管理设备的屏幕和应用程序图标。Launcher应用程序中的设计资源是指应用程序的UI元素,例如:图标、背景、字体等资源。这些资源是在应用程序启动时加载的,并用于构建应用程序的UI界面。Launcher应用程序中的布局文件包含了应用程序的UI元素的结构和排列方式。这些布局文件是在应用程序启动时加载的,并用于构建应用程序的UI界面。

工宗浩生财指南针 2452

Workbench的服务器公网白名单:

登录名称:Administrator@139.196.54.95:3389 实例id:i-uf6exzqcieg2y4tvzly1 实例名称:launch-advisor-20200109 登录实例失败,原因: 连接实例 i-uf6exzqcieg2y4tvzly1 (139.196.54.95:3389) 超时: 10 秒,请检查网络是否可达或者白名单设置 通过公网IP或者EIP访...

微道道的博客 4万+

Android Launcher 源码解析:Launcher的源码启动过程讲解

以上是Android Launcher的源码启动过程的讲解。通过对相关源代码的分析,我们了解了Launcher的初始化、加载应用程序列表和处理应用点击事件等关键步骤。希望本文对你理事件等关键步骤。Launcher的源码启动过程为我们理解和自定义Launcher提供了基础。Android中的Launcher(启动器)是用户与设备交互的重要界面之一,它负责展示设备上的应用程序列表和提供快速访问常用应用的功能。Android Launcher 源码解析:Launcher的源码启动过程讲解。

持续更新 439

[面试]SoC验证工程师面试常见问题(三)

SoC 验证工程师面试中,SystemVerilog、UVM 和 SystemC 的代码题目通常涵盖面向对象编程、约束随机化、UVM 组件设计、内存建模等主题。通过练习上述示例题目和理解关键点,你可以更好地应对现场编码环节。如果有具体的面试方向(如特定协议验证)或公司偏好,可以提供更多信息,我会进一步定制题目和答案。祝你面试顺利!

元直的博客 1614

Langchain-chatchat-0.2.9版本-企业微信开发使用指南

1.租阿里云的服务器的教程如下:网址如下:阿里云云服务器,质优价更低 (aliyun.com)](https://cn.aliyun.com/daily-act/ecs/activity_selection?from_alibabacloud=&utm_content=se_1015259433) 登录自己的阿里云账号6.下面开始创建实例(账户内至少100RMB),创建之后就开始系统会自己装对应的CUDA版本,接下来自己创建conda环境,这里申明一下ECS服务器得自己创建conda环境,创建conda环境

Panda_Panda博客 4038

DCA决策曲线的解读和代码实现

0.决策曲线 决策曲线分析法(Decision Curve Analysis,DCA)是个与ROC曲线相提并论的相对比较新的模型评价方法。 关于它的原理,长篇大论的医学统计学知识解读实在不是我的强项,放上两个链接偷偷懒: https://www.plob.org/article/12455.html https://www.iikx.com/news/statistics/1622.html 如果想看文章,这篇里的fig7就是:https://www.ncbi.nlm.nih.gov/pmc/artic

weixin_42960896的博客 1万+

Launcher布局加载流程

文章目录一、AndroidManifest文件二、桌面布局1.布局2. Launcher界面配置文件3 Launcher.java的onCreate方法:3.LauncherModel的startLoader的方法4.LoaderTask的run方法5.workspace的加载流程5.1.loadWorkspace()5.2.bindWorkspace6参考资料 一、AndroidManifest文件 <application android:backupAgent="com.

qq_43898917的博客 1115

2.4 提示词优化

我们在使用大模型的时候,同一任务使用不同表述的提示词,可能得到差异巨大的结果,因为模型的性能高度依赖输入提示词的质量。我们可以通过提示词优化来更高效地获取理想输出。

从0到1,突破自己 1009

Vivado报错-4 [Routed 35-3424] SLL Assignment failed ILP VU19P

Vivado报错[Routed 35-3424] SLL Assignment failed ILP VU19P

weixin_39896700的博客 1408

Android系统启动(四) — Launcher 启动过程

系统启动的最后一步是启动一个应用程序来显示系统中已经安装的应用程序,这个应用程序就叫做Launcher。Launcher在启动过程中会请求返回系统中已经安装的应用程序信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户就可以通过点击这些快捷图标来启动相应的应用程序。通俗的讲,Launcher就是Android系统的桌面,作为Android系统的启动器,用于启动应用程序;作为Android系统的桌面,用于显示和管理应用程序的快捷图标或者其他桌面组件;

xingyu19911016的博客 1万+

PyTorch深度学习实战(2)——PyTorch基础

PyTorch 是广泛应用于机器学习领域中的强大开源框架,因其易用性和高效性备受青睐。在本节中,将介绍使用 PyTorch 构建神经网络的基础知识。首先了解 PyTorch 的核心数据类型——张量对象。然后,我们将深入研究用于张量对象的各种操作。PyTorch 提供了许多帮助构建神经网络的高级方法及组件,并提供了利用 GPU 更快地训练神经网络的张量对象。

盼小辉丶的博客 5万+

Android Framework 之了解 Launcher 启动过程

SystemServer 完成启动 LauncherActivity 的调用;Zygote 进行 Launcher 进程的 fork 操作;进入 ActivityThread 的 main(),完成最终 Launcher 的 onCreate() 操作;

Bonnie_cat的博客 1184

推荐11个顶级的Claude Code Skills

ClaudeCode的Skills系统为程序员提供了11个强大的AI辅助工具,包括代码审查、重构建议、单元测试生成、API文档自动生成、性能分析、安全扫描、数据库迁移脚本生成、CI/CD配置、依赖管理、ER图生成和日志分析等功能。这些"技能包"可大幅提升开发效率,从代码质量到协作沟通全方位优化工作流程。用户可根据需求选择2-3个核心功能开始尝试,熟练后还可自定义Skills。该系统需要配合Anthropic的API使用,建议先在小型项目测试再应用于核心业务。

分享AI、区块链技术及应用,未来已来 1847

记录:解决Ubuntu20.04安装ros报错E: Unable to locate package ros-kinetic-desktop-full【亲测有效】

报错:E: Unable to locate package ros-kinetic-desktop-full.尝试好久一直都无法下载,最后得知真相的我眼泪哗哗的直流。

**My Coding Family** 6万+

Android系统的启动过程(三):Launcher启动过程

前两篇文章中我们已经将系统启动的过程推进到了系统服务启动完毕之后,本篇文章就来介绍Android系统启动的最后一步:启动Launcher。这个Launcher我们可以通俗地理解为桌面,系统启动的最后一步就是启动一个应用程序来显示系统中已经安装完成的应用程序,这个应用程序就叫做Launcher。在 Android 系统启动的最后一步,是启动系统的默认桌面(Launcher)。桌面是用户与 Android 设备交互的主要界面,它提供了应用程序图标、小部件、壁纸等元素,使用户可以访问和启动应用程序。Launche

Tai_Monster的博客 6341

第六部分:VTK进阶(第182章 体素化与隐式建模)

本文系统介绍了基于点云构建有符号距离场(SDF)的核心原理与VTK实现方法。主要内容包括:1)SDF的基本概念与算法原理,通过体素网格隐式表达几何表面;2)关键参数配置指南,如体素分辨率、边界范围和影响半径的选择;3)点云法向估计、SDF计算与等值面提取的完整工作流程;4)常见问题解决方案与性能优化建议。文中提供了详细的C++代码示例,展示从随机点云生成SDF并重建曲面的完整过程,特别强调了法向估计、参数调优和后处理等关键环节。该方法适用于点云表面重建、几何处理等应用场景。

仰望星空的博客 527

Launcher 启动过程

我们再回到ActivityManagerService的startHomeActivityLocked函数,假设系统的运行模式不是低级工厂模式,在注释3处判断符合Action为Intent.ACTION_MAIN,Category为Intent.CATEGORY_HOME的应用程序是否已经启动,如果没启动则调用注释4的方法启动该应用程序。被SystemServer进程启动的ActivityManagerService会启动Launcher,Launcher启动后会将已安装应用的快捷图标显示到界面上。

xfb的博客 885

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

launcher 启动过程

http://blog.youkuaiyun.com/stonecao/article/details/6536083 一LauncherApplication->onCreate         --1.//设置最小堆内存4M         --2.//建立应用图标缓存器

pgalxx的专栏 1265

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Launcher启动过程分析

在systemReady()

wcsbhwy的博客 645

Android系统启动流程(四)Launcher启动过程与系统启动流程

此前的文章我们学习了init进程、Zygote进程和SyetemServer进程的启动过程,这一篇文章我们就来学习Android系统启动流程的最后一步:Launcher的启动流程,并结合本系列的前三篇文章的内容来讲解Android系统启动流程。建议读这篇文章前要通读本系列的前三篇文章,否则你可能不会理解我在讲什么。

Android进阶三部曲 - 刘望舒 2万+

Launcher的启动过程

1.  Launcher的启动过程 从网络上找了一段关于Launcher 的启动过程的文章,作为学习 Launcher 的背景知识: Linux kernel 启动以后会通过 a pp_main 进程来初始化android Runtime Java运行环境,而 zygote 是 A ndroid的第一个进程。所有的android的应用以及大部分系统...

本事记的富伟徐 273

通过Launcher启动应用的过程分析

通过Launcher启动应用的过程分析 通过Lanucher启动 应用时会调用Lanucher的onClick方法。 Lanucher.java /** * Launches the intent referred by the clicked shortcut. * * @param v The view representing the c...

GODBAR的博客 1437
上一篇: SystemServer 进程启动过程
下一篇: Activity启动过程
八归少年
八归少年 新星创作者: 操作系统技术领域 新星创作者: 操作系统技术领域
博客等级 码龄9年 9011粉丝 63原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八归少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值