Android Launcher加载流程源码分析

Launcher加载流程分析

最近开始接手Launcher模块,为了更好的技术积累,也看到很多大神在CSDN上发的博文,就有了在CSDN写博客的想法,这篇博文是我在研究了一段时间Launcher3后写的,可能有不对的,望大家拍砖。首先我们可以先参考这篇http://blog.csdn.net/yanbober/article/details/50525559博文,这篇博文介绍了launcher的代码主流程框架,这里我直接贴代码分析。

Launcher首次启动是通过ActivityManagerService.systemReady方法启动的,在Android系统启动中会启动system_server进程,在SystemServer.java中可以找到调用ActivityManagerService.systemReady的地方,来看一下ActivityManagerService中的systemReady方法

public void systemReady(final Runnable goingCallback) {
    ...//省略
    startHomeActivityLocked(currentUserId, "systemReady");
    ...
}

继续看startHomeActivityLocked()方法

boolean startHomeActivityLocked(int userId, String reason) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                && mTopAction == null) {
            // We are running in factory test mode, but unable to find
            // the factory test app, so just sit around displaying the
            // error message and don't try to start anything.
            return false;
        }
        Intent intent = getHomeIntent();//这里得到Launcher的Intent
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
            // Don't do this if the home app is currently being
            // instrumented.
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            if (app == null || app.instrumentationClass == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                //这里就会启动
                mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
            }

            /// M: PerfService for recording the last pause activity information. @{
            if (mStackSupervisor.mLastResumedActivity.packageName == null ||
                    mStackSupervisor.isUpdatedLastActivityWhenStartHome(aInfo.packageName,
                            aInfo.name)) {
                mStackSupervisor.mLastResumedActivity.packageName = aInfo.packageName;
                mStackSupervisor.mLastResumedActivity.activityName = aInfo.name;
                mStackSupervisor.mLastResumedActivity.activityType =
                        ActivityRecord.HOME_ACTIVITY_TYPE;
            }
            /// @}
        } else {
            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
        }

        return true;
    }

来看下getHomeIntent()方法

Intent getHomeIntent() {
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);//CATEGORY_HOME
        }
        return intent;
    }

在Launcher3中的AndroidManifest.xml中有注册这个Category的activity

<activity
            android:name="com.android.launcher3.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/Theme"
            android:configChanges="mcc|mnc"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="portrait"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
            intent-filter>
        activity>

简单分析完了Launcher是怎么被启动的,现在就来看Launcher本身的启动和加载流程,现在来看Launcher.java的onCreate方法,我在一些地方加了注释

@Override
protected void onCreate(Bundle savedInstanceState) {
    /*这两行初始化LauncherAppState,这个单例对象,会注册应用安装、卸载、更新,配置变化等广播,同时会
    初始化LauncherModel,里面有一个内部类LoaderTask用来获取数据,初始化桌面*/
    LauncherAppState.setApplicationContext(getApplicationContext());
    LauncherAppState app = LauncherAppState.getInstance();
    /*new InvariantDeviceProfile对象,从名字看意思是不变的设备相关参数存储类,里面会初始化管理横
    竖屏的两个DeviceProfile对象*/
    app.RenewInvariantDeviceProfile();
    //获得横竖屏的DeviceProfile对象
    mDeviceProfile = getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE ?
                        app.getInvariantDeviceProfile().landscapeProfile
                            : app.getInvariantDeviceProfile().portraitProfile;   
    mModel = app.setLauncher(this);//获取在LauncherAppState中new LauncherModel对象
    mIconCache = app.getIconCache();//桌面图标缓存类

    mDragController = new DragController(this);//拖拽控制类
    mInflater = getLayoutInflater();
    mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);//动画管理类
    mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);//widget管理类

    mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    mAppWidgetHost.startListening();
    setupViews();//初始化布局控件
    mDeviceProfile.layout(this);//根据设备配置调整布局
    //调用mModel.startLoader方法开始加载异步加载桌面的数据,如app,folder,widget等
    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 foreground
                mModel.startLoader(mWorkspace.getRestorePage());
            }
     }
     //是否启动向导界面
     if (shouldShowIntroScreen()) {
            showIntroScreen();
        } else {
            showFirstRunActivity();
            showFirstRunClings();
     }  

