android6.0 Launcher2應用解析

來源:互聯網
上載者:User

標籤:

在之前我們分析了Android6.0系統在啟動時安裝應用程式的過程,這些應用程式安裝好之後,Launcher應用就負責把它們在案頭上展示出來。


一、AMS啟動Launcher

Launcher應用是在AMS的systemReady方法中直接調用startHomeActivityLocked啟動的,下面是systemReady啟動Launcher的代碼。

startHomeActivityLocked(mCurrentUserId, "systemReady");
我們來看下這個函數,先調用了getHomeIntent方法來擷取Intent,然後也是調用resolveActivityInfo函數從PKMS擷取ActivityInfo,接著當進程沒有啟動的話,調用ActivityStackSupervisor的startHomeActivity函數

    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();//擷取intent        ActivityInfo aInfo =            resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//擷取ActivityInfo        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) {//進程沒有啟動調用                EventLog.writeEvent(EventLogTags.AM_PROC_START,"AMS -> startHomeActivityLocked startHomeActivity then startActivityLock : "+ aInfo.processName);                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);                mStackSupervisor.startHomeActivity(intent, aInfo, reason);            }        }        return true;    }

我們先來看看getHomeIntent這個函數。

    Intent getHomeIntent() {        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);        intent.setComponent(mTopComponent);        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {            intent.addCategory(Intent.CATEGORY_HOME);        }        return intent;    }
然後我們來看下ActivityStackSupervisor的startHomeActivity函數,它也是調用了startActivityLocked來啟動Activity的,在之前的部落格分析過這個函數這裡我們就不介紹了。

    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {        moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);        startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,                null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,                null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,                null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,                0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,                false /* componentSpecified */,                null /* outActivity */, null /* container */,  null /* inTask */);        if (inResumeTopActivity) {            // If we are in resume section already, home activity will be initialized, but not            // resumed (to avoid recursive resume) and will stay that way until something pokes it            // again. We need to schedule another resume.            scheduleResumeTopActivities();        }    }


二、Launcher啟動

接著我們來看下Launcher的AndroidManifest.xml,我們看下其主Activity有一個category為android.intent.category.HOME

    <application        android:name="com.android.launcher2.LauncherApplication"        android:label="@string/application_name"        android:icon="@mipmap/ic_launcher_home"        android:hardwareAccelerated="true"        android:largeHeap="@bool/config_largeHeap"        android:supportsRtl="true">        <activity            android:name="com.android.launcher2.Launcher"            android:launchMode="singleTask"            android:clearTaskOnLaunch="true"            android:stateNotNeeded="true"            android:resumeWhilePausing="true"            android:theme="@style/Theme"            android:windowSoftInputMode="adjustPan"            android:screenOrientation="nosensor">             <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.java的onCreate函數中調用了mModel.startLoader函數

    protected void onCreate(Bundle savedInstanceState) {        ......        if (!mRestoring) {            if (sPausedFromUserAction) {                // If the user leaves launcher, then we should just load items asynchronously when                // they return.                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                mModel.startLoader(true, mWorkspace.getCurrentPage());            }        }        ......

startLoader函數會post一個Runnable訊息,我們來看下它的run方法

    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 {                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);                    sWorker.post(mLoaderTask);                }            }        }    }

在它的run方法中會調用loadAndBindAllApps函數,在loadAndBindAllApps函數中又會調用loadAllAppsByBatch函數

        public void run() {            synchronized (mLock) {                mIsLoaderTaskRunning = true;            }           final Callbacks cbk = mCallbacks.get();           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"));                    Process.setThreadPriority(mIsLaunching                            ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);                }                // First step. Load workspace first, this is necessary since adding of apps from                // managed profile in all apps is deferred until onResume. See http://b/17336902.                if (loadWorkspaceFirst) {                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");                    loadAndBindWorkspace();                } else {                    Log.d(TAG, "step 1: special: loading  all apps");                    loadAndBindAllApps();                }

