I’ve already introduced the layout of the Launcher and related interface transitions. Today we’re going to continue our study. As planned, we’re going to start with the data loading of the Launcher, mainly ICONS, widgets, and folders.

1. Basic knowledge


Before introducing loading, LET me introduce some relevant knowledge to use:

  • The desktop is an Activity with which to display ICONS, widgets, folders, and so on.

  • LauncherModel (BroadcastReceiver) LauncherModel (BroadcastReceiver) LauncherModel (BroadcastReceiver) LauncherModel (BroadcastReceiver)

  • LauncherProvider: Inherits ContentProvider, which handles database operations;

  • LauncherAppState: Singleton global management class that initializes objects, registers broadcasts, and so on.

  • Compat: compatibility package. Any class with this suffix is a compatibility package.

2. Configure the default icon


When we buy a new phone or install a new Launcher for the first time, we will find that the first page of the phone already has some application ICONS, clocks or weather plug-ins. How does this work? In fact, when a mobile phone is delivered to the factory or sent to the market, some applications are arranged by default. When it is first started up, it will load and judge whether there are these ICONS in the phone. If there are, they will be displayed in a fixed position, which is already written. Now let’s see where this position is actually written.

Let’s look at the resource file for the Launcher. We have one more XML folder than we usually have. There are a lot of XML files in it 5×5.xml and default_workspace_5x6.xml are our default layout files, followed by 4×4, 5×5, and 5×6 to represent the number of columns and rows for the desktop icon.

Let’s first look at the code in default_workspace_4x4.xml:

Line 20 is an include file, named DW_phone_hotseat in the XML folder, which we’ll look at later, moving on to the code at the bottom of the image above. Below are three resolve files that contain information: screen for screen,x for landscape, and Y for vertical So how do we get this position? Let me draw a 4×4 diagram to get the idea:

First take a look at the half part, which is the 4×4 part. Each cell represents an icon. When we draw the icon, we have divided the size of each cell, and as long as we know its position, we can draw the icon to the corresponding position. Resolve above there are two favorite, behind the most in the first a “APP_,” is this a look, we knew that the application of attribute, it means we configure the app in this position, we look again at the hotseat introduced above the XML file:

This figure I capture only a part of all want to see can I download the source code on the lot, actually just repeated, I introduce a will know that I described in the previous chapter hotseat this concept, it is our mobile phone at the bottom of the four or five of the most commonly used app icon, it is in this configuration, I in the first, for example To introduce the Hotseat configuration, let’s look at line 21. The container is more than the previous one. The previous one doesn’t have one, so this is the container Y stays the same.

This basic desktop default icon display configuration is introduced, if you want to display the default only need to configure this file.

3.Launcher process


Let’s start by introducing the startup process of the Launcher. Analyzing the startup process of the Launcher starts with the source code. In the source code it’s called startHomeActivityLocked to start the Launcher, so let’s take a look at where this function is called,

The call diagram above shows that the method Launcher is called from three different places. The first Launcher of these three methods should be the middle systemReady method. During system preparation, the systemReady method is called to start the Launcher.

The static analysis of the code above shows that it starts in the System. Main method, which is the entry point to launch the System. In this process, we start the Launcher Her, first look at the source:

Let’s look at line 3437, get the Intent, and then line 3451. If it’s not empty, start HomeActivity. Let’s look at what this Intent is:

In line 3424 above, there is an intent.category_HOME. We find the code for this attribute in the Intent:

This is the value of the property we used in the previous chapter to set our app to launcher.

Through the above analysis, you can see how the system starts the launcher. Let’s look at how the interior of the Launcher starts.

4. The Launcher initialization


We know that apps start with Application, but in our latest launch 3, Google engineers removed this class. Again, previous versions had this class. I mention it here because I had a problem developing the old Launcher between Application and C If ontentProvider exists at the same time, the onCreate method of ContentProvider starts before the onCreate method of Application.

Start the Application with the attachApplication method in ActivityManagerService:


 public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final longorigId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); }}Copy the code

