android 4.0 启动 Launcher 分析(1)

原创
2013/09/23 10:38
阅读数 4.7K

1.配置文件 (1) <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.launcher"> packege 属性可指定生成的gen源文件夹的包名,同时也表示程序运行时的进程名称

<original-package android:name="com.android.launcher2" /> 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"/>

Activity声明如上的意图过滤器可实现home的启动器功能,在按home键之后弹出对应的程序供用户选择

(4) <application android:name="com.android.launcher2.LauncherApplication" …… 该标签中 android:name属性配置的类会在程序运行时被初始化,即为代码中使用getApplication()方法获得的对象

2.Launcher启动流程 从清单文件中得知com.android.launcher2.Launcher为launcher的主要activity。 以及LauncherApplication继承了Application类,在程序初始化的时候需要先加载此类。

Launcher和普通activity一样,其生命周期如下

(1) LauncherApplication.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);
}

(2) Launcher.java 由程序初始化时启动,初始化view的布局以及启动后续的操作 @Override 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 another
    // 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), 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);
    }

(10) LauncherModel.java //加载workspace 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 + " ]");
                }
            }
        }
    }

(11) WorkSpace.java 当launcher.xml被setContentView时,workspace和hotseat等类也需要初始化。 workspace构造函数如下 /** * 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);
    }
}

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

/**
 * 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);
}
展开阅读全文
加载中
点击加入讨论🔥(1) 发布并加入讨论🔥
打赏
1 评论
10 收藏
0
分享
返回顶部
顶部