上次我们研究了新的代码和原来2.3的代码的不同处,视频播放器的控制条从Framework中改变到了本地代码中,但是Framework中的控制条并没有删除,因为什么?
为了向下兼容。
另外我们还研究了触摸消息的分发,发现新的触摸分发更为规范一些,并且也没有像以前那样分层了,而是分页面,这样也更好一些,一旦有问题,可以到指定的Page中去找,而不是在透明的层里面到处找代码了。
既然研究完触摸消息,我们再来找下按键消息的处理。
还记得2.3的代码里面是如何处理按键消息的么?
Activity->RenderView->RootLayer->GridInputProcess
里面转了很多次才处理的,我们看下新的代码,找了半天只找到back 按键的处理,在AbstractGalleryActivity里面
@Override
public void onBackPressed() {
// send the back event to the top sub-state
GLRoot root = getGLRoot();
root.lockRenderThread();
try {
getStateManager().onBackPressed();
} finally {
root.unlockRenderThread();
}
}
在StateManager里面我们看到了返回按键的处理,我们先不管通过getTopState是如何拿到当前的页面的
public void onBackPressed() {
if (!mStack.isEmpty()) {
getTopState().onBackPressed();
}
}
以AlbumSetPage页面为例子来看
@Override
public void onBackPressed() {
if (mShowDetails) {
hideDetails();
} else if (mSelectionManager.inSelectionMode()) {
mSelectionManager.leaveSelectionMode();
} else {
super.onBackPressed();
}
}
最后会调用到super.onBackPressed,当然也可能是退出选择状态,我们先看Super是谁?我们说过页面的父类都是ActivityState,看下他的处理
protected void onBackPressed() {
mActivity.getStateManager().finishState(this);
}
mActivity就是自己,看下AbstactGalleryActivity的实现
public synchronized StateManager getStateManager() {
if (mStateManager == null) {
mStateManager = new StateManager(this);
}
return mStateManager;
}
我们又遇到了StateManager.看起来状态管理器必须弄清楚,否则没法看,稍等。
现在只是知道back的处理被转来转去,最后在Page页面中处理,流程大致如下
AbstractGalleryActivity->StateManager->找到顶部的那个页面处理,这个页面里面要处理状态就->ActivityState->StateManager处理状态
其实看起来StateManager就是管理的各个页面的状态的。我们详细看下。
private Stack<StateEntry> mStack = new Stack<StateEntry>();
定义了一个范型的栈,范型的类使用的StateEntry,我们先看下这个类
private static class StateEntry {
public Bundle data;
public ActivityState activityState;
public StateEntry(Bundle data, ActivityState state) {
this.data = data;
this.activityState = state;
}
}
其实就是连个成员,一个是Activity的状态,一个是Bundle数据
我们看下mStack是如何管理这些Page的
public void startState(Class<? extends ActivityState> klass,
Bundle data) {
Log.v(TAG, "startState " + klass);
ActivityState state = null;
try {
state = klass.newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
if (!mStack.isEmpty()) {
ActivityState top = getTopState();
if (mIsResumed) top.onPause();
}
state.initialize(mContext, data);
mStack.push(new StateEntry(data, state));
state.onCreate(data, null);
if (mIsResumed) state.resume();
}
这段代码比较麻烦,传进来一个继承于ActivityState的类,当然就是一个Page了,这个Page现在叫做klass,因为每个Page只是一个类,需要实例化,所以调用了一个实例化的静态方法 klass.newInstance,生产出一个Page的实例出来,叫做state,这个state经过初始化后,被压到这个范型的栈中了。
mStack.push(new StateEntry(data,state));
最后通过state.onCreate(date,null),将数据传到了页面中去,
突然发现整个不就是工厂模式么?比起2.3里面使用层的成员变量来生产一个个的层,是不是更好一些呢?
我写了一个简化版本的类大家理解下
public class Factory{
public static Product startProduct(Class<? extends Product> klass) {
try {
return klass.newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
然后,我们需要找到最上面的那个Page(就是当前使用的Page,因为Page不透明),我们只要知道栈顶是什么就可以了
public ActivityState getTopState() {
Utils.assertTrue(!mStack.isEmpty());
return mStack.peek().activityState;
}
我们明白整个系统中都是用这个范型的栈生产管理Page,那第一个生产出来的页面是什么,又是怎样贴到Activity上去的?我们看Gallery.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_ACTION_BAR);
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.main);
if (savedInstanceState != null) {
getStateManager().restoreFromState(savedInstanceState);
} else {
initializeByIntent();
}
}
saveInstanceState是上次退出的状态,我们先不管,先看第一次走的initalizeByIntent()
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)) {
// We do NOT really support the PICK intent. Handle it as
// the GET_CONTENT. However, we need to translate the type
// in the intent here.
Log.w(TAG, "action PICK is not supported");
String type = Utils.ensureNotNull(intent.getType());
if (type.startsWith("vnd.android.cursor.dir/")) {
if (type.endsWith("/image")) intent.setType("image/*");
if (type.endsWith("/video")) intent.setType("video/*");
}
startGetContent(intent);
} else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
|| ACTION_REVIEW.equalsIgnoreCase(action)){
startViewAction(intent);
} else {
startDefaultPage();
}
}
先不考虑intent发过来处理饿情况,没有Intent就会走最后的startDefaultPage
public void startDefaultPage() {
PicasaSource.showSignInReminder(this);
Bundle data = new Bundle();
data.putString(AlbumSetPage.KEY_MEDIA_PATH,
getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
getStateManager().startState(AlbumSetPage.class, data);
mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);
if (mVersionCheckDialog != null) {
mVersionCheckDialog.setOnCancelListener(this);
}
}
可以看到第一个实例化的页面是AlbumSetPage这个类
我们看到一个页面是怎么进栈的,那么一个页面是如何出栈的呢?
void finishState(ActivityState state) {
// The finish() request could be rejected (only happens under Monkey),
// If it is rejected, we won't close the last page.
if (mStack.size() == 1) {
Activity activity = (Activity) mContext.getAndroidContext();
if (mResult != null) {
activity.setResult(mResult.resultCode, mResult.resultData);
}
activity.finish();
if (!activity.isFinishing()) {
Log.w(TAG, "finish is rejected, keep the last state");
return;
}
Log.v(TAG, "no more state, finish activity");
}
Log.v(TAG, "finishState " + state);
if (state != mStack.peek().activityState) {
if (state.isDestroyed()) {
Log.d(TAG, "The state is already destroyed");
return;
} else {
throw new IllegalArgumentException("The stateview to be finished"
+ " is not at the top of the stack: " + state + ", "
+ mStack.peek().activityState);
}
}
// Remove the top state.
mStack.pop();
state.mIsFinishing = true;
if (mIsResumed) state.onPause();
mContext.getGLRoot().setContentPane(null);
state.onDestroy();
if (!mStack.isEmpty()) {
// Restore the immediately previous state
ActivityState top = mStack.peek().activityState;
if (mIsResumed) top.resume();
}
}
如果是最后一个页面,不光要出栈,还要退出应用,
然后判断要出栈的是否栈顶,必须是栈顶,否则有问题了
最后才是出栈,
然后才是重新设置当前页面。
进和出我们都看到了,页面和页面之间的切换实际上是一进一出,只要调用如下接口即可,比如AlbumPage-->PhotoPage
mActivity.getStateManager().startStateForResult(
PhotoPage.class, REQUEST_PHOTO, data);
主要是下面三个页面,相册集页面 相册页面 单张图片页面
+ ------------ + + ------- + + ---------- +
| Fullscreen |---> | Album |--->| AlbumSet |
| Photo | | Page | | Page |
+ ------------ + + ------- + + ---------- +