在开篇之前,大家开发中应该都会有这么一种思考吧,怎么样让你的布局变成动态的?何为动态?即是在不发表新版本的前提下,让你的界面在用户看来变了,然而他们没有更新app,也许你会说可以直接h5吧,好这种方案的确可行,但是如果h5过多,那还用app开发人员干嘛,不好,是不是感觉饭碗被抢了,当然你也可以把h5也学了,抢他们饭碗,但是h5的速度毕竟赶不上原生的,那么有没有好的解决方案呢?好吧,现在插件化非常的火,可以用它解决,只需要把里面的动态加载资源部分的代码拿出来改造一下就ok了,这不失为一种好的解决方案。
那还有没有其他的方案的?既然数据是从服务器获取的,那么布局文件是不是也可以在服务器获取,答案是肯定的,我们只需要把下载的布局文件保存在程序缓存内,然后将它解析出来就ok了,当然这部分解析,你可以参考LayoutInflater源码解析xml,你也可以直接用自带的api直接加载,不过这里你放的布局文件必须是编译完的,也就是二进制文件,这个原理和动态加载apk的资源类似。模拟代码如下:
AssetManager asset = getResources().getAssets();
Class c = AssetManager.class;
Method method= c.getMethod("addAssetPath",String.class);
int cookie = (Integer) method.invoke(asset, Environment.getExternalStorageDirectory().getPath());
XmlResourceParser paser= asset.openXmlResourceParser(cookie,"activity_main.xml");
为什么系统要采用二进制文件的加载?当然是为了减少占用空间了,通俗的说就是给压缩了。那么Virtualview-Android采用哪种方式呢?大家在研究源码的时候首先应该明确自己的目的,并不是源码的所有的东西,我们都需要捋一遍,因为看源码有时只是为了解决当前遇到的棘手的问题或者为了学习人家的思想、又或者对它的某种功能感兴趣。
下面正式开始Virtualview-Android旅行吧,首先将框架提供的demo导入后,发现这么一些文件,如下
看到这些文件不禁会联想到,xml是做布局用的,json是做布局data用的,然而又发现了这个
这里的每一个代码文件里面只有这么两三行代码,如下
public static final byte[] BIN = new byte[] {
65, 76, 73, 86, 86, 0, 1, 0, 0, 0, 1, 0, 0, 0, 47, 0, 0, 4, -12, 0, 0, 5, 35, 0, 0, 1, 124, 0, 0, 6, -93, 0, 0, 0, 0, 0, 0, 6, -89, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 5, 86, 84, 101, 120, 116, 4, -25, 0, 0, 2, 4, -86, 50, -11, -48, 0, 0, 0, 0, 0, 47, -1, 108, 0, 0, 0, 48, 92, -43, -16, -15, -1, -1, -1, -1, 119, 112, -84, -68, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 82, -14, -15, -117, 0, 0, 1, 0, 0, 8, 4, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 54, 40, -36, 92, 0, 0, 1, 0, 0, 8, 4, -60, 45, 58, -50, 0, 0, 0, 16, 92, -43, -16, -15, 0, 0, 0, 20, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 62, 29, 48, 56, 0, 0, 1, 0, 0, 8, 4, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, -1, 0, 0, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 78, -85, -57, 2, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -63, 127, 28, -124, 0, 0, 0, 1, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 112, 33, -64, -17, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -63, 127, 28, -124, 0, 0, 0, 8, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 95, 9, -101, 109, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -63, 127, 28, -124, 0, 0, 0, 2, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -41, 80, 18, -95, 0, 0, 1, 0, 0, 8, 5, 43, 21, -122, -105, -1, 0, -120, -1, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 1, 44, 44, -124, -6, 63, -128, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -110, 78, 117, 111, 0, 0, 1, 0, 0, 8, 6, 43, 21, -122, -105, -1, 0, -120, -1, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, -1, 0, 35, 119, 112, -84, -68, -1, -1, -1, -2, -64, -101, 46, 54, -1, 51, 51, 51, 0, 2, 80, 106, -5, -34, 64, -96, 0, 0, 44, 44, -124, -6, 0, 0, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -56, 86, -22, 27, 0, 0, 1, 0, 0, 8, 6, 43, 21, -122, -105, -1, 0, -120, -1, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, 16, -72, 114, 78, 0, 0, 0, 36, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 1, 44, 44, -124, -6, 63, -128, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 0, 0, 8, 5, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, 7, 18, 5, 12, 0, 0, 1, 0, 0, 8, 6, -60, 45, 58, -50, 0, 0, 0, 12, 92, -43, -16, -15, 0, 0, 0, 20, -80, -104, 85, 46, -1, 0, -120, -103, 16, -72, 114, 78, 0, 0, 0, 36, 119, 112, -84, -68, -1, -1, -1, -1, -64, -101, 46, 54, -1, 51, 51, 51, 0, 0, 0, 1, 0, 54, 69, 45, -52, 64, 91, -120, 0, 0, 1, 1, 0, 0, 0, 11, -110, 78, 117, 111, 0, 61, 84, 105, 116, 108, 101, 58, 32, 98, 111, 114, 100, 101, 114, 87, 105, 100, 116, 104, 32, 61, 32, 49, 32, 98, 111, 114, 100, 101, 114, 67, 111, 108, 111, 114, 32, 61, 32, 35, 48, 48, 56, 56, 70, 70, 32, 98, 111, 114, 100, 101, 114, 82, 97, 100, 105, 117, 115, 32, 61, 32, 53, 95, 9, -101, 109, 0, 25, 84, 105, 116, 108, 101, 58, 32, 116, 101, 120, 116, 83, 116, 121, 108, 101, 32, 61, 32, 105, 116, 97, 108, 105, 99, 62, 29, 48, 56, 0, 26, 84, 105, 116, 108, 101, 58, 32, 116, 101, 120, 116, 67, 111, 108, 111, 114, 32, 61, 32, 35, 70, 70, 48, 48, 48, 48, -52, 64, 91, -120, 0, 18, 72, 101, 108, 108, 111, 32, 86, 105, 114, 116, 117, 97, 108, 32, 86, 105, 101, 119, 78, -85, -57, 2, 0, 23, 84, 105, 116, 108, 101, 58, 32, 116, 101, 120, 116, 83, 116, 121, 108, 101, 32, 61, 32, 98, 111, 108, 100, -56, 86, -22, 27, 0, 34, 84, 105, 116, 108, 101, 58, 32, 103, 114, 97, 118, 105, 116, 121, 32, 61, 32, 104, 95, 99, 101, 110, 116, 101, 114, 124, 118, 95, 99, 101, 110, 116, 101, 114, 112, 33, -64, -17, 0, 25, 84, 105, 116, 108, 101, 58, 32, 116, 101, 120, 116, 83, 116, 121, 108, 101, 32, 61, 32, 115, 116, 114, 105, 107, 101, -41, 80, 18, -95, 0, 44, 84, 105, 116, 108, 101, 58, 32, 98, 111, 114, 100, 101, 114, 87, 105, 100, 116, 104, 32, 61, 32, 49, 32, 98, 111, 114, 100, 101, 114, 67, 111, 108, 111, 114, 32, 61, 32, 35, 48, 48, 56, 56, 70, 70, 54, 40, -36, 92, 0, 20, 84, 105, 116, 108, 101, 58, 32, 116, 101, 120, 116, 83, 105, 122, 101, 32, 61, 32, 49, 54, 82, -14, -15, -117, 0, 11, 84, 105, 116, 108, 101, 58, 32, 116, 101, 120, 116, 7, 18, 5, 12, 0, 27, 84, 105, 116, 108, 101, 58, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 32, 61, 32, 35, 48, 48, 56, 56, 57, 57, 0, 0, 0, 0,
};
什么鬼?字节码,有意思,这不禁让我脑袋一大,难道这些文件是阿里用了某个工具把xml生成的,好吧,看看github上有介绍吗?如下
加载模板数据,利用 VirtualView Tools 编译出二进制文件,在初始化的时候加载,有两张方式,一种是直接加载二进制字节数组(推荐):
viewManager.loadBinBufferSync(TMALLCOMPONENT1.BIN);
viewManager.loadBinBufferSync(TMALLCOMPONENT2.BIN);
......
额,只是用阿里的tools工具将xml生成的二进制文件,然后放在服务器,每次更新需要重新下载的时候将xml下载到本地,然后进行加载解析显示,什么时候去下载,这是个问题,不过你可以在app空闲的时候去检查是否有最新版本的布局文件没,即本地文件有没有过期,过期了则重新下载,这里所指的空闲状态是指你app退到后台的时候,或者你也可以选择充电的时候,或者连接wifi的时候,当然连接wifi情况下下载,是为了给用户省流量,或者屏幕锁屏的时候,当然这些方式都各有利弊,它们的共同点都是在app主动去调用的服务端。当然也可以让服务器在更新布局文件的时候通知客户端,客户端选择更新哪一个文件。
ok,继续,虽然阿里文档介绍了是VirtualView Tools 的作用,但是这个他没有开源啊,想看都看不到啊,只能反向推导了,这个例子中根布局是固定的,采用直接将二进制数据解析view添加到根布局的方式进行界面显示,如下
Layout.Params p = iContainer.getVirtualView().getComLayoutParams();
LinearLayout.LayoutParams marginLayoutParams = new LinearLayout.LayoutParams(p.mLayoutWidth, p.mLayoutHeight);
marginLayoutParams.leftMargin = p.mLayoutMarginLeft;
marginLayoutParams.topMargin = p.mLayoutMarginTop;
marginLayoutParams.rightMargin = p.mLayoutMarginRight;
marginLayoutParams.bottomMargin = p.mLayoutMarginBottom;
mLinearLayout.addView(container, marginLayoutParams);
那么框架第一步肯定是将二进制文件进行解析,如下
sVafContext = new VafContext(this.getApplicationContext());
VafContext.loadImageLoader(sVafContext.getContext());
sViewManager = sVafContext.getViewManager();
sViewManager.init(this.getApplicationContext());
sViewManager.loadBinBufferSync(NTEXT.BIN);
然后将解析的数据映射成view,如下
View container = sVafContext.getContainerService().getContainer(name, true);
最后绑定数据
IContainer iContainer = (IContainer)container;
if (!TextUtils.isEmpty(data)) {
JSONObject json = getJSONDataFromAsset(data);
if (json != null) {
iContainer.getVirtualView().setVData(json);
}
}
额,这看起来有点像MVVM模式啊。
好,接下里先看第一步解析,这里以最常用的TextView映射为导火索,然后点燃它,额.....
public int loadFromBuffer(byte[] buf) {
int ret = -1;
if (null != buf) {
mDepPageIds = null;
if (buf.length > 27) {
// check tag
int tagLen = Common.TAG.length();
//前5个字节是标签
String tag = new String(buf, 0, tagLen);
if (TextUtils.equals(Common.TAG, tag)) {
CodeReader reader = new CodeReader();
reader.setCode(buf);
reader.seekBy(tagLen);
// check version
//主版本号
int majorVersion = reader.readShort();
//此版本号
int minorVersion = reader.readShort();
//补丁版本号
int patchVersion = reader.readShort();
reader.setPatchVersion(patchVersion);
//前11个字节是固定的有木有
//版本号判断
if ((Common.MAJOR_VERSION == majorVersion) && (Common.MINOR_VERSION == minorVersion)) {
//ui标签偏移位置
int uiStartPos = reader.readInt();
reader.seekBy(4);
//
int strStartPos = reader.readInt();
reader.seekBy(4);
int exprCodeStartPos = reader.readInt();
reader.seekBy(4);
//自定义view(扩展view开始的位置)
int extraStartPos = reader.readInt();
reader.seekBy(4);
//页面id
int pageId = reader.readShort();
//页面嵌套深度
int depPageCount = reader.readShort();
if (depPageCount > 0) {
mDepPageIds = new int[depPageCount];
for (int i = 0; i < depPageCount; ++i) {
//深度配置id
mDepPageIds[i] = reader.readShort();
}
}
//定位到ui开始的二进制文件
if (reader.seek(uiStartPos)) {
// parse ui codes
boolean result = mUiCodeLoader.loadFromBuffer(reader, pageId);
// 开始解析字符串的属性
if (reader.getPos() == strStartPos) {
if (null != mStringLoader) {
result = mStringLoader.loadFromBuffer(reader, pageId);
} else {
Log.e(TAG, "mStringManager is null");
}
} else {
Log.e(TAG, "string pos error:" + strStartPos + " read pos:" + reader.getPos());
}
// 扩展属性
if (reader.getPos() == exprCodeStartPos) {
if (null != mExprCodeLoader) {
result = mExprCodeLoader.loadFromBuffer(reader, pageId);
} else {
Log.e(TAG, "mExprCodeStore is null");
}
} else {
Log.e(TAG, "expr pos error:" + exprCodeStartPos + " read pos:" + reader.getPos());
}
// load extra data
if (reader.getPos() == extraStartPos) {
} else {
Log.e(TAG, "extra pos error:" + extraStartPos + " read pos:" + reader.getPos());
}
if (result) {
ret = pageId;
}
}
} else {
Log.e(TAG, "version dismatch");
}
} else {
Log.e(TAG, "loadFromBuffer failed tag is invalidate:" + tag);
}
} else {
Log.e(TAG, "file len invalidate:" + buf.length);
}
} else {
Log.e(TAG, "buf is null");
}
return ret;
}
上面这个方法就是解析二进制文件的内容了,首先前5个字节是固定的,也就是 "ALIVV"这个字符串的字节码,这里用到了一个读取字节的工具类 CodeReader ,进去瞧一瞧
这个类没什么特别之处,就是简单的字节操作,如下
读取单个字节:
public byte readByte() {
if (null != mCode && mCurIndex < mCount) {
return mCode[mCurIndex++];
} else {
Log.e(TAG, "readByte error mCode:" + mCode + " mCurIndex:"
+ mCurIndex + " mCount:" + mCount);
return -1;
}
}
读取两个字节:
public short readShort() {
if (null != mCode && mCurIndex < mCount - 1) {
return (short)(((mCode[mCurIndex++] & 0xff) << 8) | (mCode[mCurIndex++] & 0xff));
} else {
Log.e(TAG, "readShort error mCode:" + mCode + " mCurIndex:"
+ mCurIndex + " mCount:" + mCount);
return -1;
}
}
读取四个字节:
public int readInt() {
if (null != mCode && mCurIndex < mCount - 3) {
return ((mCode[mCurIndex++] & 0xff) << 24) |
((mCode[mCurIndex++] & 0xff) << 16) |
((mCode[mCurIndex++] & 0xff) << 8) |
((mCode[mCurIndex++] & 0xff));
} else {
Log.e(TAG, "readInt error mCode:" + mCode + " mCurIndex:"
+ mCurIndex + " mCount:" + mCount);
return -1;
}
}
可见利用了高效的字节平移法(左移),这里生成字节文件时,是高位在前的。ok,继续上面的解析:如果前五个字节满足我们设置的固定的tag(也就是标记这个二进制文件是我们自己生成的,换句话说我们不可能去加载别人的二进制文件吧,也有满足我们自己规则的二进制文件才可以被我们加载),然后读取字节文件的版本号(主版本号、次版本号、补丁版本(假如换成其他的了)),然后读取ui标签开始的位置、字符串开始的位置(假如用TextView、Button等是不是得有(text))、代码标签开始的位置(也就是你为某个标签假如动态值的时候需要json配合
{${titleColor} ? ${titleColor}),然后读取页面pageId(这里的pageId我们可以随机生成(例如文件名字加随机数)(为了区分加载的哪一个文件)),然后读取嵌套深度id,然后定位到标签开始处,开始解析标签位置
public boolean loadFromBuffer(CodeReader reader, int pageId) {
boolean ret = true;
int tabIndex = pageId;
if (tabIndex < Common.MAX_TAB_SIZE) {
ArrayMap<String, CodeReader> typeToCodeReader = mCodeReaders[tabIndex];
if (null == typeToCodeReader) {
typeToCodeReader = new ArrayMap<>();
mCodeReaders[tabIndex] = typeToCodeReader;
}
ArrayMap<String, Integer> typeToPos = mUiTab[tabIndex];
if (null == typeToPos) {
typeToPos = new ArrayMap<>();
mUiTab[tabIndex] = typeToPos;
}
//得到ui标签的数量
int count = reader.readInt();
Log.w(TAG, "load view count: " + count);
for(int i = 0; i < count; ++i) {
//前两个字节声明名字标签的大小
short nameSize = reader.readShort();
//得到名字
String name = new String(reader.getCode(), reader.getPos(), nameSize, Charset.forName("UTF-8"));
Log.w(TAG, "load view name " + name);
//将名字放入集合
typeToCodeReader.put(name, reader);
//跳过名字
reader.seekBy(nameSize);
//
short uiCodeSize = reader.readShort();
//保存名字标签下面属性所在的位置
typeToPos.put(name, reader.getPos());
if (!reader.seekBy(uiCodeSize) ) {
ret = false;
Log.e(TAG, "seekBy error:" + uiCodeSize + " i:" + i);
break;
}
}
} else {
ret = false;
}
return ret;
}
这个方法做的主要工作就是遍历ui标签,然后将标签名字保存到集合和标签开始位置保存到集合(毕竟接下来还有属性需要读取吗),通俗的说这个方法就是把二进制文件以pageId区分开保存到集合中,然后记录这个二进制文件的每个标签开始的位置,因为我们要从这个标签中映射出View和它的属性吗。
接下来就是字符串的读取了,如下:
public boolean loadFromBuffer(CodeReader reader, int pageId) {
boolean ret = true;
mCurPage = pageId;
int totalSize = reader.getMaxSize();
int count = reader.readInt();
for (int i = 0; i < count; ++i) {
int id = reader.readInt();
int len = reader.readShort();
int pos = reader.getPos();
if (pos + len <= totalSize) {
String str = new String(reader.getCode(), reader.getPos(), len);
mIndex2String.put(id, str);
mString2Index.put(str, id);
reader.seekBy(len);
} else {
Log.e(TAG, "read string over");
ret = false;
break;
}
}
return ret;
}
字符串读取相对简单许多,直接根据读取的字符串长度,将接下里的 字符串长度字节转化为字符串,然后将字符串以字符id(这里的id是为了区分是哪一个标签控件的内容的)为以准保存到集合中,接下来就是扩展性属性的解析了,原理一样这里不再探讨。
最后就是从这些字节码中得到我们想要的映射View,add进我们的布局了,也就是下面这个方法:
View container = sVafContext.getContainerService().getContainer(name, true);
name属性是代表标签view的名字,当然这个规则是这个框架限定的,例如下面这个标签
<VText
id="2"
layoutGravity="h_center"
text="${title}"
textSize="12"
textColor="@{${titleColor} ? ${titleColor} : #333333 }"
layoutWidth="wrap_content"
layoutHeight="wrap_content"/>
最终就会被映射为TextView,来看一看它是怎么映射的吧
public ViewBase newView(String type, SparseArray<ViewBase> uuidContainers) {
ViewBase ret = null;
if (null != mLoader) {
//得到当前类型的字节读取器
CodeReader cr = mUiCodeLoader.getCode(type);
if (null != cr) {
mComArr.clear();
ViewBase curView = null;
//读取标签
int tag = cr.readByte();
int state = STATE_continue;
ViewCache viewCache = new ViewCache();
while (true) {
switch (tag) {
//bi标签开始为0
case Common.CODE_START_TAG:
//读取
short comID = cr.readShort();
//从缓存中获取View
ViewBase view = createView(mAppContext, comID, viewCache);
if (null != view) {
Layout.Params p;
if (null != curView) {
p = ((Layout)curView).generateParams();
mComArr.push(curView);
} else {
p = new Layout.Params();
}
view.setComLayoutParams(p);
curView = view;
// int
//读取属性数量
byte attrCount = cr.readByte();
while (attrCount > 0) {
int key = cr.readInt();
int value = cr.readInt();
//获取view属性
view.setValue(key, value);
--attrCount;
}
// int RP
attrCount = cr.readByte();
while (attrCount > 0) {
int key = cr.readInt();
int value = cr.readInt();
view.setRPValue(key, value);
--attrCount;
}
// float
attrCount = cr.readByte();
while (attrCount > 0) {
int key = cr.readInt();
float value = Float.intBitsToFloat(cr.readInt());
view.setValue(key, value);
--attrCount;
}
// float RP
attrCount = cr.readByte();
while (attrCount > 0) {
int key = cr.readInt();
float value = Float.intBitsToFloat(cr.readInt());
view.setRPValue(key, value);
--attrCount;
}
// string code
attrCount = cr.readByte();
while (attrCount > 0) {
int key = cr.readInt();
int value = cr.readInt();
view.setStrValue(key, value);
--attrCount;
}
// expr code
attrCount = cr.readByte();
while (attrCount > 0) {
int key = cr.readInt();
int value = cr.readInt();
view.setValue(key, mExprCodeLoader.get(value));
--attrCount;
}
// user var
attrCount = cr.readByte();
while (attrCount > 0) {
int varType = cr.readByte();
int nameId = cr.readInt();
int value = cr.readInt();
view.addUserVar(varType, nameId, value);
--attrCount;
}
int uuid = view.getUuid();
if (uuid > 0 && null != uuidContainers) {
uuidContainers.put(uuid, view);
}
List<Item> pendingItems = viewCache.getCacheItem(view);
if (pendingItems == null || pendingItems.isEmpty()) {
view.onParseValueFinished();
}
} else {
state = STATE_failed;
Log.e(TAG, "can not find view id:" + comID);
}
break;
case Common.CODE_END_TAG:
if (mComArr.size() > 0) {
ViewBase c = mComArr.pop();
if (c instanceof Layout) {
((Layout) c).addView(curView);
} else {
state = STATE_failed;
Log.e(TAG, "com can not contain subcomponent");
}
curView = c;
} else {
// can break;
state = STATE_successful;
}
break;
default:
Log.e(TAG, "invalidate tag type:" + tag);
state = STATE_failed;
break;
}
if (STATE_continue != state) {
break;
} else {
tag = cr.readByte();
}
}
if (STATE_successful == state) {
ret = curView;
cr.seek(Common.TAG.length() + 4);
int version = cr.readShort();
ret.setVersion(version );
}
} else {
Log.e(TAG, "can not find component type:" + type);
}
} else {
Log.e(TAG, "loader is null");
}
return ret;
}
这个方法又开始解析字节了,只不过这次直接从缓存中获取的,带有目的性的,比如我们想获得 VText, 根据标签名字得到这个字节码的开始位置,开始读取,首先读取int属性变量,然后是float,然后是string,最后是扩展的代码读取,注意这里有个 comID,如下
mBuilders.put(Common.VIEW_ID_FrameLayout, new FrameLayout.Builder());
mBuilders.put(Common.VIEW_ID_GridLayout, new GridLayout.Builder());
mBuilders.put(Common.VIEW_ID_VHLayout, new VHLayout.Builder());
mBuilders.put(Common.VIEW_ID_FlexLayout, new FlexLayout.Builder());
mBuilders.put(Common.VIEW_ID_RatioLayout, new RatioLayout.Builder());
mBuilders.put(Common.VIEW_ID_VH2Layout, new VH2Layout.Builder());
mBuilders.put(Common.VIEW_ID_NativeText, new NativeText.Builder());
mBuilders.put(Common.VIEW_ID_VirtualText, new VirtualText.Builder());
mBuilders.put(Common.VIEW_ID_NativeImage, new NativeImage.Builder());
mBuilders.put(Common.VIEW_ID_VirtualImage, new VirtualImage.Builder());
mBuilders.put(Common.VIEW_ID_VirtualLine, new VirtualLine.Builder());
mBuilders.put(Common.VIEW_ID_Scroller, new Scroller.Builder());
mBuilders.put(Common.VIEW_ID_Page, new Page.Builder());
mBuilders.put(Common.VIEW_ID_Grid, new Grid.Builder());
mBuilders.put(Common.VIEW_ID_NativeLine, new NativeLine.Builder());
mBuilders.put(Common.VIEW_ID_VirtualGraph, new VirtualGraph.Builder());
mBuilders.put(Common.VIEW_ID_VH, new VH.Builder());
mBuilders.put(Common.VIEW_ID_VirtualTime, new VirtualTime.Builder());
mBuilders.put(Common.VIEW_ID_Slider, new Slider.Builder());
mBuilders.put(Common.VIEW_ID_VirtualProgress, new VirtualProgress.Builder());
mBuilders.put(Common.VIEW_ID_VirtualContainer, new VirtualContainer.Builder());
}
用来区分是什么View标签的,这样相当于形成了一个映射,例如当我们写的转xml为二进制文件的工具时,当遇到 VHLayout标签就向二进制文件中写入 VIEW_ID_VHLayout这个int值,从而就有了这种解析,俗话说的好,你怎么写进去的,我就怎么给你解析出来,这是我的规则,我做主。
不管怎么读取,最重要的是它怎么映射为属性的,同理读取int属性的时候也是采用这种映射
final public static int STR_ID_VHLayout = 1302701180;
final public static int STR_ID_GridLayout = -1822277072;
final public static int STR_ID_FrameLayout = 1310765783;
final public static int STR_ID_NText = 74637979;
final public static int STR_ID_VText = 82026147;
final public static int STR_ID_NImage = -1991132755;
final public static int STR_ID_VImage = -1762099547;
final public static int STR_ID_TMNImage = -2005645978;
final public static int STR_ID_List = 2368702;
final public static int STR_ID_Component = 604060893;
final public static int STR_ID_id = 3355;
final public static int STR_ID_layoutWidth = 2003872956;
final public static int STR_ID_layoutHeight = 1557524721;
final public static int STR_ID_paddingLeft = -1501175880;
final public static int STR_ID_paddingRight = 713848971;
final public static int STR_ID_paddingTop = 90130308;
final public static int STR_ID_paddingBottom = 202355100;
final public static int STR_ID_layoutMarginLeft = 1248755103;
final public static int STR_ID_layoutMarginRight = 62363524;
final public static int STR_ID_layoutMarginTop = -2037919555;
final public static int STR_ID_layoutMarginBottom = 1481142723;
final public static int STR_ID_orientation = -1439500848;
final public static int STR_ID_text = 3556653;
final public static int STR_ID_src = 114148;
final public static int STR_ID_name = 3373707;
final public static int STR_ID_pos = 111188;
final public static int STR_ID_type = 3575610;
final public static int STR_ID_gravity = 280523342;
final public static int STR_ID_background = -1332194002;
final public static int STR_ID_color = 94842723;
final public static int STR_ID_size = 3530753;
final public static int STR_ID_layoutGravity = 516361156;
final public static int STR_ID_colCount = -669528209;
final public static int STR_ID_itemHeight = 1671241242;
final public static int STR_ID_flag = 3145580;
final public static int STR_ID_data = 3076010;
final public static int STR_ID_dataTag = 1443184528;
final public static int STR_ID_style = 109780401;
final public static int STR_ID_action = -1422950858;
final public static int STR_ID_actionParam = 1569332215;
final public static int STR_ID_scaleType = -1877911644;
final public static int STR_ID_VLine = 81791338;
final public static int STR_ID_textStyle = -1048634236;
final public static int STR_ID_FlexLayout = -1477040989;
final public static int STR_ID_flexDirection = -975171706;
final public static int STR_ID_flexWrap = 1744216035;
final public static int STR_ID_flexFlow = 1743704263;
final public static int STR_ID_justifyContent = 1860657097;
final public static int STR_ID_alignItems = -1063257157;
final public static int STR_ID_alignContent = -752601676;
final public static int STR_ID_alignSelf = 1767100401;
final public static int STR_ID_order = 106006350;
final public static int STR_ID_flexGrow = 1743739820;
final public static int STR_ID_flexShrink = 1031115618;
final public static int STR_ID_flexBasis = -1783760955;
final public static int STR_ID_typeface = -675792745;
final public static int STR_ID_Scroller = -337520550;
final public static int STR_ID_minWidth = -1375815020;
final public static int STR_ID_minHeight = -133587431;
final public static int STR_ID_TMVImage = -1776612770;
final public static int STR_ID_class = 94742904;
final public static int STR_ID_onClick = -1351902487;
final public static int STR_ID_onLongClick = 71235917;
final public static int STR_ID_self = 3526476;
final public static int STR_ID_textColor = -1063571914;
final public static int STR_ID_textSize = -1003668786;
final public static int STR_ID_dataUrl = 1443186021;
final public static int STR_ID_this = 3559070;
final public static int STR_ID_parent = -995424086;
final public static int STR_ID_ancestor = -973829677;
final public static int STR_ID_siblings = 166965745;
final public static int STR_ID_module = -1068784020;
final public static int STR_ID_RatioLayout = -2105120011;
final public static int STR_ID_layoutRatio = 1999032065;
final public static int STR_ID_layoutDirection = -1955718283;
final public static int STR_ID_VH2Layout = -494312694;
final public static int STR_ID_onAutoRefresh = 173466317;
final public static int STR_ID_initValue = -266541503;
final public static int STR_ID_uuid = 3601339;
final public static int STR_ID_onBeforeDataLoad = 361078798;
final public static int STR_ID_onAfterDataLoad = -251005427;
final public static int STR_ID_Page = 2479791;
final public static int STR_ID_onPageFlip = -665970021;
final public static int STR_ID_autoSwitch = -380157501;
final public static int STR_ID_canSlide = -137744447;
final public static int STR_ID_stayTime = 1322318022;
final public static int STR_ID_animatorTime = 1347692116;
final public static int STR_ID_autoSwitchTime = 78802736;
final public static int STR_ID_animatorStyle = -1171801334;
final public static int STR_ID_layoutOrientation = 1942742086;
final public static int STR_ID_Grid = 2228070;
final public static int STR_ID_paintWidth = 793104392;
final public static int STR_ID_itemHorizontalMargin = 2129234981;
final public static int STR_ID_itemVerticalMargin = 196203191;
final public static int STR_ID_NLine = 74403170;
final public static int STR_ID_visibility = 1941332754;
final public static int STR_ID_mode = 3357091;
final public static int STR_ID_supportSticky = -977844584;
final public static int STR_ID_VGraph = -1763797352;
final public static int STR_ID_diameterX = 1360592235;
final public static int STR_ID_diameterY = 1360592236;
final public static int STR_ID_itemWidth = 2146088563;
final public static int STR_ID_itemMargin = 1810961057;
final public static int STR_ID_VH = 2738;
final public static int STR_ID_onSetData = -974184371;
final public static int STR_ID_children = 1659526655;
final public static int STR_ID_lines = 102977279;
final public static int STR_ID_ellipsize = 1554823821;
final public static int STR_ID_autoDimDirection = -1422893274;
final public static int STR_ID_autoDimX = 1438248735;
final public static int STR_ID_autoDimY = 1438248736;
final public static int STR_ID_VTime = 82029635;
final public static int STR_ID_containerID = 207632732;
final public static int STR_ID_if = 3357;
final public static int STR_ID_elseif = -1300156394;
final public static int STR_ID_for = 101577;
final public static int STR_ID_while = 113101617;
final public static int STR_ID_do = 3211;
final public static int STR_ID_else = 3116345;
final public static int STR_ID_Slider = -1815780095;
final public static int STR_ID_Progress = -936434099;
final public static int STR_ID_onScroll = 1490730380;
final public static int STR_ID_backgroundImage = 1292595405;
final public static int STR_ID_Container = 1593011297;
final public static int STR_ID_span = 3536714;
final public static int STR_ID_paintStyle = 789757939;
final public static int STR_ID_var = 116519;
final public static int STR_ID_vList = 111344180;
final public static int STR_ID_dataParam = -377785597;
final public static int STR_ID_autoRefreshThreshold = -51356769;
final public static int STR_ID_dataMode = 1788852333;
final public static int STR_ID_waterfall = -213632750;
final public static int STR_ID_supportHTMLStyle = 506010071;
final public static int STR_ID_lineSpaceMultiplier = -667362093;
final public static int STR_ID_lineSpaceExtra = -1118334530;
final public static int STR_ID_borderWidth = 741115130;
final public static int STR_ID_borderColor = 722830999;
final public static int STR_ID_maxLines = 390232059;
final public static int STR_ID_dashEffect = 1037639619;
final public static int STR_ID_lineSpace = -1807275662;
final public static int STR_ID_firstSpace = -172008394;
final public static int STR_ID_lastSpace = 2002099216;
final public static int STR_ID_maskColor = -77812777;
final public static int STR_ID_blurRadius = -1428201511;
final public static int STR_ID_filterWhiteBg = 617472950;
final public static int STR_ID_ratio = 108285963;
final public static int STR_ID_disablePlaceHolder = -1358064245;
final public static int STR_ID_disableCache = -1012322950;
final public static int STR_ID_fixBy = 97444684;
final public static int STR_ID_alpha = 92909918;
final public static int STR_ID_ck = 3176;
final public static int STR_ID_borderRadius = 1349188574;
final public static int STR_ID_borderTopLeftRadius = -1228066334;
final public static int STR_ID_borderTopRightRadius = 333432965;
final public static int STR_ID_borderBottomLeftRadius = 581268560;
final public static int STR_ID_borderBottomRightRadius = 588239831;
而String的读取则是用了
StringLoader进行读取,还记得上面的String怎么解析的吗,它被保存到集合之中,用id区分,这里的标签属性中就持有这个id,从而让view匹配正确的字符串,如下
protected boolean setStrAttribute(int key, int value) {
StringLoader sm = mContext.getStringLoader();
String stringValue = sm.getString(value);
boolean ret = setAttribute(key, stringValue);
return ret;
}
其他读取同样的道理,这里不再阐述,最后就是将这个假的映射View变成真的View了,例如假的
NativeText持有
NativeTextImp,而它刚好如下
public class NativeTextImp extends TextView implements IView
继承自 TextView ,最后映射的NativeText,将属性全部交给NativeTextImp,然后将NativeTextImp加到根布局ViewGroup上。看到这里也大概看出这个框架的一些缺陷,比如,这么动态加载View的话,适合只是改变原有布局文件的样式,比如字体大下了,颜色了,默认图片了,文本显示内容了,加个边框了,修改间距了,改个背景图了等等,不适合大范围改变布局(例如整个显示的排版都大变样了),还有就是假如View所设计的行为也改变了,这怎么加载,是用脚本吗?或者用插件化,当然每种方式都有每种方式的好处,每个框架都不是完美的,但我们要学习人家框架的优势来弥补自己的不足。