以上就是Launcher.java中onCreate方法中的部分代码,以上很多地方都用到了LauncherAppState这个单例对象,现在主要看这个类的构造方法,这个构造方法会初始化很多关键对象

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

        Log.v(Launcher.TAG, "LauncherAppState inited");

        if (sContext.getResources().getBoolean(R.bool.debug_memory_enabled)) {
            MemoryTracker.startTrackingMe(sContext, "L");
        }

        mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
        mIconCache = new IconCache(sContext, mInvariantDeviceProfile);//存放Icon的对象
        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));
        mModel = new LauncherModel(this, mIconCache, mAppFilter);//LauncherModel加载桌面的类

        /*下面这行就是注册APP安装,更新,卸载的广播,LauncherAppsCompat.getInstance(sContext)
        会根据Sdk的版本得到不同的实例,当前分析的是M版本的所以得到的是LauncherAppsCompatV16这个
        单例对象,调用这个addOnAppsChangedCallback方法,调用registerForPackageIntents();
        这个方法就是注册APP安装,更新,卸载的广播的*/
        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);

        if (LauncherLog.DEBUG) {
            LauncherLog.d(TAG, "LauncherAppState: mIconCache = " + mIconCache + ", mModel = "
                    + mModel + ", this = " + this);
        }

        sContext.registerReceiver(mModel, filter);
        UserManagerCompat.getInstance(sContext).enableAndResetCache();

        mPowerManager = (PowerManager) sContext.getSystemService(Context.POWER_SERVICE);

    }

桌面其实就是手机中的APP的入口,放的就是APP的快捷方式,widget等,所有首先我们要获取这些信息,然后把这些信息用UI的方法显示在桌面上,但是获取这些信息是一个耗时的操作,不可能在主线程里面去操作,所以需要用异步线程的方式获取,在LauncherModel类中就有一个异步线程sWorkerThread,这个异步线程中有一个Handler sWorker = new Handler(sWorkerThread.getLooper());还有一个主线程的Handler—mHandler = new DeferredHandler();这个Handler是封装好的,上文说到onCreate中mModle.startLoader代码就是来加载桌面的,现在我们进入LauncherModel这个类去分析startLoader方法,

public void startLoader(int synchronousBindPage, int loadFlags) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        InstallShortcutReceiver.enableInstallQueue();
        synchronized (mLock) {
            if (DEBUG_LOADERS) {
                LauncherLog.d(TAG, "startLoader:  mCallbacks = " + mCallbacks);
            }

            // Clear any deferred bind-runnables from the synchronized load process
            // We must do this before any loading/binding is scheduled below.
            synchronized (mDeferredBindRunnables) {
                mDeferredBindRunnables.clear();
            }

            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                // If there is already one running, tell it to stop.
                stopLoaderLocked();
                /// M: op01, 0p02, added for top package feature, load top packages from a xml file.
                AllAppsListPluginEx.loadTopPackage(mApp.getContext());
                mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
                if (LauncherLog.DEBUG) {
                    LauncherLog.d(TAG, "startLoader: mAllAppsLoaded = " + mAllAppsLoaded
                            + ",mWorkspaceLoaded = " + mWorkspaceLoaded + ",synchronousBindPage = "
                            + synchronousBindPage + ",mIsLoaderTaskRunning = "
                            + mIsLoaderTaskRunning + ",mLoaderTask = " + mLoaderTask);
                }

                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                        && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    /*通过分析,第一次加载最后会走到这里,sWorker上面已经说过异步线程的Handler,
                    执行mLoaderTask这个Runnable,继续看mLoaderTask这个线程的run()方法*/
                    sWorker.post(mLoaderTask);
                }
            }
        }
    }
