1. 程式人生 > >Android 7.0 Gallery圖庫源碼分析2 - 分析啟動流程

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

rec star bject erro argument hold default add 切換

前面一講解了Gallery啟動Activity以及界面如何繪制,現在開始講解啟動流程的代碼邏輯。
GalleryActivity的onCreate方法中調用initializeByIntent()方法,顧名思義這個方法就是根據Intent事件來初始化的。

 1 private void initializeByIntent() {
 2         Intent intent = getIntent();
 3         String action = intent.getAction();
 4 
 5         if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {
6 startGetContent(intent); 7 } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) { 8 ...... 9 } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action) 10 || ACTION_REVIEW.equalsIgnoreCase(action)){ 11 startViewAction(intent); 12
} else { 13 //我們從桌面啟動應用時Intent的Action是android.intent.action.MAIN,所以會走到這一步 14 startDefaultPage(); 15 } 16 }

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

1 public void startDefaultPage() {
2         ......
3         Bundle data = new Bundle();
4         data.putString(AlbumSetPage.KEY_MEDIA_PATH,
5 getDataManager().getTopSetPath(DataManager.INCLUDE_ALL)); 6 getStateManager().startState(AlbumSetPage.class, data); 7 ...... 8 }

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

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

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

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

 1 @Override
 2     public synchronized DataManager getDataManager() {
 3         if (mDataManager == null) {
 4             //實例化一個DataManager,並且傳入GalleryAppImpl的引用,然後獲取主線程的Handler
 5             mDataManager = new DataManager(this);
 6             //初始化數據源
 7             mDataManager.initializeSourceMap();
 8         }
 9         return mDataManager;
10     }

現在看一下initializeSourceMap方法

 1 public synchronized void initializeSourceMap() {
 2         if (!mSourceMap.isEmpty()) return;
 3         //addSource就是把所有數據源都添加給mSourceMap,mSourceMap是一個HashMap,其key是各數據源的名稱,value就是各數據源的對象。
 4         // 這個添加順序很重要
 5         addSource(new LocalSource(mApplication));
 6         addSource(new PicasaSource(mApplication));
 7         addSource(new ComboSource(mApplication));
 8         addSource(new ClusterSource(mApplication));
 9         addSource(new FilterSource(mApplication));
10         addSource(new SecureSource(mApplication));
11         addSource(new UriSource(mApplication));
12         addSource(new SnailSource(mApplication));
13 
14         if (mActiveCount > 0) {
15             for (MediaSource source : mSourceMap.values()) {
16                 source.resume();
17             }
18         }
19     }

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

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

 1 data.putString(AlbumSetPage.KEY_MEDIA_PATH,
 2                 getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
 3 
 4 public String getTopSetPath(int typeBits) {
 5 
 6         switch (typeBits) {
 7             case INCLUDE_IMAGE: return TOP_IMAGE_SET_PATH;
 8             case INCLUDE_VIDEO: return TOP_VIDEO_SET_PATH;
 9             case INCLUDE_ALL: return TOP_SET_PATH;
10             case INCLUDE_LOCAL_IMAGE_ONLY: return TOP_LOCAL_IMAGE_SET_PATH;
11             case INCLUDE_LOCAL_VIDEO_ONLY: return TOP_LOCAL_VIDEO_SET_PATH;
12             case INCLUDE_LOCAL_ALL_ONLY: return TOP_LOCAL_SET_PATH;
13             default: throw new IllegalArgumentException();
14         }
15     }
16 
17 private static final String TOP_SET_PATH = "/combo/{/local/all,/picasa/all}";

然後將此data傳給AlbumSetPage並啟動此頁面

1 getStateManager().startState(AlbumSetPage.class, data);

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

 1 private Stack<StateEntry> mStack = new Stack<StateEntry>();
 2 
 3 private static class StateEntry {
 4         public Bundle data;
 5         public ActivityState activityState;
 6 
 7         public StateEntry(Bundle data, ActivityState state) {
 8             this.data = data;
 9             this.activityState = state;
10         }
11     }

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

 1 //這裏的klass就是AlbumSetPage類,data就是傳入的數據
 2 public void startState(Class<? extends ActivityState> klass,
 3             Bundle data) {
 4         Log.v(TAG, "startState " + klass);
 5         //獲取AlbumSetPage對象
 6         ActivityState state = null;
 7         try {
 8             state = klass.newInstance();
 9         } catch (Exception e) {
10             throw new AssertionError(e);
11         }
12         //如果棧不為空,將棧頂的ActivityState暫停
13         if (!mStack.isEmpty()) {
14             ActivityState top = getTopState();
15             top.transitionOnNextPause(top.getClass(), klass,
16                     StateTransitionAnimation.Transition.Incoming);
17             if (mIsResumed) top.onPause();
18         }
19         ......
20         //根據mActivity和data初始化AlbumSetPage
21         state.initialize(mActivity, data);
22         //入棧
23         mStack.push(new StateEntry(data, state));
24         //下面兩個方法就關鍵了,用來顯示界面的
25         state.onCreate(data, null);
26         if (mIsResumed) state.resume();
27     }

這裏的state是指AlbumSetPage,我們查看一下AlbumSetPage的onCreate方法

1 @Override
2     public void onCreate(Bundle data, Bundle restoreState) {
3         super.onCreate(data, restoreState);
4         //初始化View
5         initializeViews();
6         //初始化數據
7         initializeData(data);
8         ......
9     }

我們接著看下怎麽初始化View的

 1 private void initializeViews() {
 2         ......
 3         //mConfig是用來設置SlotView的參數,而SlotView就是一個相冊
 4         mConfig = Config.AlbumSetPage.get(mActivity);
 5         //實例化一個SlotView
 6         mSlotView = new SlotView(mActivity, mConfig.slotViewSpec);
 7         //mAlbumSetView是mSlotView的渲染器,控制mSlotView的顯示
 8         mAlbumSetView = new AlbumSetSlotRenderer(
 9                 mActivity, mSelectionManager, mSlotView, mConfig.labelSpec,
10                 mConfig.placeholderColor);
11         //將mAlbumSetView設置給mSlotView
12         mSlotView.setSlotRenderer(mAlbumSetView);
13         //監聽SlotView事件,根據手勢操作做出相應的響應,這個監聽事件的原理後面再分析
14         mSlotView.setListener(new SlotView.SimpleListener() {
15             @Override
16             public void onDown(int index) {
17                 AlbumSetPage.this.onDown(index);
18             }
19 
20             @Override
21             public void onUp(boolean followedByLongPress) {
22                 AlbumSetPage.this.onUp(followedByLongPress);
23             }
24 
25             @Override
26             public void onSingleTapUp(int slotIndex) {
27                 AlbumSetPage.this.onSingleTapUp(slotIndex);
28             }
29 
30             @Override
31             public void onLongTap(int slotIndex) {
32                 AlbumSetPage.this.onLongTap(slotIndex);
33             }
34         });
35         ......
36         //把這個SlotView作為一個子控件傳給GLView,mRootPane是GLView類
37         mRootPane.addComponent(mSlotView);
38     }

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

 1 private void initializeData(Bundle data) {
 2         //獲取data傳入的value
 3         String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
 4         //獲取MediaSet,前面講了每個ActivityState頁面都需要一個數據源MediaSource來獲取顯示數據,而MediaSource是由MediaObject組成,MediaObject相當於MediaSource的單位,MediaSet就是一個MediaObject的子類,管理一組媒體數據
 5         mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
 6         //mSelectionManager用於管理選擇事件
 7         mSelectionManager.setSourceMediaSet(mMediaSet);
 8         //mAlbumSetDataAdapter類似於橋梁來連接頁面和數據源
 9         mAlbumSetDataAdapter = new AlbumSetDataLoader(
10                 mActivity, mMediaSet, DATA_CACHE_SIZE);
11         //設置數據加載的監聽接口
12         mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
13         mAlbumSetView.setModel(mAlbumSetDataAdapter);
14     }

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