The attachApplicationLocked method is then called as follows:


   private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
        app.makeActive(thread, mProcessStats);
        app.curAdj = app.setAdj = -100;
        app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
        app.forcingToForeground = null;
        updateProcessForegroundLocked(app, false.false);
        app.hasShownUi = false;
        app.debugging = false;
        app.cached = false;
        app.killedByAm = false;

        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

        if(! normalMode) { Slog.i(TAG,"Launching preboot mode app: " + app);
        }

        if (DEBUG_ALL) Slog.v(
            TAG, "New app record " + app
            + " thread=" + thread.asBinder() + " pid=" + pid);
        try{... ProfilerInfo profilerInfo = profileFile ==null ? null
                    : newProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop); thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || ! normalMode, app.persistent,new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false.null);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
        } catch (Exception e) {

            app.resetPackageList(mProcessStats);
            app.unlinkDeathRecipient();
            startProcessLocked(app, "bind fail", processName);
            return false; }...return true;
    }

Copy the code

The bindApplication method binds the application to thread.bindApplication.

 public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
                Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
                Bundle coreSettings) {... AppBindData data =new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableOpenGlTrace = enableOpenGlTrace;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            sendMessage(H.BIND_APPLICATION, data);
        }
Copy the code

Prepare the data and send the message to a Handler that processes the message as follows:


public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {


                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

            }
            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
        }

Copy the code

Call the handleBindApplication method based on the message type BIND_APPLICATION,

 private void handleBindApplication(AppBindData data) {...try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if(! data.restrictedBackupMode) { List<ProviderInfo> providers = data.providers;if(providers ! =null) {
                
                	/ / ContentProviders installation
                    installContentProviders(app, providers);
                    
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); }}...try {
                // Start Application's onCreate method
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if(! mInstrumentation.onException(app, e)) {throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ":"+ e.toString(), e); }}}finally{ StrictMode.setThreadPolicy(savedPolicy); }}Copy the code

To install the ContentProvider, call the installContentProviders method in the above function:

 private void installContentProviders( Context context, List
       
         providers)
        {
        final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(":");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, ... }... }Copy the code

Call installProvider returns a IActivityManager ContentProviderHolder object, we are doing this method in processing,

private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;         if (holder == null || holder.provider == null) {
            if (DEBUG_PROVIDER || noisy) {
                Slog.d(TAG, "Loading provider " + info.authority + ":"
                        + info.name);
            }
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            if (context.getPackageName().equals(ai.packageName)) {
                c = context;
            } else if(mInitialApplication ! =null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                c = mInitialApplication;
            } else {
                try {
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore}}if (c == null) {
                Slog.w(TAG, "Unable to get context for package " +
                      ai.packageName +
                      " while loading content provider " +
                      info.name);
                return null;
            }
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                / / get the ContentProvider
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                    
                // XXX Need to create the correct context for this provider.
                localProvider.attachInfo(c, info);
                
            } catch (java.lang.Exception e) {
                if(! mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ":" + e.toString(), e);
                }
                return null; }}else {
            provider = holder.provider;
            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ":"+ info.name); }...return retHolder;
    }

Copy the code

AttachInfo (c, info); attachInfo(c, info); attachInfo(c, info);