public void run() {
            synchronized (mLock) {
                if (DEBUG_LOADERS) {
                    LauncherLog.d(TAG, "Set load task running flag >>>>, mIsLaunching = " +
                            ",this = " + this);
                }

                if (mStopped) {
                    return;
                }
                mIsLoaderTaskRunning = true;//更新状态,正在加载
            }
            // Optimize for end-user experience: if the Launcher is up and // running with the
            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
            // workspace first (default).
            keep_running: {
                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                loadAndBindWorkspace();//看名字,应该就知道这个就是来加载和bind

                if (mStopped) {
                    LauncherLog.i(TAG, "LoadTask break in the middle, this = " + this);
                    break keep_running;
                }

                waitForIdle();

                // second step
                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                /*这个是加载所有APP,桌面最下面那一排中间的全部app按钮,国内Launcher基本上
                都会屏蔽这个*/
                loadAndBindAllApps();
   }

继续分析loadAndBindWorkspace()这个方法;

private void loadAndBindWorkspace() {
            mIsLoadingAndBindingWorkspace = true;//更新状态

            // Load the workspace
            if (DEBUG_LOADERS) {
                Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
            }

            if (!mWorkspaceLoaded) {
                loadWorkspace();//这个方法就是去加载app等信息
                synchronized (LoaderTask.this) {
                    if (mStopped) {
                        LauncherLog.d(TAG, "loadAndBindWorkspace returned by stop flag.");
                        return;
                    }
                    mWorkspaceLoaded = true;
                }
            }

            // Bind the workspace
            bindWorkspace(-1);//bind,就是创建视图view,更新UI
        }

先看loadWorkspace()这个方法,这个方法代码很多,就不全部贴出来,只分析里面的一些关键性代码

private void loadWorkspace() {
       ...
       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);
                /*如果数据库中没有数据,就从默认的defaultxxx.xml中解析出布局文件,并存入数据库
                favorites表,存的就是所有的app,widget,folder(文件夹)信息,如位置,icon
                title等,还有workspacescreens表,存的就是桌面每屏的id和顺序
                下面的操作都会去查询这些数据库*/
                LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
            }
            synchronized (sBgLock) {
                clearSBgDataStructures();
                final HashMap installingPkgs = PackageInstallerCompat
                        .getInstance(mContext).updateAndGetActiveSessionCache();
                //从数据库获取屏幕的信息,比如屏幕的个数
                sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
                //查询Favorites表
                final Cursor c = contentResolver.query(contentUri, null, null, null, null);
                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) {
                            //app的类型,就是桌面的每一个APP图标
                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                                id = c.getLong(idIndex);
                                intentDescription = c.getString(intentIndex);
                                serialNumber = c.getInt(profileIdIndex);
                                user = allUsers.get(serialNumber);
                                int promiseType = c.getInt(restoredIndex);
                                int disabledState = 0;
                                boolean itemReplaced = false;
                                if (user == null) {
                                    // User has been deleted remove the item.
                                    itemsToRemove.add(id);
                                    continue;
                                }
                                try {
                                    intent = Intent.parseUri(intentDescription, 0);
                                    ComponentName cn = intent.getComponent();
                                    if (cn != null && cn.getPackageName() != null) {
                                        //判断APP是否可用
                                        boolean validPkg = launcherApps.isPackageEnabledForProfile(
                                                cn.getPackageName(), user);
                                        boolean validComponent = validPkg &&
                                                launcherApps.isActivityEnabledForProfile(cn, user);

                                        if (validComponent) {
                                            if (restored) {
                                                // no special handling necessary for this item
                                                restoredRows.add(id);
                                                restored = false;

                                            ...

                                } else if (itemType ==
                                        LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                                    //得到app的信息,对应一个ShortcutInfo
                                    info = getAppShortcutInfo(manager, intent, user, context, c,
                                            cursorIconInfo.iconIndex, titleIndex,
                                            allowMissingTarget, useLowResIcon);
                                } else {
                                    info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);

                                    // App shortcuts that used to be automatically added to Launcher
                                    // didn't always have the correct intent flags set, so do that
                                    // here
                                    if (intent.getAction() != null &&
                                        intent.getCategories() != null &&
                                        intent.getAction().equals(Intent.ACTION_MAIN) &&
                                        intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
                                        intent.addFlags(
                                            Intent.FLAG_ACTIVITY_NEW_TASK |
                                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                                    }
                                }

                                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);
                                    if (info.promisedIntent != null) {
                                        info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
                                    }
                                    info.isDisabled = disabledState;
                                    if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
                                        info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
                                    }

                                    // check & update map of what's occupied
                                    if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
                                        itemsToRemove.add(id);
                                        break;
                                    }

                                    if (restored) {
                                        ComponentName cn = info.getTargetComponent();
                                        if (cn != null) {
                                            Integer progress = installingPkgs.get(cn.getPackageName());
                                            if (progress != null) {
                                                info.setInstallProgress(progress);
                                            } else {
                                                info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
                                            }
                                        }
                                    }

                                    switch (container) {
                                    //DESKTOP就是桌面中可滑动的部分
                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                    //HOTSEAT就是桌面底部放4个常用app的地方
                                    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);//文件夹中的app
                                } else {
                                    throw new RuntimeException("Unexpected null ShortcutInfo");
                                }
                                break;
                            //文件夹类型,放在sBgFolders这个集合中
                            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                                id = c.getLong(idIndex);
                                FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
                                ....
                            //widget类型    
                            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                            case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                                // Read all Launcher-specific widget details
                                boolean customWidget = itemType ==
                                    LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
}

