天天看點

Android 7.0 Gallery圖庫源碼分析2 - 分析啟動流程

轉載請注明出處:http://blog.csdn.net/lb377463323/article/details/69523891

前面一講解了Gallery啟動Activity以及界面如何繪制,現在開始講解啟動流程的代碼邏輯。

GalleryActivity的onCreate方法中調用initializeByIntent()方法,顧名思義這個方法就是根據Intent事件來初始化的。

private void initializeByIntent() {
        Intent intent = getIntent();
        String action = intent.getAction();

        if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {
            startGetContent(intent);
        } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) {
           ......
        } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
                || ACTION_REVIEW.equalsIgnoreCase(action)){
            startViewAction(intent);
        } else {
            //我們從桌面啟動應用時Intent的Action是android.intent.action.MAIN,是以會走到這一步
            startDefaultPage();
        }
    }
           

我們看一下這個方法,它是通過Bundle來傳輸資料

public void startDefaultPage() {
        ......
        Bundle data = new Bundle();
        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
                getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
        getStateManager().startState(AlbumSetPage.class, data);
        ......
    }
           

上面這個方法涉及的東西很多,AlbumSetPage繼承自ActivityState,顯示所有相冊縮略圖的頁面,也就是進入Gallery顯示的第一個界面。DataManager是資料管理,給每個界面提供資料源。StateManager是用來管理ActivityState的,控制每個界面的建立和銷毀。

首先講一下ActivityState,它是一個抽象類,它的具體實作的子類有AlbumSetPage(顯示所有相冊縮略圖的頁面),AlbumPage(顯示單個相冊所有照片縮略圖的頁面),ManageCachePage(緩存管理頁面),PhotoPage(單張照片),SlideShowPage(滑屏頁面)。進入Gallery顯示的是AlbumSetPage,它是由多個相冊組成的;然後點選某一相冊顯示的AlbumPage,它由該相冊的所有縮略圖組成;再點選其中某一張圖檔顯示的就是PhotoPage。這些頁面的切換都是由StateManager來管理。頁面切換示意圖如下:

Android 7.0 Gallery圖庫源碼分析2 - 分析啟動流程

Gallery中切換界面是不會跳轉Activity的,因為并沒有對應每個界面的Activity,那怎麼更新界面的?其實就是StateManager通過棧(stack)來管理要顯示的界面,每個界面都是一個ActivityState類,切換界面做的就是stack的入棧和出棧操作,是以我們可以把ActivityState想象成是一個Activity。

然後講一下DataManager,看上述代碼,Bundle的value是getDataManager().getTopSetPath(DataManager.INCLUDE_ALL)。

getDataManager()是調用GalleryAppImpl的方法

@Override
    public synchronized DataManager getDataManager() {
        if (mDataManager == null) {
            //執行個體化一個DataManager,并且傳入GalleryAppImpl的引用,然後擷取主線程的Handler
            mDataManager = new DataManager(this);
            //初始化資料源
            mDataManager.initializeSourceMap();
        }
        return mDataManager;
    }
           

現在看一下initializeSourceMap方法

public synchronized void initializeSourceMap() {
        if (!mSourceMap.isEmpty()) return;
        //addSource就是把所有資料源都添加給mSourceMap,mSourceMap是一個HashMap,其key是各資料源的名稱,value就是各資料源的對象。
        // 這個添加順序很重要
        addSource(new LocalSource(mApplication));
        addSource(new PicasaSource(mApplication));
        addSource(new ComboSource(mApplication));
        addSource(new ClusterSource(mApplication));
        addSource(new FilterSource(mApplication));
        addSource(new SecureSource(mApplication));
        addSource(new UriSource(mApplication));
        addSource(new SnailSource(mApplication));

        if (mActiveCount > 0) {
            for (MediaSource source : mSourceMap.values()) {
                source.resume();
            }
        }
    }
           

資料源如上述代碼所示分為七種,由一個HashMap類型的mSourceMap來管理。它們都是繼承自MediaSource抽象類,資料源的作用就是給前面講的ActivityState類型提供縮略圖。至于ActivityState和資料源之間怎麼互動的後面再講。

接着之前的代碼分析,data最終的key是AlbumSetPage.KEY_MEDIA_PATH,value值就是”/combo/{/local/all,/picasa/all}”,見如下代碼

data.putString(AlbumSetPage.KEY_MEDIA_PATH,
                getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));

