抱歉,你查看的文章不存在

android 4.0 启动 Launcher 分析(1)

1.配置文件 (1)packege 属性可指定生成的gen源文件夹的包名,同时也表示程序运行时的进程名称

original-package 表示源码中真实的源代码层次结构 当 original-package和package相同时,声明Activity的时候可以使用.ClassName方式。否则需要使用完整的类名声明。当包名发生变更时,通过original-package来通知系统—-包名需要变更。旧包对应的数据文件也将重命名为original-package指定的包名。这样就能保证数据可延续使用。original-package只能用于system.img中包含的软件。

(2) permission标签用来声明一个权限被其他程序声明访问的权限

Declares a security permission that can be used to limit access to specific components or features of this or other applications

(3)

<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"/>
``` plain
Activity声明如上的意图过滤器可实现home的启动器功能,在按home键之后弹出对应的程序供用户选择
(4)
 ```xml
 <application android:name="com.android.launcher2.LauncherApplication"
``` plain
 …… 该标签中 android:name属性配置的类会在程序运行时被初始化,即为代码中使用getApplication()方法获得的对象
2.Launcher启动流程 从清单文件中得知com.android.launcher2.Launcher为launcher的主要activity。 以及LauncherApplication继承了Application类,在程序初始化的时候需要先加载此类。
Launcher和普通activity一样,其生命周期如下
(1) LauncherApplication.java 由程序初始化时启动,初始化部分设备的参数以及注册广播接收者等等
```java
public void onCreate() \{ super.onCreate();
    // set sIsScreenXLarge and sScreenDensity *before* creating icon cache
        //判断是否是大屏幕(最小边长>720px)
        sIsScreenLarge = getResources().getBoolean(R.bool.is_large_screen);
        //获取屏幕密度
        sScreenDensity = getResources().getDisplayMetrics().density;
        //新建icon缓存
        mIconCache = new IconCache(this);
        mModel = new LauncherModel(this, mIconCache);
        //动态注册广播接收者(app的卸载,安装和改变)
        // Register intent receivers
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addDataScheme("package");
        //动态注册广播接收者(app是否可用,地域变化,屏幕方向变化等等)
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
        registerReceiver(mModel, filter);
        //动态注册广播接收者(搜索提供者变化等等)
        filter = new IntentFilter();
        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
        registerReceiver(mModel, filter);
    
        // Register for changes to the favorites
        ContentResolver resolver = getContentResolver();
        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
                mFavoritesObserver);
    }
``` plain
(2) Launcher.java 由程序初始化时启动,初始化view的布局以及启动后续的操作
 ```java
 protected void onCreate(Bundle savedInstanceState) \{ …… super.onCreate(savedInstanceState); //获取application对象
LauncherApplication app = ((LauncherApplication)getApplication()); mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(), Context.MODE\_PRIVATE); //获取launchermodel对象,封装了数据库操作等 mModel = app.setLauncher(this); //获取应用图标的缓存集合 mIconCache = app.getIconCache(); mDragController = new DragController(this); mInflater = getLayoutInflater();
    mAppWidgetManager = AppWidgetManager.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 anothe
        // LauncherModel load.
        mPaused = false;
    
        if (PROFILE_STARTUP) {
            android.os.Debug.startMethodTracing(
                    Environment.getExternalStorageDirectory() + "/launcher");
        }
//校验本地设置是否更改 checkForLocaleChange(); setContentView(R.layout.launcher); //初始化页面的各个控件 setupViews(); //控制是否显示Cling画面,仅在第一次使用(或清空数据)之后展现guide画面 showFirstRunWorkspaceCling(); //注册ContentResolver registerContentObservers();
    lockAllApps();
//恢复状态 mSavedState = savedInstanceState; restoreState(mSavedState); //恢复状态后更新widget // Update customization drawer *after* restoring the states if (mAppsCustomizeContent != null) \{ mAppsCustomizeContent.onPackagesUpdated(); \}
    if (PROFILE_STARTUP) {
            android.os.Debug.stopMethodTracing();
        }
    
        if (!mRestoring) {
            if (sPausedFromUserAction) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                //需要重新绑定所有worksapce页面
                mModel.startLoader(true, -1);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                //需要重新绑定当前worksapce页面
                mModel.startLoader(true, mWorkspace.getCurrentPage());
            }
        }