我們先來看下loadAndBindAllApps函數,這個函數先進入while迴圈,然後調用了LauncherApps的getActivityList函數,後面又會調用callbacks的bindAllApplications

        private void loadAllAppsByBatch() {            final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;            ......            mBgAllAppsList.clear();            final int profileCount = profiles.size();            for (int p = 0; p < profileCount; p++) {                ......                while (i < N && !mStopped) {                    if (i == 0) {                        final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;                        apps = mLauncherApps.getActivityList(null, user);                        ......                                                                 mHandler.post(new Runnable() {                        public void run() {                            final long t = SystemClock.uptimeMillis();                            if (callbacks != null) {                                if (firstProfile) {                                    callbacks.bindAllApplications(added);                                } else {                                    callbacks.bindAppsAdded(added);                                }                                if (DEBUG_LOADERS) {                                    Log.d(TAG, "bound " + added.size() + " apps in "                                        + (SystemClock.uptimeMillis() - t) + "ms");                                }                            } else {                                Log.i(TAG, "not binding apps: no Launcher activity");                            }                        }                    });                    ......

我們先來看LauncherApps的getActivityList函數,它先用mService成員變數調用getLauncherActivities函數擷取到list<ResolveInfo>,然後封裝在ArrayList<LauncherActivityInfo> 中。

    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {        List<ResolveInfo> activities = null;        try {            activities = mService.getLauncherActivities(packageName, user);        } catch (RemoteException re) {            throw new RuntimeException("Failed to call LauncherAppsService");        }        if (activities == null) {            return Collections.EMPTY_LIST;        }        ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();        final int count = activities.size();        for (int i = 0; i < count; i++) {            ResolveInfo ri = activities.get(i);            long firstInstallTime = 0;            try {                firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,                    PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;            } catch (NameNotFoundException nnfe) {                // Sorry, can't find package            }            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,                    firstInstallTime);            if (DEBUG) {                Log.v(TAG, "Returning activity for profile " + user + " : "                        + lai.getComponentName());            }            lais.add(lai);        }        return lais;    }

其service是class LauncherAppsImpl extends ILauncherApps.Stub 下面是getLauncherActivities函數,肯定也是通過PKMS來擷取相關Activity的ResolveInfo的。

        @Override        public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)                throws RemoteException {            ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);            if (!isUserEnabled(user)) {                return new ArrayList<ResolveInfo>();            }            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);            mainIntent.setPackage(packageName);            long ident = Binder.clearCallingIdentity();            try {                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */,                        user.getIdentifier());                return apps;            } finally {                Binder.restoreCallingIdentity(ident);            }        }

最後回調Launcher.java的bindAllApplications函數,最後在這個函數中可以在案頭上展示系統中所有的應用程式了。

    public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {        Runnable setAllAppsRunnable = new Runnable() {            public void run() {                if (mAppsCustomizeContent != null) {                    mAppsCustomizeContent.setApps(apps);                }            }        };        // Remove the progress bar entirely; we could also make it GONE        // but better to remove it since we know it's not going to be used        View progressBar = mAppsCustomizeTabHost.            findViewById(R.id.apps_customize_progress_bar);        if (progressBar != null) {            ((ViewGroup)progressBar.getParent()).removeView(progressBar);            // We just post the call to setApps so the user sees the progress bar            // disappear-- otherwise, it just looks like the progress bar froze            // which doesn't look great            mAppsCustomizeTabHost.post(setAllAppsRunnable);        } else {            // If we did not initialize the spinner in onCreate, then we can directly set the            // list of applications without waiting for any progress bars views to be hidden.            setAllAppsRunnable.run();        }    }


三、顯示應用表徵圖

我們再來看下Launcher的onClick函數,當調用showWorkspace可以顯示所有應用的表徵圖。

    public void onClick(View v) {        // Make sure that rogue clicks don't get through while allapps is launching, or after the        // view has detached (it's possible for this to happen if the view is removed mid touch).        if (v.getWindowToken() == null) {            return;        }        if (!mWorkspace.isFinishedSwitchingState()) {            return;        }        Object tag = v.getTag();        if (tag instanceof ShortcutInfo) {            // Open shortcut            final Intent intent = ((ShortcutInfo) tag).intent;            int[] pos = new int[2];            v.getLocationOnScreen(pos);            intent.setSourceBounds(new Rect(pos[0], pos[1],                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));            boolean success = startActivitySafely(v, intent, tag);            if (success && v instanceof BubbleTextView) {                mWaitingForResume = (BubbleTextView) v;                mWaitingForResume.setStayPressed(true);            }        } else if (tag instanceof FolderInfo) {            if (v instanceof FolderIcon) {                FolderIcon fi = (FolderIcon) v;                handleFolderClick(fi);            }        } else if (v == mAllAppsButton) {            if (isAllAppsVisible()) {                showWorkspace(true);            } else {                onClickAllAppsButton(v);            }        }    }

在showWorkspace中會顯示所有的表徵圖

    void showWorkspace(boolean animated, Runnable onCompleteRunnable) {        if (mState != State.WORKSPACE) {            boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);            mWorkspace.setVisibility(View.VISIBLE);            hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);            // Show the search bar (only animate if we were showing the drop target bar in spring            // loaded mode)            if (mSearchDropTargetBar != null) {                mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);            }            // We only need to animate in the dock divider if we're going from spring loaded mode            showDockDivider(animated && wasInSpringLoadedMode);            // Set focus to the AppsCustomize button            if (mAllAppsButton != null) {                mAllAppsButton.requestFocus();            }        }        mWorkspace.flashScrollingIndicator(animated);        // Change the state *after* we've called all the transition code        mState = State.WORKSPACE;        // Resume the auto-advance of widgets        mUserPresent = true;        updateRunning();        // Send an accessibility event to announce the context change        getWindow().getDecorView()                .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);    }

而點擊應用表徵圖,最終會調用Launcher.java的startActivitySafely來啟動應用。這裡調用的startActivity就是Activity的startActivity函數。

    boolean startActivitySafely(View v, Intent intent, Object tag) {        boolean success = false;        try {            success = startActivity(v, intent, tag);        } catch (ActivityNotFoundException e) {            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);        }        return success;    }


android6.0 Launcher2應用解析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.