public String getTopSetPath(int typeBits) {

        switch (typeBits) {
            case INCLUDE_IMAGE: return TOP_IMAGE_SET_PATH;
            case INCLUDE_VIDEO: return TOP_VIDEO_SET_PATH;
            case INCLUDE_ALL: return TOP_SET_PATH;
            case INCLUDE_LOCAL_IMAGE_ONLY: return TOP_LOCAL_IMAGE_SET_PATH;
            case INCLUDE_LOCAL_VIDEO_ONLY: return TOP_LOCAL_VIDEO_SET_PATH;
            case INCLUDE_LOCAL_ALL_ONLY: return TOP_LOCAL_SET_PATH;
            default: throw new IllegalArgumentException();
        }
    }

private static final String TOP_SET_PATH = "/combo/{/local/all,/picasa/all}";
           

然後将此data傳給AlbumSetPage并啟動此頁面

在講startState方法之前,我先講一下StateManager類,它最主要的一點就是持有一個棧來管理ActivityState和上面的data資料,

private Stack<StateEntry> mStack = new Stack<StateEntry>();

private static class StateEntry {
        public Bundle data;
        public ActivityState activityState;

        public StateEntry(Bundle data, ActivityState state) {
            this.data = data;
            this.activityState = state;
        }
    }
           

startState方法所做的就是根據建立一個StateEntry并且push到mStack棧中

//這裡的klass就是AlbumSetPage類,data就是傳入的資料
public void startState(Class<? extends ActivityState> klass,
            Bundle data) {
        Log.v(TAG, "startState " + klass);
        //擷取AlbumSetPage對象
        ActivityState state = null;
        try {
            state = klass.newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
        //如果棧不為空,将棧頂的ActivityState暫停
        if (!mStack.isEmpty()) {
            ActivityState top = getTopState();
            top.transitionOnNextPause(top.getClass(), klass,
                    StateTransitionAnimation.Transition.Incoming);
            if (mIsResumed) top.onPause();
        }
        ......
        //根據mActivity和data初始化AlbumSetPage
        state.initialize(mActivity, data);
        //入棧
        mStack.push(new StateEntry(data, state));
        //下面兩個方法就關鍵了,用來顯示界面的
        state.onCreate(data, null);
        if (mIsResumed) state.resume();
    }
           

這裡的state是指AlbumSetPage,我們檢視一下AlbumSetPage的onCreate方法,

@Override
    public void onCreate(Bundle data, Bundle restoreState) {
        super.onCreate(data, restoreState);
        //初始化View
        initializeViews();
        //初始化資料
        initializeData(data);
        ......
    }
           

我們接着看下怎麼初始化View的

private void initializeViews() {
        ......
        //mConfig是用來設定SlotView的參數,而SlotView就是一個相冊
        mConfig = Config.AlbumSetPage.get(mActivity);
        //執行個體化一個SlotView
        mSlotView = new SlotView(mActivity, mConfig.slotViewSpec);
        //mAlbumSetView是mSlotView的渲染器,控制mSlotView的顯示
        mAlbumSetView = new AlbumSetSlotRenderer(
                mActivity, mSelectionManager, mSlotView, mConfig.labelSpec,
                mConfig.placeholderColor);
        //将mAlbumSetView設定給mSlotView
        mSlotView.setSlotRenderer(mAlbumSetView);
        //監聽SlotView事件,根據手勢操作做出相應的響應,這個監聽事件的原理後面再分析
        mSlotView.setListener(new SlotView.SimpleListener() {
            @Override
            public void onDown(int index) {
                AlbumSetPage.this.onDown(index);
            }

            @Override
            public void onUp(boolean followedByLongPress) {
                AlbumSetPage.this.onUp(followedByLongPress);
            }

            @Override
            public void onSingleTapUp(int slotIndex) {
                AlbumSetPage.this.onSingleTapUp(slotIndex);
            }

            @Override
            public void onLongTap(int slotIndex) {
                AlbumSetPage.this.onLongTap(slotIndex);
            }
        });
        ......
        //把這個SlotView作為一個子控件傳給GLView,mRootPane是GLView類
        mRootPane.addComponent(mSlotView);
    }
           

最後看一下初始化資料,資料都是通過DataManger來管理,至于如何加載後面在分析。

private void initializeData(Bundle data) {
        //擷取data傳入的value
        String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
        //擷取MediaSet,前面講了每個ActivityState頁面都需要一個資料源MediaSource來擷取顯示資料,而MediaSource是由MediaObject組成,MediaObject相當于MediaSource的機關,MediaSet就是一個MediaObject的子類,管理一組媒體資料
        mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
        //mSelectionManager用于管理選擇事件
        mSelectionManager.setSourceMediaSet(mMediaSet);
        //mAlbumSetDataAdapter類似于橋梁來連接配接頁面和資料源
        mAlbumSetDataAdapter = new AlbumSetDataLoader(
                mActivity, mMediaSet, DATA_CACHE_SIZE);
        //設定資料加載的監聽接口
        mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
        mAlbumSetView.setModel(mAlbumSetDataAdapter);
    }