//校验allapps是否已加载 if (!mModel.isAllAppsLoaded()) \{ ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent(); mInflater.inflate(R.layout.apps\_customize\_progressbar, appsCustomizeContentParent); \}
    // For handling default keys
        mDefaultKeySsb = new SpannableStringBuilder();
        Selection.setSelection(mDefaultKeySsb, 0);
//注册广播接收者 IntentFilter filter = new IntentFilter(Intent.ACTION\_CLOSE\_SYSTEM\_DIALOGS); registerReceiver(mCloseSystemDialogsReceiver, filter); //更新search图标,app市场图标等 updateGlobalIcons();
    // On large interfaces, we want the screen to auto-rotate based on the current orientation
        unlockScreenOrientation(true);
    }

(3) LauncherModel.java

实现加载workspace的celllayout和allapps的功能

public void startLoader(boolean isLaunching, int synchronousBindPage) \{ synchronized (mLock) \{ if (DEBUG\_LOADERS) \{ Log.d(TAG, "startLoader isLaunching=" + isLaunching); \}
    // Clear any deferred bind-runnables from the synchronized load process
            // We must do this before any loading/binding is scheduled below.
            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.
                // also, don't downgrade isLaunching if we're already running
                isLaunching = isLaunching || stopLoaderLocked();
                mLoaderTask = new LoaderTask(mApp, isLaunching);
                //此时只是预加载某个页面
                if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                } else {
                //此时只是加载全部所有的celllayout和allapps
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    sWorker.post(mLoaderTask);
                }
            }
        }
    }

当 synchronousBindPage为-1时,表示需要加载所有的页面(跳至步骤8),而synchronousBindPage>-1则是需要预加载某个页面(跳至步骤4)

(4)

LauncherModel.java

将加载分为加载workspace和加载allapps画面

void runBindSynchronousPage(int synchronousBindPage) \{ ……
    synchronized (mLock) {
                if (mIsLoaderTaskRunning) {
                    // Ensure that we are never running the background loading at this point since
                    // we also touch the background collections
                    throw new RuntimeException("Error! Background loading is already running");
                }
            }
    
            // XXX: Throw an exception if we are already loading (since we touch the worker thread
            //      data structures, we can't allow any other thread to touch that data, but because
            //      this call is synchronous, we can get away with not locking).
    
            // The LauncherModel is static in the LauncherApplication and mHandler may have queued
            // operations from the previous activity.  We need to ensure that all queued operations
            // are executed before any synchronous binding work is done.
            mHandler.flush();
    
            // Divide the set of loaded items into those that we are binding synchronously, and
            // everything else that is to be bound normally (asynchronously).
            //异步绑定workspace页面,优先加载当前页面
            bindWorkspace(synchronousBindPage);
            // XXX: For now, continue posting the binding of AllApps as there are other issues that
            //      arise from that.
            onlyBindAllApps();
        }

(5) LauncherModel.java

以下代码中callbacks即为Launcher的weakreference。launcher实现了callbacks的接口。当内存紧张的时候会被虚拟机回收此块内存。造成Launcher重启的现象(在widget的grid画面尤其明显,当快速旋转屏幕时,不停加载widgets造成内存使用过多,callbacks被gc。不使用weakreference则会导致oom报错)

实现绑定对应page的celllayout。 synchronizeBindPage>-1时异步加载。加载完成后依次调用callbacks的startBinding(),onPageBoundSynchronously(currentScreen),finishBindingItems().可以在launcher里实现相应的加载每一步之后的操作。