以上这些都是从数据库中将各种类型的信息读取出来,如app,folder,widget等,然后放在不同的集合里面,
sBgWorkspaceItems—–这里面放的所有桌面的item
sBgFolders—–这里面放的folder
sBgAppWidgets—–这里面放的widget
分析完了,加载信息的过程,那这些app,folder,widget是怎么显示到桌面上的呢,现在我们就来分析,在上面的loadAndBindWorkspace()方法中,执行完上面的loadworkspace后,就会执行bindworkspace方法,这个方法就把这些显示到桌面的的,继续分析代码

private void bindWorkspace(int synchronizeBindPage) {
            ...
            // Save a copy of all the bg-thread collections
            ArrayList workspaceItems = new ArrayList();
            ArrayList appWidgets =
                    new ArrayList();
            ArrayList orderedScreenIds = new ArrayList();

            final LongArrayMap folders;
            final LongArrayMap itemsIdMap;

            //复制一份上面获取到的各种信息集合
            synchronized (sBgLock) {
                workspaceItems.addAll(sBgWorkspaceItems);
                appWidgets.addAll(sBgAppWidgets);
                orderedScreenIds.addAll(sBgWorkspaceScreens);

                folders = sBgFolders.clone();
                itemsIdMap = sBgItemsIdMap.clone();
            }

            ...

            ArrayList currentWorkspaceItems = new ArrayList();
            ArrayList otherWorkspaceItems = new ArrayList();
            ArrayList currentAppWidgets =
                    new ArrayList();
            ArrayList otherAppWidgets =
                    new ArrayList();
            LongArrayMap currentFolders = new LongArrayMap<>();
            LongArrayMap otherFolders = new LongArrayMap<>();

            ...

            //过滤当前页面的item和其他页面的
            filterCurrentWorkspaceItems(tempCurrentScreen, workspaceItems, currentWorkspaceItems,
                    otherWorkspaceItems);
            filterCurrentAppWidgets(tempCurrentScreen, appWidgets, currentAppWidgets,
                    otherAppWidgets);
            filterCurrentFolders(tempCurrentScreen, itemsIdMap, folders, currentFolders,
                    otherFolders);
            ...

            // Tell the workspace that we're about to start binding items
            r = new Runnable() {
                public void run() {
                    //这个回调一直会用到,Luancher.java实现了这个回调接口,
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        //实际调用Launcher.java中的startbinding方法,做一些准备操作
                        callbacks.startBinding();
                    }
                }
            };
            runOnMainThread(r);//在主线程中运行这个runnable

            //创建桌面屏的个数
            bindWorkspaceScreens(oldCallbacks, orderedScreenIds);

            // Load items on the current page
            //创建当前桌面的图标
            bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
                    currentFolders, null);
            ...

            //绑定其他桌面的图标
            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
                    (isLoadingSynchronously ? mDeferredBindRunnables : null));

            // Tell the workspace that we're done binding items
            r = new Runnable() {
                public void run() {
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        //完成绑定
                        callbacks.finishBindingItems();
                    }

                    mIsLoadingAndBindingWorkspace = false;

                    // Run all the bind complete runnables after workspace is bound.
                    if (!mBindCompleteRunnables.isEmpty()) {
                        synchronized (mBindCompleteRunnables) {
                            for (final Runnable r : mBindCompleteRunnables) {
                                runOnWorkerThread(r);
                            }
                            mBindCompleteRunnables.clear();
                        }
                    }

                    // If we're profiling, ensure this is the last thing in the queue.
                    if (DEBUG_LOADERS) {
                        Log.d(TAG, "bound workspace in "
                            + (SystemClock.uptimeMillis()-t) + "ms");
                    }

                }
            };
            if (isLoadingSynchronously) {
                synchronized (mDeferredBindRunnables) {
                    mDeferredBindRunnables.add(r);
                }
            } else {
                runOnMainThread(r);//在主线程运行
            }
        }