private void attachInfo(Context context, ProviderInfo info, boolean testing) {

        /* * Only allow it to be set once, so after the content service gives * this to us clients can't change it. */
        if (mContext == null) {
            mContext = context;
            if(context ! =null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if(info ! =null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                mExported = info.exported;
            }
            ContentProvider.this.onCreate(); }}Copy the code

We saw in the last call the ContentProvider. This. The onCreate () this approach, Then will return to handleBindApplication method that executes mInstrumentation. CallApplicationOnCreate (app) method, the code is as follows:


 public void callApplicationOnCreate(Application app) {
      app.onCreate();
 }

Copy the code

So we see that the onCreate method of ContentProvider is called earlier than the onCreate method of Application. Here is just a brief introduction to the detailed process to see the source code.

What I’m talking about now is based on the latest Launcher3 code, so we don’t have an Application in our Launcher, so we start the program with the onCreate method of ContentProvider, which looks like this:

public boolean onCreate(a) {
        final Context context = getContext();
        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
        mOpenHelper = new DatabaseHelper(context);
        StrictMode.setThreadPolicy(oldPolicy);
        LauncherAppState.setLauncherProvider(this);
        return true;
}

Copy the code

There are not many things to do in the code, mainly starting the harsh mode and creating a database. See the official documentation or blog for detailed information about the harsh mode, and then place the ContentProvider into the LauncherAppState management class of the entire Launcher for easy access.

Now to start the Launcher, let’s look at the code in the onCreate method in the Launcher:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        if (DEBUG_STRICT_MODE) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()   // or .detectAll() for all detectable problems
                    .penaltyLog()
                    .build());
            StrictMode.setVmPolicy(newStrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath()  .build()); }if(mLauncherCallbacks ! =null) {
            mLauncherCallbacks.preOnCreate();
        }

        super.onCreate(savedInstanceState);

        LauncherAppState.setApplicationContext(getApplicationContext());
        LauncherAppState app = LauncherAppState.getInstance();

        // Load configuration-specific DeviceProfile
        mDeviceProfile = getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE ?
                app.getInvariantDeviceProfile().landscapeProfile
                : app.getInvariantDeviceProfile().portraitProfile;

        mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
                Context.MODE_PRIVATE);
        mIsSafeModeEnabled = getPackageManager().isSafeMode();
        mModel = app.setLauncher(this);
        mIconCache = app.getIconCache();

        mDragController = new DragController(this);
        mInflater = getLayoutInflater();
        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);

        mStats = new Stats(this);

        mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);

        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
        mAppWidgetHost.startListening();

        // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
        // this also ensures that any synchronous binding below doesn't re-trigger another
        // LauncherModel load.
        mPaused = false;

        if (PROFILE_STARTUP) {
            android.os.Debug.startMethodTracing(
                    Environment.getExternalStorageDirectory() + "/launcher");
        }

        setContentView(R.layout.launcher);

        registerHomeKey();
        setupViews();

        // Dynamically set the parameters of each layout
        mDeviceProfile.layout(this);

        mSavedState = savedInstanceState;
        restoreState(mSavedState);

        if (PROFILE_STARTUP) {
            android.os.Debug.stopMethodTracing();
        }

        if(! mRestoring) {if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foregroundmModel.startLoader(mWorkspace.getRestorePage()); }}// For handling default keys
        mDefaultKeySsb = new SpannableStringBuilder();
        Selection.setSelection(mDefaultKeySsb, 0);

        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        registerReceiver(mCloseSystemDialogsReceiver, filter);

        mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext());
        // In case we are on a device with locked rotation, we should look at preferences to check
        // if the user has specifically allowed rotation.
        if(! mRotationEnabled) { mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(),false);
        }

        // On large interfaces, or on devices that a user has specifically enabled screen rotation,
        // we want the screen to auto-rotate based on the current orientation
        setOrientation();

        if(mLauncherCallbacks ! =null) {
            mLauncherCallbacks.onCreate(savedInstanceState);
            if(mLauncherCallbacks.hasLauncherOverlay()) { ViewStub stub = (ViewStub) findViewById(R.id.launcher_overlay_stub); mLauncherOverlayContainer = (InsettableFrameLayout) stub.inflate(); mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView( mLauncherOverlayContainer, mLauncherOverlayCallbacks); mWorkspace.setLauncherOverlay(mLauncherOverlay); }}if (shouldShowIntroScreen()) {
            showIntroScreen();
        } else{ showFirstRunActivity(); showFirstRunClings(); }}Copy the code

Let’s take a look at the execution process diagram:

The first step is to start harsh mode, prepare the callback interface, and initialize LauncherAppState:

    private LauncherAppState(a) {
        if (sContext == null) {
            throw new IllegalStateException("LauncherAppState inited before app context set");
        }

        if (sContext.getResources().getBoolean(R.bool.debug_memory_enabled)) {
            MemoryTracker.startTrackingMe(sContext, "L");
        }
		
		// Initializes the fixed device configuration
        mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
        
        // Initialize the icon management tool
        mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
        
        // Initialize the Widget to load the mix tool
        mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);

        mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
        mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
        
        // Initialize the broadcast
        mModel = new LauncherModel(this, mIconCache, mAppFilter);

        LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);

        // Register intent receivers
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
        // For handling managed profiles
        filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
        filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);

		// Register a broadcast
        sContext.registerReceiver(mModel, filter);
        UserManagerCompat.getInstance(sContext).enableAndResetCache();
    }