/\*\* \* Binds all loaded data to actual views on the main thread. \*/ private void bindWorkspace(int synchronizeBindPage) \{ final long t = SystemClock.uptimeMillis(); Runnable r;
    // Don't use these two variables in any of the callback runnables.
            // Otherwise we hold a reference to them.
            final Callbacks oldCallbacks = mCallbacks.get();
            if (oldCallbacks == null) {
                // This launcher has exited and nobody bothered to tell us.  Just bail.
                Log.w(TAG, "LoaderTask running with no launcher");
                return;
            }
    
            final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
            final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
                oldCallbacks.getCurrentWorkspaceScreen();
    
            // Load all the items that are on the current page first (and in the process, unbind
            // all the existing workspace items before we call startBinding() below.
            unbindWorkspaceItemsOnMainThread();
            ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
            ArrayList<LauncherAppWidgetInfo> appWidgets =
                    new ArrayList<LauncherAppWidgetInfo>();
            HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
            HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
            synchronized (sBgLock) {
                workspaceItems.addAll(sBgWorkspaceItems);
                appWidgets.addAll(sBgAppWidgets);
                folders.putAll(sBgFolders);
                itemsIdMap.putAll(sBgItemsIdMap);
            }
    
            ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
            ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
            ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
                    new ArrayList<LauncherAppWidgetInfo>();
            ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
                    new ArrayList<LauncherAppWidgetInfo>();
            HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
            HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
    
            // Separate the items that are on the current screen, and all the other remaining items
            //填充当前workspace的cellayout和hotseat的appItem
            filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
                    otherWorkspaceItems);
            //填充当前workspace的cellayout的wigdet
            filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
                    otherAppWidgets);
            //填充当前workspace的cellayout和hotseat的folder
            filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
                    otherFolders);
            sortWorkspaceItemsSpatially(currentWorkspaceItems);
            sortWorkspaceItemsSpatially(otherWorkspaceItems);
    
            // Tell the workspace that we're about to start binding items
            //此时准备向workspace画面添加item
            r = new Runnable() {
                public void run() {
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        //weakreference当被gc的时候怎么通知?此时需要重启
                        //调用Launcher的startBinding
                        callbacks.startBinding();
                    }
                }
            };
            runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
    
            // Load items on the current page
            bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
                    currentFolders, null);
            if (isLoadingSynchronously) {
                r = new Runnable() {
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.onPageBoundSynchronously(currentScreen);
                        }
                    }
                };
                runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
            }
    
            // Load all the remaining pages (if we are loading synchronously, we want to defer this
            // work until after the first render)
            mDeferredBindRunnables.clear();
            //加载其他页面(除当前页面)
            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
                    (/**如果加载的是全部页面则为false否则为true**/isLoadingSynchronously ? mDeferredBindRunnables : null));
    
            // Tell the workspace that we're done binding items
            //结束launcher的绑定item过程
            r = new Runnable() {
                public void run() {
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        callbacks.finishBindingItems();
                    }
    
                    // 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");
                    }
    
                    mIsLoadingAndBindingWorkspace = false;
                }
            };
            if (isLoadingSynchronously) {
                mDeferredBindRunnables.add(r);
            } else {
                runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
            }
        }

(6) Launcher.java

开始准备绑定,清除掉所有celllayout的子view(其实是ShortcutAndWidgetContainer的子 view,ShortcutAndWidgetContainer是celllayout的唯一的子view) 正常来说 WorkSpace-》CellLayout-》ShortcutAndWidgetContainer-》ItemInfo

/\*\* \* Refreshes the shortcuts shown on the workspace. \* \* Implementation of the method from LauncherModel.Callbacks. \*/ public void startBinding() \{ //开始准备绑定items final Workspace workspace = mWorkspace;
    mNewShortcutAnimatePage = -1;
        mNewShortcutAnimateViews.clear();
        mWorkspace.clearDropTargets();
        int count = workspace.getChildCount();
        //移除所有的cellLayout的子view
        for (int i = 0; i < count; i++) {
            // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
            final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
            layoutParent.removeAllViewsInLayout();
        }
        mWidgetsToAdvance.clear();
        if (mHotseat != null) {
            mHotseat.resetLayout();
        }
    }

(7) Launcher.java

callbacks最后一步,完成所有的绑定工作

