android源码中的sdk,androidBindingxsdk源码分析
android Bindingx sdk源码分析
Bindingx 是什么?
BindingX 是解决weex和React Native上富交互问题的⼀种解决⽅案。
如果按照传统的weex 和 native 的交互⽅式,⽐如要实现⼀个清屏的动画,需要在这个视图上绑定touch事件后,监听到每⼀次⼿势的变化,native与weex都要进⾏⼀次通信,频繁的通信交互必然会消耗性能,导致⽆法在16ms内完成⼀次渲染,使得动画效果卡顿。
⽽bindingx则是利⽤了Expression Binding⽅案,通过对⼿势控制⾏为的表达式传递给native,当⼿势变化时,不再与weex交互,⽽是直接解析表达式的运算符和⽬标view来更新视图,达到流畅的实现weex动画。
Bindingx 流程
Bindingx 是⼀个扩展的module, 所以集成到android项⽬中时,要注册这个module.
public class BindingX {
private BindingX(){}
/**
* register binding module
* */
public static void register() throws WXException{
}
}
这⾥以timing动画为例来分析。
timing动画就是随着时间流逝,对view进⾏相应的更新。所以变量为时间t,⽽因变量则为view的基础属性,如
translateX,translateY,alpha,opacity, scaleX,scaleY,rotateX,rotateY,background-color,tOffsetX,
具体可以参看源码WXViewUpdateSerive.java定义的sTransformPropertyUpdaterMap。
#WXViewUpdateService.java
static {
sTransformPropertyUpdaterMap = new HashMap<>();
sTransformPropertyUpdaterMap.put("opacity",new OpacityUpdater());
sTransformPropertyUpdaterMap.put("anslate",new TranslateUpdater());
sTransformPropertyUpdaterMap.put("anslateX",new TranslateXUpdater());
sTransformPropertyUpdaterMap.put("anslateY",new TranslateYUpdater());
sTransformPropertyUpdaterMap.put("transform.scale",new ScaleUpdater());
sTransformPropertyUpdaterMap.put("transform.scaleX",new ScaleXUpdater());
sTransformPropertyUpdaterMap.put("transform.scaleY",new ScaleYUpdater()); sTransformPropertyUpdaterMap.put("ate",new RotateUpdater()); sTransformPropertyUpdaterMap.put("ateZ",new RotateUpdater()); sTransformPropertyUpdaterMap.put("ateX",new RotateXUpdater()); sTransformPropertyUpdaterMap.put("ateY",new RotateYUpdater()); sTransformPropertyUpdaterMap.put("background-color",new BackgroundUpdater()); sTransformPropertyUpdaterMap.put("color", new ColorUpdater());
sTransformPropertyUpdaterMap.put("tOffset", new ContentOffsetUpdater()); sTransformPropertyUpdaterMap.put("tOffsetX", new ContentOffsetXUpdater()); sTransformPropertyUpdaterMap.put("tOffsetY", new ContentOffsetYUpdater()); sTransformPropertyUpdaterMap.put("border-top-left-radius", new BorderRadiusTopLeftUpdater()); sTransformPropertyUpdaterMap.put("border-top-right-radius", new BorderRadiusTopRightUpdater());
sTransformPropertyUpdaterMap.put("border-bottom-left-radius", new BorderRadiusBottomLeftUpdater()); sTransformPropertyUpdaterMap.put("border-bottom-right-radius", new BorderRadiusBottomRightUpdater()); sTransformPropertyUpdaterMap.put("border-radius", new BorderRadiusUpdater());
}
在weex定义⼀个timing动画,
Binding.bind({
eventType: 'timing',
exitExpression: 't>1000',
props: [{
element: my,
property: 'anslateX',
expression: `easeOutExpo(t,${self.x},${changed_x},1000)`
},
{
element: my,
property: 'opacity',
expression: `linear(t,${self.opacity},${changed_opacity},1000)`
}
]
});
这个⽅法会先⾛到类BindingXCore.java的⽅法doBind()
public String doBind(@Nullable Context context,
@Nullable String instanceId,
@NonNull Map params,
@NonNull JavaScriptCallback callback, extension) {
String eventType = StringValue(params, BindingXConstants.KEY_EVENT_TYPE);
...
ExpressionPair exitExpressionPair = ExpressionPair(params, BindingXConstants.KEY_EXIT_EXPRESSION);
String anchor = StringValue(params, BindingXConstants.KEY_ANCHOR); // maybe nullable
List> expressionArgs = RuntimeProps(params);
Map interceptors = CustomInterceptors(params);
return doBind(anchor, anchorInstanceId, eventType, configMap, exitExpressionPair, expressionArgs, interceptors, callback, context, instanceId, extension);
}
这个⽅法主要⽤来解析⼀些参数,weex⽅代码的参数会映射成parmas, 那么在timing动画中,就会解析到
eventType,exitExpressionPair,expressionArgs。expressionArgs对应的就是属性名为props。 另外anchor参数表⽰为动作触发的⽬标view,因为动画类型为timing, 所以也就不存在anchor,如果存在anchor, 会把anchor赋值给定义的动画的返回结果即token, 便于后⾯的解绑,如果没有anchor, 那么sdk就会⾃动⽣成⼀个token,来赋值给动画绑定完成后的结果。interceptors指的是动画,可以⾃定义特定需要监听的事件。定义的格式如下:
interceptors: {
interceptor_name: {
expression: {
origin:'',
transformed:''
}
},
...
}
如想要收到⽤户横向滑动到100px这个事件,可以定义如下:
interceptors: {
user_horizontal_scroll_100: {
expression: 'x>100'
}
}
当解析完成之后,就到了关键执⾏动画表达式的代码了。也就是继续调⽤另⼀个doBind()⽅法。
public String doBind(@Nullable String anchor,
@Nullable String anchorInstanceId,
@Nullable String eventType,
@Nullable Map globalConfig,
@Nullable ExpressionPair exitExpressionPair,
@Nullable List> expressionArgs,
@Nullable Map interceptors,
@Nullable JavaScriptCallback callback,
@Nullable Context context,
@Nullable String instanceId,
@ extension) {
.
..
token = doPrepare(context, instanceId, anchor, anchorInstanceId, eventType, globalConfig);
...
...
}
在这个⽅法中,重点是这两个⽅法的调⽤doPrepare() 和 onBindExpression, 在doPrepare()⽅法中,主要⽬的是为了⽣成⼀个token,⽤于后续的unbind以及设置⼀些相关的变量,即相关⽣命周期的调⽤,如onCreate,onStart。
onBindExpression()⽅法是定义在IEventHandler, AbstractEventHandler做了基础的解析,然后对于每种动画类型都做了各⾃特有的实现。
先看AbstractEventHandler类中的⽅法:
@Override
public void onBindExpression(@NonNull String eventType,
@Nullable Map globalConfig,
@Nullable ExpressionPair exitExpressionPair,
@NonNull List> expressionArgs,
@Nullable BindingXCore.JavaScriptCallback callback) {
clearExpressions();
transformArgs(eventType, expressionArgs);
this.mCallback = callback;
this.mExitExpressionPair = exitExpressionPair;
sdkif(!mScope.isEmpty()) {
mScope.clear();
}
applyFunctionsToScope();
}
⼤神的代码很严谨啊,绑定表达式的时候,都做了清空处理。 重点只需要看两个⽅法:transformArgs(eventType, expressionArgs);和applyFunctionsToScope();
transformArgs(eventType, expressionArgs)这个⽅法是将props⾥⾯的json转成相对应的map存储在mExpressionHoldersMap中。
private void transformArgs(@NonNull String eventType, @NonNull List> originalArgs) {
if (mExpressionHoldersMap == null) {
mExpressionHoldersMap = new HashMap<>();
}
for (Map arg : originalArgs) {
String targetRef = StringValue(arg, BindingXConstants.KEY_ELEMENT);
String targetInstanceId = StringValue(arg, BindingXConstants.KEY_INSTANCE_ID);
String property = StringValue(arg, BindingXConstants.KEY_PROPERTY);
ExpressionPair expressionPair = ExpressionPair(arg, BindingXConstants.KEY_EXPRESSION);
Object configObj = (BindingXConstants.KEY_CONFIG);
Map configMap = null;
if(configObj != null && configObj instanceof Map) {
try {
configMap = Map(new JSONObject((Map) configObj));
}catch (Exception e) {
LogProxy.e("parse config failed", e);
}
}
if (TextUtils.isEmpty(targetRef) || TextUtils.isEmpty(property) || expressionPair == null) {
LogProxy.e("skip illegal binding args[" + targetRef + "," + property + "," + expressionPair + "]");
continue;
}
ExpressionHolder holder = new ExpressionHolder(targetRef,targetInstanceId, expressionPair, property, eventType, configMap);
List holders = (targetRef);
if (holders == null) {
holders = new ArrayList<>(4);
mExpressionHoldersMap.put(targetRef, holders);
holders.add(holder);
} else if (!ains(holder)) {
holders.add(holder);
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论