上面这个流程基本上是很条理清晰的,我们来看一下runOnMainThread这个方法,在Activiy中内部会自带这个方法,但是在这个LauncherModel内是怎么实现的呢,其实就是用我们上面提到的主线程的mHander的post方法

@Thunk void runOnMainThread(Runnable r) {
        if (sWorkerThread.getThreadId() == Process.myTid()) {
            // If we are on the worker thread, post onto the main handler
            mHandler.post(r);
        } else {
            r.run();
        }
    }

上面说过,Luancher.java实现了Callback这个回调接口,那我们现在就来分析Luancher是这么实现这些回调的,看Luancher.java的代码

public void startBinding() {
        setWorkspaceLoading(true);
        if (LauncherLog.DEBUG) {
            LauncherLog.d(TAG, "startBinding: this = " + this);
        }

        // If we're starting binding all over again, clear any bind calls we'd postponed in
        // the past (see waitUntilResume) -- we don't need them since we're starting binding
        // from scratch again
        //开始bind前,做一些清除操作
        mBindOnResumeCallbacks.clear();

        // Clear the workspace because it's going to be rebound
        mWorkspace.clearDropTargets();
        mWorkspace.removeAllWorkspaceScreens();

        mWidgetsToAdvance.clear();
        if (mHotseat != null) {
            mHotseat.resetLayout();
        }
        if (LauncherLog.DEBUG) {
            LauncherLog.d(TAG, "startBinding: mIsLoadingWorkspace = " + mIsLoadingWorkspace);
        }
    }

先看bindscreens()方法

@Override
    public void bindScreens(ArrayList orderedScreenIds) {
        //调用这个方法,继续看这个方法
        bindAddScreens(orderedScreenIds);

        // If there are no screens, we need to have an empty screen
        if (orderedScreenIds.size() == 0) {
            mWorkspace.addExtraEmptyScreen();
        }

        // Create the custom content page (this call updates mDefaultScreen which calls
        // setCurrentPage() so ensure that all pages are added before calling this).
        if (hasCustomContentToLeft()) {
            mWorkspace.createCustomContentContainer();
            populateCustomContentContainer();
        }
    }

先来看一下桌面的界面布局,用一张图来展示,图片截取于http://blog.csdn.net/yanbober/article/details/50525559,如有侵权请告知
Android Launcher加载流程源码分析_第1张图片
Workspace继承于PagedView,是一个自定义的ViewGroup,用来滑动,每一个屏幕就是一个CellLayout
理解了这些,下面的代码就很容易看懂了

@Override
    public void bindAddScreens(ArrayList orderedScreenIds) {
        int count = orderedScreenIds.size();
        //for循环,有多少屏就add多少CellLayout,
        for (int i = 0; i < count; i++) {
            mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
        }
    }

继续来看binditems()方法