Copy the code

Then initialize the phone firmware information object DeviceProfile, initialize the drag manager DragController, initialize the widget manager, load the layout, initialize the various desktop controls, and set the location of the various controls:

public void layout(Launcher launcher) {
        FrameLayout.LayoutParams lp;
        boolean hasVerticalBarLayout = isVerticalBarLayout();
        final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());

        // Layout the search bar space
        View searchBar = launcher.getSearchDropTargetBar();
        lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
        if (hasVerticalBarLayout) {
            // Vertical search bar space -- The search bar is fixed in the layout to be on the left
            // of the screen regardless of RTL
            lp.gravity = Gravity.LEFT;
            lp.width = searchBarSpaceHeightPx;

            LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
            targets.setOrientation(LinearLayout.VERTICAL);
            FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
            targetsLp.gravity = Gravity.TOP;
            targetsLp.height = LayoutParams.WRAP_CONTENT;

        } else {
            // Horizontal search bar space
            lp.gravity = Gravity.TOP;
            lp.height = searchBarSpaceHeightPx;

            LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
            targets.getLayoutParams().width = searchBarSpaceWidthPx;
        }
        searchBar.setLayoutParams(lp);

		// Other omissions. }Copy the code

Here is the dynamic setting of the desktop of each control position and width properties. When all the information is initialized, the mModel.startLoader method is called to load the application data. Let’s look at the data loading process in detail.

5.Launcher Data load


Data loading mainly starts with the startLoader method in the LauncherModel. Let’s look at what this method does:

LoaderTask implements the Runnable interface, so first run. Let’s take a look at what the run method does.

 public void run(a) {... keep_running: { loadAndBindWorkspace();if (mStopped) {
                    breakkeep_running; } waitForIdle(); . loadAndBindAllApps(); }... }Copy the code

There are three main things to do in this method, and we use a sequence diagram:

The first is to execute the loadAndBindWorkspace method:

 private void loadAndBindWorkspace(a) {...// Check whether workspace is loaded
            if(! mWorkspaceLoaded) { loadWorkspace();synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return;
                    }
                    mWorkspaceLoaded = true; }}// Bind the workspace
            bindWorkspace(-1);
        }

Copy the code

LoadWorkspace and bindWorkspace are used to load and bindWorkspace applications. Let’s look at the loadWorkspace method first. There’s a lot of code, so we’ll only post the key parts:

