前面一讲解了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来管理。页面切换示意图如下

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并启动此页面

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

在讲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);
}

Android 7.0 Gallery图库源码分析2 - 分析启动流程的更多相关文章

  1. Android 7.0 Gallery图库源码分析3 - 数据加载及显示流程

    前面分析Gallery启动流程时,说了传给DataManager的data的key是AlbumSetPage.KEY_MEDIA_PATH,value值,是”/combo/{/local/all,/p ...

  2. Android 7.0 Gallery图库源码分析1 - 初识Gallery源码

    分析一个项目的源代码时,第一件事就是查看清单文件,找到程序入口,我们从Gallery2源码的清单文件中可以看到GalleryActivity是此应用的启动Activity. <activity ...

  3. Android 7.0 Gallery图库源码分析4 - SlotView手势监听及页面跳转

    上篇文章讲了初始化View时会实例化一个SlotView并监听其事件,至于它是怎么实现的,用的是Android自带的GestureDetector. GestureDetector是Android自带 ...

  4. Cocos2dx源码赏析(1)之启动流程与主循环

    Cocos2dx源码赏析(1)之启动流程与主循环 我们知道Cocos2dx是一款开源的跨平台游戏引擎,而学习开源项目一个较实用的办法就是读源码.所谓,"源码之前,了无秘密".而笔者 ...

  5. ThinkPHP5.0源码学习之框架启动流程

    ThinkPHP5框架的启动流程图如下: ThinkPHP5的启动流程按照文件分为三步: 1.请求入口(public/index.php) 2.框架启动(thinkphp/start.php) 3.应 ...

  6. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  7. Android -- Camera源码简析,启动流程

    com.android.camera.Camera.java,主要的实现Activity,继承于ActivityBase. ActivityBase 在ActivityBase中执行流程: onCre ...

  8. wordpress学习二:源码目录结构和启动流程

    wordpress安装后的文件目录如下: 其中的主要目录和文件用途介绍如下: wp-admin:用于进行博客后台设置的功能目录 wp-content: wordpress的 主题,插件和本地化的存储目 ...

  9. Android Small插件化框架源码分析

    Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...

随机推荐

  1. Django之瀑布流

    一. 小功能瀑布流的实现 1.完成效果图 2.代码部分 <1>models.py from django.db import models # Create your models her ...

  2. 【Tool】Mac环境维护

    1. 安装编译opencv https://blog.csdn.net/lijiang1991/article/details/50756065 /Users/yuhua.cheng/Opt/open ...

  3. docker数据卷的使用 -v --volumes--from

    总结一下docker数据管理的三种方法: 1.普通的挂在数据: -v docker run  -v /father/path:/child/path-v 参数会把当前系统的文件目录/father/pa ...

  4. Mybaitis-generator生成数据对象和时间的优化

    1.本章涉及到知识点,Mybaitis-generator生成数据对象和时间,xml的引用*.properties 外部文件(在这之前必须导入了mybaitis的核心架包) A.在pom.xml的案例 ...

  5. HttpClient异步请求Post传递Json

    HttpClient异步Post请求,HttpClient.PostAsync(String, HttpContent, CancellationToken),String为Post的Url,Http ...

  6. [Linux]第二部分-linux文件磁盘格式

    账户信息在/etc/passwd中,密码在/etc/shadow中,组信息在etc/group中 (d/-)rwxrwxrwx 1 root 293 Oct 19 21:24 test 文件属性 连接 ...

  7. Java 序列化 和 反序列--by Vincent

    序列化: Java 提供了一种把对象序列化的机制. 即把一个对象可以被表示为一个字节序列.然后可以写入文件保存. 字节序列包括:      该对象的数据.有关对象的类型的信息和存储在对象中数据的类型. ...

  8. 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t

    菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

  9. 浅谈Model1 VS Model2

    在学习drp的过程中就对model1和model2产生了浓厚的兴趣,一直都没有好好总结下,接下来就和大家一起学习下它们的真面目. 一.是什么? 首先大家提到model想到了什么?它的名词意识是:模型: ...

  10. 0x41 并查集

    太菜了才做到并查集啊啊啊啊啊啊啊啊啊啊啊 还是很有收获的说 水 bzoj4195: [Noi2015]程序自动分析 好题 poj1456 感受到并查集传递性的美妙啊!对于一个商品,去找他过期前那天的集 ...