/\*\* \* Callback saying that there aren't any more items to bind. \* \* Implementation of the method from LauncherModel.Callbacks. \*/ public void finishBindingItems() \{ setLoadOnResume(); //binditem 完成 if (mSavedState != null) \{ if (!mWorkspace.hasFocus()) \{ mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus(); \} //所有状态已恢复?清空savestate mSavedState = null; \}
    mWorkspace.restoreInstanceStateForRemainingPages();
        //当worksapce加载是用户添加item到worksapce则将其加入到sPendingAddList中,此时可以处理添加事件
        // 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.
        for (int i = 0; i < sPendingAddList.size(); i++) {
            completeAdd(sPendingAddList.get(i));
        }
        sPendingAddList.clear();
    
        // Update the market app icon as necessary (the other icons will be managed in response to
        // package changes in bindSearchablesChanged()
        updateAppMarketIcon();
    
        // Animate up any icons as necessary
        if (mVisible || mWorkspaceLoading) {
            Runnable newAppsRunnable = new Runnable() {
                [@Override](https://my.oschina.net/u/1162528)
                public void run() {
                    //新app初始动画?
                    runNewAppsAnimation(false);
                }
            };
    
            boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
                    mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
            if (canRunNewAppsAnimation()) {
                // If the user has not interacted recently, then either snap to the new page to show
                // the new-apps animation or just run them if they are to appear on the current page
                if (willSnapPage) {
                    mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
                } else {
                    runNewAppsAnimation(false);
                }
            } else {
                // If the user has interacted recently, then just add the items in place if they
                // are on another page (or just normally if they are added to the current page)
                runNewAppsAnimation(willSnapPage);
            }
        }
    
        mWorkspaceLoading = false;
    }

(8) LauncherModel.java

此时执行的是LoaderTask的run方法(实现了runnable接口),当前我显示的allapps画面则需要先加载allapps画面,再加载workspace画面。反之,则先加载workspace画面再加在allapps画面. loadAndBindWorkspace()方法会调用步骤五的方法,其参数为-1,表示需要加载全部页面。

