轉載請注明出處: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來管理。頁面切換示意圖如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuEXeWRFNzEzLc12bj5ic1dWbp5Savw1LcpDc0RHaiojIsJye.png)
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);
}