public void bindItems(final ArrayList shortcuts, final int start, final int end,
                          final boolean forceAnimateIcons) {
        /*下面的这两段代码会经常用到,它的作用就是保证桌面可见即执行onresume之后才执行这些方法
        如果桌面不可见,这个waitUntilResume(r)会把这个runnable放入集合中,等到执行onresume
        的时候去执行这个线程*/
        Runnable r = new Runnable() {
            public void run() {
                bindItems(shortcuts, start, end, forceAnimateIcons);
            }
        };
        if (waitUntilResume(r)) {
            return;
        }

        // Get the list of added shortcuts and intersect them with the set of shortcuts here
        final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
        final Collection bounceAnims = new ArrayList();
        final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
        Workspace workspace = mWorkspace;
        long newShortcutsScreenId = -1;
        for (int i = start; i < end; i++) {
            final ItemInfo item = shortcuts.get(i);
            if (LauncherLog.DEBUG) {
                LauncherLog.d(TAG, "bindItems: start = " + start + ", end = " + end
                        + "item = " + item + ", this = " + this);
            }

            // Short circuit if we are loading dock items for a configuration which has no dock
            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                    mHotseat == null) {
                continue;
            }

            final View view;
            switch (item.itemType) {
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                    ShortcutInfo info = (ShortcutInfo) item;
                    view = createShortcut(info);//创建app快捷方式的view

                    /*
                     * TODO: FIX collision case
                     */
                    if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                        CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
                        if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
                            View v = cl.getChildAt(item.cellX, item.cellY);
                            Object tag = v.getTag();
                            String desc = "Collision while binding workspace item: " + item
                                    + ". Collides with " + tag;
                            if (LauncherAppState.isDogfoodBuild()) {
                                throw (new RuntimeException(desc));
                            } else {
                                Log.d(TAG, desc);
                            }
                        }
                    }
                    break;
                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                    //创建文件夹的view
                    view = FolderIcon.fromXml(R.layout.folder_icon, this,
                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                            (FolderInfo) item, mIconCache);
                    break;
                default:
                    throw new RuntimeException("Invalid Item Type");
            }

            //将view添加进workspace中
            workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
                    item.cellY, 1, 1);
            if (animateIcons) {
                // Animate all the applications up now
                view.setAlpha(0f);
                view.setScaleX(0f);
                view.setScaleY(0f);
                bounceAnims.add(createNewAppBounceAnimation(view, i));
                newShortcutsScreenId = item.screenId;
            }
        }

        ...
        workspace.requestLayout();
    }

bindfolders()方法

public void bindFolders(final LongArrayMap folders) {
      if (LauncherLog.DEBUG) {
            LauncherLog.d(TAG, "bindFolders: this = " + this);
       }

       Runnable r = new Runnable() {
            public void run() {
                bindFolders(folders);
            }
        };
        if (waitUntilResume(r)) {
            return;
        }
        /*并没有做什么操作,只是clone文件夹集合,因为在上面的binditems中的创建文件夹view
        FolderIcon.fromXml(R.layout.folder_icon, this, (ViewGroup)已经把
        打开folder的布局初始化好了,具体的大家可以去看仔细的代码*/
        sFolders = folders.clone();
    }

还有一个bindWidget()这个方法略微复杂,这里就不说了,代码流程差不多,具体的大家可以去看详细的代码,最后还一个收尾的finishBindingItems方法

public void finishBindingItems() {
        if (LauncherLog.DEBUG) {
            LauncherLog.d(TAG, "finishBindingItems: mSavedState = " + mSavedState
                + ", mSavedInstanceState = " + mSavedInstanceState + ", this = " + this);
        }
        Runnable r = new Runnable() {
            public void run() {
                finishBindingItems();
            }
        };
        if (waitUntilResume(r)) {
            return;
        }
        if (mSavedState != null) {
            if (!mWorkspace.hasFocus()) {
                View view = mWorkspace.getChildAt(mWorkspace.getCurrentPage());
                if (view != null) {
                    view.requestFocus();
                }
            }
            mSavedState = null;
        }

        mWorkspace.restoreInstanceStateForRemainingPages();

        setWorkspaceLoading(false);//设置状态
        sendLoadingCompleteBroadcastIfNecessary();//launcher是否首次启动,保存状态

        // If we received the result of any pending adds while the loader was running (e.g. the
        // widget configuration forced an orientation change), process them now.
        if (sPendingAddItem != null) {
            final long screenId = completeAdd(sPendingAddItem);

            // TODO: this moves the user to the page where the pending item was added. Ideally,
            // the screen would be guaranteed to exist after bind, and the page would be set through
            // the workspace restore process.
            mWorkspace.post(new Runnable() {
                @Override
                public void run() {
                    mWorkspace.snapToScreenId(screenId);
                }
            });
            sPendingAddItem = null;
        }

        InstallShortcutReceiver.disableAndFlushInstallQueue(this);

        if (mLauncherCallbacks != null) {
            mLauncherCallbacks.finishBindingItems(false);
        }
    }

以上这些就是Launcher界面的主要加载流程,里面还有很多的细节都没有讲,比如workspace,celllayout怎么添加这些view,还有桌面的滑动snap,拖拽drag流程,还有Launcher的各种模式,文件夹等。后面有时间会继续分析。
这是我第一次发博客,里面难免会有些错误或没考虑的问题,希望大家指出

你可能感兴趣的:(Launcher)