Android 7.0 Gallery圖庫源碼分析2 - 分析啟動流程
前面一講解了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 - 分析啟動流程