private void loadWorkspace(a) {
            // Initialize some values.if((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) ! =0) {
                // append the user's Launcher2 shortcuts
                Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2".true);
                LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
            } else {
                // Make sure the default workspace is loaded
                Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites".false);
                LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
            }

            synchronized (sBgLock) {
            	// Initialize some values.try {
                   // Query all application information parsed from the database.while(! mStopped && c.moveToNext()) {try {
                            int itemType = c.getInt(itemTypeIndex);
                            boolean restored = 0! = c.getInt(restoredIndex);boolean allowMissingTarget = false;
                            container = c.getInt(containerIndex);

                            switch (itemType) {
                                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                                
                                    ...
                                    
                                    try {
                                        intent = Intent.parseUri(intentDescription, 0);
                                        ComponentName cn = intent.getComponent();
                                        if(cn ! =null&& cn.getPackageName() ! =null) {
                                            // Check whether the app package extracted from the database (parsed from the XML file stored in the database) exists
                                            boolean validPkg = launcherApps.isPackageEnabledForProfile(
                                                    cn.getPackageName(), user);
                                            // Check whether the app component extracted from the database (parsed from the XML file and stored in the database) exists
                                            boolean validComponent = validPkg &&
                                                    launcherApps.isActivityEnabledForProfile(cn, user);

                                            if (validComponent) {
                                            
                                                ...
                                                
                                            } else if (validPkg) {
                                               
                                               ...
                                               
                                            } else if (restored) {
                                                
                                                ...
                                                
                                            } else if (launcherApps.isAppEnabled(
                                                    manager, cn.getPackageName(),
                                                    PackageManager.GET_UNINSTALLED_PACKAGES)) {
                                               
                                               ...
                                               
                                            } else if(! isSdCardReady) { ... }else{... }}else if (cn == null) {
                                            // For shortcuts with no component, keep them as they are
                                            restoredRows.add(id);
                                            restored = false; }}catch (URISyntaxException e) {
                                        Launcher.addDumpLog(TAG,
                                                "Invalid uri: " + intentDescription, true);
                                        itemsToRemove.add(id);
                                        continue; }...if(info ! =null) {
                                        info.id = id;
                                        info.intent = intent;
                                        info.container = container;
                                        info.screenId = c.getInt(screenIndex);
                                        info.cellX = c.getInt(cellXIndex);
                                        info.cellY = c.getInt(cellYIndex);
                                        info.rank = c.getInt(rankIndex);
                                        info.spanX = 1;
                                        info.spanY = 1; info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber); .switch (container) {
                                            case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                            case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
                                                sBgWorkspaceItems.add(info);
                                                break;
                                            default:
                                                // Item is in a user folder
                                                FolderInfo folderInfo =
                                                        findOrMakeFolder(sBgFolders, container);
                                                folderInfo.add(info);
                                                break;
                                        }
                                        sBgItemsIdMap.put(info.id, info);
                                    } else {
                                        throw new RuntimeException("Unexpected null ShortcutInfo");
                                    }
                                    break;

                                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                                
                                    ...
                                
                                    break;

                                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                                case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                                
                                    ...
                                                                    break; }}catch (Exception e) {
                            Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true); }}}finally{... }...// Sort all the folder items and make sure the first 3 items are high resolution.
                for (FolderInfo folder : sBgFolders) {
                    Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
                    int pos = 0;
                    for (ShortcutInfo info : folder.contents) {
                        if (info.usingLowResIcon) {
                            info.updateIcon(mIconCache, false);
                        }
                        pos++;
                        if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
                            break; }}}if (restoredRows.size() > 0) {
                    // Update restored items that no longer require special handling
                    ContentValues values = new ContentValues();
                    values.put(LauncherSettings.Favorites.RESTORED, 0);
                    contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
                            Utilities.createDbSelectionQuery(
                                    LauncherSettings.Favorites._ID, restoredRows), null);
                }

                if(! isSdCardReady && ! sPendingPackages.isEmpty()) { context.registerReceiver(new AppsAvailabilityCheck(),
                            new IntentFilter(StartupReceiver.SYSTEM_READY),
                            null, sWorker);
                }

                // Remove any empty screens.// If there are any empty screens remove them, and update.
                if(unusedScreens.size() ! =0) { sBgWorkspaceScreens.removeAll(unusedScreens); updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); }... }}Copy the code

First is called loadDefaultFavoritesIfNecessary this method, to resolve the configured default desktop ICONS which we speak XML file, the process is: Initialize AutoInstallsLayout, then call the loadFavorites method in the LauncherProvider, where the loadLayout method in AutoInstallsLayout is called to parse the configured XML file. In AutoInstallsLayout, widgets, ICONS, folders and other categories are resolved. If there are include tags in the resolution process, the corresponding XML file is parsed. The parsing process is relatively simple and will not be explained in detail. For later use, when the XML document parsing is complete, start to parse the XML configuration file stored in the database of data read, read out, according to the corresponding type (ICONS, widgets, folders, etc.) to judge, judge whether the application in the system exists, if available, if available, to generate the corresponding object and stored in the map of scenarios, If not, delete the data in the database, so that the data in the database after the whole judgment is complete, only the configuration that exists in the system has been applied.

After loading the configuration application icon, execute the bindWorkspace method to bind the application icon to the desktop.