public void run() \{ synchronized (mLock) \{ mIsLoaderTaskRunning = true; \} // 前台UI是allapps则先加载allapps画面,否则加载workspace // 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). final Callbacks cbk = mCallbacks.get(); //callback被回收了则默认使用加载workspace,否则按照allapps是否在前台来加载 final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
    keep_running: {
                // Elevate priority when Home launches for the first time to avoid
                // starving at boot time. Staring at a blank home is not cool.
                synchronized (mLock) {
                    if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
                            (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
                    android.os.Process.setThreadPriority(mIsLaunching
                            ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
                }
                if (loadWorkspaceFirst) {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                    //先加载workspace
                    loadAndBindWorkspace();
                } else {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
                    //先加载allapps
                    loadAndBindAllApps();
                }
    
                if (mStopped) {
                    break keep_running;
                }
    
               
                ……
                waitForIdle();
    
                // second step
                if (loadWorkspaceFirst) {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                    loadAndBindAllApps();
                } else {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
                    loadAndBindWorkspace();
                }
    
                // Restore the default thread priority after we are done loading items
                synchronized (mLock) {
                    android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                }
            }
    
    
            // Update the saved icons if necessary
            if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
            synchronized (sBgLock) {
                for (Object key : sBgDbIconCache.keySet()) {
                    updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
                }
                sBgDbIconCache.clear();
            }
    
            // Clear out this reference, otherwise we end up holding it until all of the
            // callback runnables are done.
            mContext = null;
    
            synchronized (mLock) {
                // If we are still the last one to be scheduled, remove ourselves.
                if (mLoaderTask == this) {
                    mLoaderTask = null;
                }
                mIsLoaderTaskRunning = false;
            }
        }

(9) LauncherModel.java 该方法需要在第一次被调用时通过调用 loadWorkspace方法加载对应资源目录下的default_workspace.xml文件。该文件描述 workspace里对应位置的app,widget等等(也包括hotseat), ```java private void loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true;

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

        if (!mWorkspaceLoaded) {
            loadWorkspace();
            synchronized (LoaderTask.this) {
                if (mStopped) {
                    return;
                }
                mWorkspaceLoaded = true;
            }
        }

        // Bind the workspace
        bindWorkspace(-1);
    }

plain (10) LauncherModel.java //加载workspacejava private void loadWorkspace() { final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;

final Context context = mContext;
        final ContentResolver contentResolver = context.getContentResolver();
        final PackageManager manager = context.getPackageManager();
        final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
        final boolean isSafeMode = manager.isSafeMode();

        // Make sure the default workspace is loaded, if needed
        // 判断是否准备加载默认的default_workspace.xml里
        mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);

        synchronized (sBgLock) {
            sBgWorkspaceItems.clear();
            sBgAppWidgets.clear();
            sBgFolders.clear();
            sBgItemsIdMap.clear();
            sBgDbIconCache.clear();

            final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
            //查找需要展现在桌面上的app及widget
            final Cursor c = contentResolver.query(
                    LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);

            // +1 for the hotseat (it can be larger than the workspace)
            // Load workspace in reverse order to ensure that latest items are loaded first (and
            // before any earlier duplicates)
            final ItemInfo occupied[][][] =
                    new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];

            try {
                final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
                final int intentIndex = c.getColumnIndexOrThrow
                        (LauncherSettings.Favorites.INTENT);
                final int titleIndex = c.getColumnIndexOrThrow
                        (LauncherSettings.Favorites.TITLE);
                final int iconTypeIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.ICON_TYPE);
                final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
                final int iconPackageIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.ICON_PACKAGE);
                final int iconResourceIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.ICON_RESOURCE);
                final int containerIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.CONTAINER);
                final int itemTypeIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.ITEM_TYPE);
                final int appWidgetIdIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.APPWIDGET_ID);
                final int screenIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.SCREEN);
                final int cellXIndex = c.getColumnIndexOrThrow
                        (LauncherSettings.Favorites.CELLX);
                final int cellYIndex = c.getColumnIndexOrThrow
                        (LauncherSettings.Favorites.CELLY);
                final int spanXIndex = c.getColumnIndexOrThrow
                        (LauncherSettings.Favorites.SPANX);
                final int spanYIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.SPANY);
                //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
                //final int displayModeIndex = c.getColumnIndexOrThrow(
                //        LauncherSettings.Favorites.DISPLAY_MODE);

                ShortcutInfo info;
                String intentDescription;
                LauncherAppWidgetInfo appWidgetInfo;
                int container;
                long id;
                Intent intent;

                while (!mStopped && c.moveToNext()) {
                    try {
                        int itemType = c.getInt(itemTypeIndex);

                        switch (itemType) {
                        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                            intentDescription = c.getString(intentIndex);
                            try {
                                intent = Intent.parseUri(intentDescription, 0);
                            } catch (URISyntaxException e) {
                                continue;
                            }

                            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                                info = getShortcutInfo(manager, intent, context, c, iconIndex,
                                        titleIndex, mLabelCache);
                            } else {
                                info = getShortcutInfo(c, context, iconTypeIndex,
                                        iconPackageIndex, iconResourceIndex, iconIndex,
                                        titleIndex);

                                // 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.intent = intent;
                                info.id = c.getLong(idIndex);
                                container = c.getInt(containerIndex);
                                info.container = container;
                                info.screen = c.getInt(screenIndex);
                                info.cellX = c.getInt(cellXIndex);
                                info.cellY = c.getInt(cellYIndex);

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

                                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);

                                // now that we've loaded everthing re-save it with the
                                // icon in case it disappears somehow.
                                queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
                            } else {
                                // Failed to load the shortcut, probably because the
                                // activity manager couldn't resolve it (maybe the app
                                // was uninstalled), or the db row was somehow screwed up.
                                // Delete it.
                                id = c.getLong(idIndex);
                                Log.e(TAG, "Error loading shortcut " + id + ", removing it");
                                contentResolver.delete(LauncherSettings.Favorites.getContentUri(
                                            id, false), null, null);
                            }
                            break;

                        case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                            id = c.getLong(idIndex);
                            FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);

                            folderInfo.title = c.getString(titleIndex);
                            folderInfo.id = id;
                            container = c.getInt(containerIndex);
                            folderInfo.container = container;
                            folderInfo.screen = c.getInt(screenIndex);
                            folderInfo.cellX = c.getInt(cellXIndex);
                            folderInfo.cellY = c.getInt(cellYIndex);

                            // check & update map of what's occupied
                            if (!checkItemPlacement(occupied, folderInfo)) {
                                break;
                            }
                            switch (container) {
                                case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
                                    sBgWorkspaceItems.add(folderInfo);
                                    break;
                            }

                            sBgItemsIdMap.put(folderInfo.id, folderInfo);
                            sBgFolders.put(folderInfo.id, folderInfo);
                            break;

                        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                            // Read all Launcher-specific widget details
                            int appWidgetId = c.getInt(appWidgetIdIndex);
                            id = c.getLong(idIndex);

                            final AppWidgetProviderInfo provider =
                                    widgets.getAppWidgetInfo(appWidgetId);

                            if (!isSafeMode && (provider == null || provider.provider == null ||
                                    provider.provider.getPackageName() == null)) {
                                String log = "Deleting widget that isn't installed anymore: id="
                                    + id + " appWidgetId=" + appWidgetId;
                                Log.e(TAG, log);
                                Launcher.sDumpLogs.add(log);
                                itemsToRemove.add(id);
                            } else {
                                appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                        provider.provider);
                                appWidgetInfo.id = id;
                                appWidgetInfo.screen = c.getInt(screenIndex);
                                appWidgetInfo.cellX = c.getInt(cellXIndex);
                                appWidgetInfo.cellY = c.getInt(cellYIndex);
                                appWidgetInfo.spanX = c.getInt(spanXIndex);
                                appWidgetInfo.spanY = c.getInt(spanYIndex);
                                int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
                                appWidgetInfo.minSpanX = minSpan[0];
                                appWidgetInfo.minSpanY = minSpan[1];

                                container = c.getInt(containerIndex);
                                if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                    container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                                    Log.e(TAG, "Widget found where container != " +
                                        "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
                                    continue;
                                }
                                appWidgetInfo.container = c.getInt(containerIndex);

                                // check & update map of what's occupied
                                if (!checkItemPlacement(occupied, appWidgetInfo)) {
                                    break;
                                }
                                sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
                                sBgAppWidgets.add(appWidgetInfo);
                            }
                            break;
                        }
                    } catch (Exception e) {
                        Log.w(TAG, "Desktop items loading interrupted:", e);
                    }
                }
            } finally {
                c.close();
            }

            if (itemsToRemove.size() > 0) {
                ContentProviderClient client = contentResolver.acquireContentProviderClient(
                                LauncherSettings.Favorites.CONTENT_URI);
                // Remove dead items
                for (long id : itemsToRemove) {
                    if (DEBUG_LOADERS) {
                        Log.d(TAG, "Removed id = " + id);
                    }
                    // Don't notify content observers
                    try {
                        client.delete(LauncherSettings.Favorites.getContentUri(id, false),
                                null, null);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Could not remove id = " + id);
                    }
                }
            }

            if (DEBUG_LOADERS) {
                Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
                Log.d(TAG, "workspace layout: ");
                for (int y = 0; y < mCellCountY; y++) {
                    String line = "";
                    for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
                        if (s > 0) {
                            line += " | ";
                        }
                        for (int x = 0; x < mCellCountX; x++) {
                            line += ((occupied[s][x][y] != null) ? "#" : ".");
                        }
                    }
                    Log.d(TAG, "[ " + line + " ]");
                }
            }
        }
    }

plain (11) WorkSpace.java 当launcher.xml被setContentView时,workspace和hotseat等类也需要初始化。 workspace构造函数如下java /** * Used to inflate the Workspace from XML. * * @param context The application’s context. * @param attrs The attributes set containing the Workspace’s customization values. * @param defStyle Unused. */ public Workspace(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContentIsRefreshable = false; mOriginalPageSpacing = mPageSpacing;

mDragEnforcer = new DropTarget.DragEnforcer(context);
    // With workspace, data is available straight from the get-go
    setDataIsReady();

    mLauncher = (Launcher) context;
    final Resources res = getResources();
    mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
    mFadeInAdjacentScreens = false;
    mWallpaperManager = WallpaperManager.getInstance(context);

    int cellCountX = DEFAULT_CELL_COUNT_X;
    int cellCountY = DEFAULT_CELL_COUNT_Y;

    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.Workspace, defStyle, 0);

    if (LauncherApplication.isScreenLarge()) {
        // 判断是否是大屏幕(最小边长大于720px)
        // Determine number of rows/columns dynamically
        // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
        // Around that ratio we should make cells the same size in portrait and
        // landscape
        TypedArray actionBarSizeTypedArray =
            context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
        final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);

        Point minDims = new Point();
        Point maxDims = new Point();
        mLauncher.getWindowManager().getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
        //手动设置的时候会覆盖此值,是否可以先判断值是否设定?
        cellCountX = 1;
        while (CellLayout.widthInPortrait(res, cellCountX + 1) <= minDims.x) {
            cellCountX++;
        }

        cellCountY = 1;
        while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
            <= minDims.y) {
            cellCountY++;
        }
    }

    mSpringLoadedShrinkFactor =
        res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
    mSpringLoadedPageSpacing =
            res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing);
    mCameraDistance = res.getInteger(R.integer.config_cameraDistance);

    // if the value is manually specified, use that instead
    cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
    cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
    mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
    a.recycle();

    setOnHierarchyChangeListener(this);
    //更新cell的尺寸
    LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
    //打开触摸反馈
    setHapticFeedbackEnabled(false);
    //初始化workspace,包括壁纸的设定等等
    initWorkspace();

    // Disable multitouch across the workspace/all apps/customize tray
    setMotionEventSplittingEnabled(true);

    // Unless otherwise specified this view is important for accessibility.
    if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
        setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    }
}

plain (12) WorkSpace.java 初始化workspace的部分属性,包括壁纸的设定等等java /**

 * Initializes various states for this workspace.
 */
protected void initWorkspace() {
    Context context = getContext();
    mCurrentPage = mDefaultPage;
    //当前页设置为默认页
    Launcher.setScreen(mCurrentPage);
    LauncherApplication app = (LauncherApplication)context.getApplicationContext();
    //保存应用图片的缓存
    mIconCache = app.getIconCache();
    //显示workspace的边界,通常来说viewgroup是不需要描绘边界的
    setWillNotDraw(false);
    //设置子view绘图缓存开启
    setChildrenDrawnWithCacheEnabled(true);

    final Resources res = getResources();
    try {
        mBackground = res.getDrawable(R.drawable.apps_customize_bg);
    } catch (Resources.NotFoundException e) {
        // In this case, we will skip drawing background protection
    }
    //wallPaper 偏移
    mWallpaperOffset = new WallpaperOffsetInterpolator();
    Display display = mLauncher.getWindowManager().getDefaultDisplay();
    //获取屏幕大小,此方法在android 4.0之前不支持
    display.getSize(mDisplaySize);
    mWallpaperTravelWidth = (int) (mDisplaySize.x *
            wallpaperTravelToScreenWidthRatio(mDisplaySize.x, mDisplaySize.y));

    mMaxDistanceForFolderCreation = (0.55f * res.getDimensionPixelSize(R.dimen.app_icon_size));
    mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
}

``` plain

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

后端之路

0 篇文章0 人订阅

相关文章

来自专栏潇涧技术专栏

Head First Android SwipeRefreshLayout

本文内容和代码参考自Implementing Swipe to Refresh, an Android Material Design UI Pattern,原...

1022
来自专栏程序员宝库

Android屏幕截图,View截图(干货)

691
来自专栏俞其荣的博客

Android Architecture Component之Lifecycle解析HeaderLifecyclePart 1Part 2Part 3Footer

终于到了最后的关头,Android Architecture Component 系列的最后一节内容。今天给大家带来的就是 Lifecycle 的解析。

1644
来自专栏三流程序员的挣扎

Android 单元测试 Robolectric

通过实现一套 JVM 能够运行的 Android 代码,从而实现脱离 Android 环境进行测试。

2303
来自专栏Android 开发学习

Android Architecture Components 之LiveData, ViewModel

2994
来自专栏郭霖

Android ListView工作原理完全解析,带你从源码的角度彻底理解

在Android所有常用的原生控件当中,用法最复杂的应该就是ListView了,它专门用于处理那种内容元素很多,手机屏幕无法展示出所有内容的情况。ListVie...

36710
来自专栏一个会写诗的程序员的博客

关于Webview如何自动登录保存登录信息

1013
来自专栏向治洪

android异步操作

为了使UI在数据获取时不僵死,数据获取需要在新开Worker线程中进行,然后将返回结果在UI线程中进行读取并渲染页面。面对这种异步处理,到底如何写才简洁,先后面...

2218
来自专栏码匠的流水账

聊聊spring cloud gateway的SetStatusGatewayFilter

本文主要研究下spring cloud gateway的SetStatusGatewayFilter

1291
来自专栏码匠的流水账

聊聊spring cloud gateway的RedirectToGatewayFilter

本文主要研究下spring cloud gateway的RedirectToGatewayFilter

1591

扫码关注云+社区

领取腾讯云代金券