From the sequence diagram above, we can see that the filter work is performed first, such as whether the icon is in workspace or Hotseat, different categories are placed in different positions, and then the sorting process is carried out, and then the bindWorkspaceScreens method is executed to bind the phone with several screens. The bindWorkspaceItems method is then called to bind the ICONS, folders, and widgets for the current screen, and finally to bind the application ICONS, folders, and widgets for other screens, which we’ll cover in the next chapter.

Then execute the waitForIdle method of LoadTask, which waits for the data to finish loading.

LoadAndBindAllApps loadAndBindAllApps loadAndBindAllApps loadAndBindAllApps

 private void loadAndBindAllApps(a) {
            if (DEBUG_LOADERS) {
                Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
            }
            if(! mAllAppsLoaded) { loadAllApps();synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return;
                    }
                }
                updateIconCache();
                synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return;
                    }
                    mAllAppsLoaded = true; }}else{ onlyBindAllApps(); }}Copy the code

The main thing is that if all the applications are loaded this is just the binding application, if not the loading operation is performed. Here’s the load operation:

private void loadAllApps(a) {...final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();

            // Clear the list of apps
            mBgAllAppsList.clear();
            for (UserHandleCompat user : profiles) {
            
            	...
            
               final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
                
                // Fail if we don't have any apps
                // TODO: Fix this. Only fail for the current user.
                if (apps == null || apps.isEmpty()) {
                    return;
                }

                // Create the ApplicationInfos
                for (int i = 0; i < apps.size(); i++) {
                    LauncherActivityInfoCompat app = apps.get(i);
                    // This builds the icon bitmaps.
                    mBgAllAppsList.add(newAppInfo(mContext, app, user, mIconCache)); }...// Huh? Shouldn't this be inside the Runnable below?
            final ArrayList<AppInfo> added = mBgAllAppsList.added;
            mBgAllAppsList.added = new ArrayList<AppInfo>();

            // Post callback on main thread
            mHandler.post(new Runnable() {
                public void run(a) {

                    final long bindTime = SystemClock.uptimeMillis();
                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if(callbacks ! =null) {
                        callbacks.bindAllApplications(added);
                        if (DEBUG_LOADERS) {
                            Log.d(TAG, "bound " + added.size() + " apps in "
                                    + (SystemClock.uptimeMillis() - bindTime) + "ms"); }}else {
                        Log.i(TAG, "not binding apps: no Launcher activity"); }}}); . loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks),true /* refresh */); . }Copy the code

. The above code through mLauncherApps getActivityList method to obtain a list of all application startup interface of an object, and then according to the LauncherActivityInfoCompat corresponding app to initialize the object, so you can get all the applications in the mobile phone list. Access to and operation is executed after the completion of binding, and finally call loadAndBindWidgetsAndShortcuts method load binding interface widgets and shortcut to small parts.

public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {

        runOnWorkerThread(new Runnable() {
            @Override
            public void run(a) {
                updateWidgetsModel(refresh);
                final WidgetsModel model = mBgWidgetsModel.clone();

                mHandler.post(new Runnable() {
                    @Override
                    public void run(a) {
                        Callbacks cb = getCallback();
                        if(callbacks == cb && cb ! =null) { callbacks.bindAllPackages(model); }}});// update the Widget entries inside DB on the worker thread.LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews( model.getRawList()); }}); }Copy the code

In this method, we call the updateWidgetsModel method first, as follows:

 void updateWidgetsModel(boolean refresh) {
        PackageManager packageManager = mApp.getContext().getPackageManager();
        final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
        widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
        widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
        mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
    }

Copy the code

The above code gets all widgets by calling getWidgetProviders, all cross-boundary methods by calling shortcutsIntent, . At last, by mBgWidgetsModel setWidgetsAndShortcuts method put widgets and shortcut in WidgetsModel object, can be obtained from the inside in the later loaded widgets and shortcuts.

To this whole launcher data loading is basically completed, there are many details did not talk about, XML parsing, this Google engineer design is very good, interested can look at the source code.

reference


Android system default Home application Launcher startup process source code analysis

Android application component Content Provider startup process source code analysis