深圳幻海软件技术有限公司 欢迎您!

元数据绑定系列(二):元数据绑定进阶

2023-02-27

想了解更多内容,请访问:51CTO和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com先上源码:喵叔catuncle/TestMetaDataBinding(主程序)喵叔catuncle/TestDataCenter(配合主程序演示跨进程、跨设备特性)demo截图上

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

先上源码:

喵叔catuncle / TestMetaDataBinding(主程序)

喵叔catuncle / TestDataCenter(配合主程序演示跨进程、跨设备特性)

demo截图

上一篇元数据绑定系列(一):元数据绑定的使用,算是元数据绑定入门。通过codelbs代码的学习,这一篇我们更深入思考一下。不然,对于元数据绑定我们永远只是停留在使用工具的重复劳动阶段。

代码项目结构


  • entry:程序入口(主程序,自定义元数据类HelloUiMetaData也放在这个module)
  • dataability:请忽略这个名字,最开始放的是AlarmClock的数据绑定。后来rdb作为数据源要和dataability对比,所以也放在这个module里。
  • datacenter:数据源相关的类AlarmsDataAbility和AlarmRdbStoreMetaDataHelper以及数据库都放在这个module。为了体现数据绑定的ui层和数据层分离的思想,所以我刻意把数据层放在独立的Module中。
  • mylibrary:放一些工具类

思考一:一个xml布局文件中可以使用多个元数据吗?

先说答案:可以。

核心代码:

<?xml version="1.0" encoding="utf-8"?> 
<DirectionalLayout 
    xmlns:ohos="http://schemas.huawei.com/res/ohos" 
    xmlns:metaDataBinding="http://schemas.huawei.com/res/metaDatabinding" 
    ohos:height="match_parent" 
    ohos:width="match_parent" 
    ohos:alignment="center" 
    ohos:orientation="vertical"
 
    <request-meta-data 
        name="data" 
        schema="wang.unclecat.meta-data.hello"/> 
 
    <request-meta-data 
        name="helloUi" 
        class="wang.unclecat.databinding.HelloUiMetaData" 
        schema="wang.unclecat.meta-data.hello.ui"/> 
 
    <Text 
        ohos:id="$+id:text_helloworld" 
        ohos:height="match_content" 
        ohos:width="match_content" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:layout_alignment="horizontal_center" 
        metaDataBinding:text="@{data.msg}" 
        ohos:text_size="40vp" 
        /> 
 
    <Text 
        ohos:id="$+id:text_helloworld2" 
        ohos:height="match_content" 
        ohos:width="match_content" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:layout_alignment="horizontal_center" 
        ohos:text_size="40vp" 
        metaDataBinding:text="*{helloUi.toStr(@{helloUi.count})}" 
        /> 
 
 
</DirectionalLayout> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
MetaDataRequestInfo request = new MetaDataRequestInfo.Builder() 
        .fromRequestItems(binding.getRequestMetaData()) 
        .setSyncRequest("data"true
        .setSyncRequest("helloUi"true
        .setCustomDao("data", simpleDao) 
        .setCustomDao("helloUi", simpleDaoUi) 
        .build(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

demo中,我定义了两个元数据data和helloUi,都可以成功绑定。

实际开发中,如果一个页面中有多个业务上不同的区域,那么每个区域绑定的数据也必然是不同的。(如果强行定义在一个元数据实体中也可以,但是违背了“面向对象”的思想。)

详见 wang.unclecat.databinding.slice.HelloSlice中示例

思考二:为什么Feature中使用 元数据绑定,Json Schema文件必须放在Entry的resource/rawfile.jsonschema路径下?

否则报错提示"没有定义相应的schema"

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] ohos.mp.metadata.binding.schema.SchemaProto.getFields()' on a null object reference 
  • 1.

 反编译:

package ohos.mp.metadata.binding.schema
 
public class SchemaParseUtil { 
 
    public static void loadAllMetaDataSchema(Context var0) { 
        try { 
            RawFileEntry var1 = var0.getResourceManager().getRawFileEntry("resources/rawfile/jsonschema/");//这个路径有问题!!! 
            Entry[] var2 = var1.getEntries(); 
 
            for(int var3 = 0; var3 < var2.length; ++var3) { 
                String var4 = var2[var3].getPath(); 
                String var5 = "resources/rawfile/jsonschema/" + var4; 
                loadSingleMetadataSchema(var0, var5); 
            } 
        } catch (IOException var6) { 
            Log.error("Load All MetaData Schema Failed: IO exception"); 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

之前我总结过rawfile的使用,有这样的结论:

就算在myfeature这个module里"resources/..."也是"entry/resources/..."的简写形式。

所以,Json Schema文件必须放在Entry中。可是,这显然不符合我们的开发习惯啊,我们应该把相关的资源放在同一个module里。

在本例中,dataability中使用的Json Schemaalarm_schema.json是放在dataability这个module中,我是怎么做到的呢?

其实很简单

package wang.unclecat.dataability; 
 
@MetaDataApplication(requireData = true, exportData = false
public class MyApplication extends AbilityPackage { 
 
    @Override 
    public void onInitialize() { 
        super.onInitialize(); 
 
        //MetaDataFramework.init(this);//有bug 
        MetaDataFrameworkExtra.init(this);//用它来代替 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

详见wang.unclecat.mylibrary.MetaDataFrameworkExtra

package wang.unclecat.mylibrary; 
 
public class MetaDataFrameworkExtra { 
 
    public static void init(AbilityPackage var0) { 
        MetaDataFramework.appContext = var0; 
        loadAllMetaDataSchema(var0);//fix: SchemaParseUtil.loadAllMetaDataSchema(var0); 
        UiOperatorManager.init(); 
    } 
 
    //fix: SchemaParseUtil.loadAllMetaDataSchema(var0); 
    public static void loadAllMetaDataSchema(Context var0) { 
        String moduleName = var0.getHapModuleInfo().getModuleName(); 
 
        try { 
            //这里是关键!把moduleName加上! 
            RawFileEntry var1 = var0.getResourceManager().getRawFileEntry(moduleName+"/resources/rawfile/jsonschema/"); 
            Entry[] var2 = var1.getEntries(); 
 
            for(int var3 = 0; var3 < var2.length; ++var3) { 
                String var4 = var2[var3].getPath(); 
                String var5 = moduleName+"/resources/rawfile/jsonschema/" + var4; 
                loadSingleMetadataSchema(var0, var5); 
            } 
        } catch (IOException var6) { 
            Log.error("Load All MetaData Schema Failed: IO exception"); 
        } 
 
    } 
 
    //call the method using reflection 
    private static void loadSingleMetadataSchema(Context var0, String var1){ 
        try { 
            Class<?> threadClazz = Class.forName("ohos.mp.metadata.binding.schema.SchemaParseUtil"); 
            Method method = threadClazz.getDeclaredMethod("loadSingleMetadataSchema", Context.class, String.class); 
            method.setAccessible(true); 
            System.out.println(method.invoke(null, var0, var1)); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.

思考三:除了DataAbility作为数据源,还可以有其它形式的数据源吗?

codelabs中使用的元数据有如下三个:

class ClockRowMetaData extends DataAbilityMetaData 
class CustomMetaData extends DataAbilityMetaData 
class NoteMetaData extends MetaData 
  • 1.
  • 2.
  • 3.

 MetaData是所有元数据类的最终基类。

其中ClockRowMetaData和CustomMetaData都继承自DataAbilityMetaData,而DataAbilityMetaData的基类还是MetaData,DataAbilityMetaData以DataAbility为数据源。

NoteMetaData通过自定义的class MyDataHandler implements CustomDao.ICustomMetaDataHandler最终还是使用DataAbility作为数据源。

难道我们使用元数据绑定框架只能通过DataAbility来访问数据?

当然不是。

com.huawei.middleplatform:ohos-metadata-binding:1.0.0.0中默认有三个MetaData的子类

DataAbilityMetaData 
RdbStoreMetaData 
RemoteServiceMetaData 
  • 1.
  • 2.
  • 3.

 从名字上不难看出,它们本质只是数据源不同,用法上应该大同小异。

在 喵叔catuncle / TestMetaDataBinding中

  • 我也会演示RdbStoreMetaData的用法,它以Rdb(关系型数据库)直接作为数据源,而不需要DataAbility中转。

详见:wang.unclecat.dataability.alarm.metadata.ClockRowMetaData2

  • 也会直接继承MetaData自定义我们的HelloUiMetaData元数据类,它的数据源很简陋,只是内存中的一个变量。

详见:wang.unclecat.databinding.HelloUiMetaData

  • 至于RemoteServiceMetaData后续我会慢慢加上,先把这篇文章写完,不然放得时间太长了。

说了这么多,现在认真回答一下这个问题:除了DataAbility作为数据源,可以有Rdb、RemoteService、自定义数据源(这里可以根据自己的需求自由发挥)

思考四:关于RdbStoreMetaData的问题一:

设置"rdbstore:///wang.unclecat.datacenter.AlarmRdbStoreMetaDataHelper"这样的uri会报错

ohos.mp.metadata.binding.databinding.DataSourceConnectionException: 
Make sure [wang.unclecat.datacenter.AlarmRdbStoreMetaDataHelper] is accessible 
  • 1.
  • 2.

 如果把元数据数据绑定的日志开关打开,我们可以看到这样的error提示:

"Illegal dataUri. supported type:rdbstore:///wang.unclecat.datacenter.AlarmRdbStoreMetaDataHelper 
  • 1.

 反编译:

package ohos.mp.metadata.binding.databinding; 
 
public class MetaDataRequestInfo { 
  
    public static class Builder { 
         
        public MetaDataRequestInfo.Builder setRequestSource(String var1, String var2) { 
            if (!this.isIllegalDataUri(var2)) { 
                String var4 = "|dataability|dataservice|datahandler|"
                Log.error("Illegal dataUri. supported type: " + var4); 
                throw new DataSourceNotFoundException("Illegal dataUri. supported type: " + var4); 
            } else { 
                MetaDataRequestInfo.RequestItem var3 = this.getOrCreateRequest(var1); 
                var3.dataUri = var2; 
                return this; 
            } 
        } 
 
        //这个方法有bug!!! 
        private boolean isIllegalDataUri(String var1) { 
            String var2 = "^(dataability|dataservice|datahandler)\\:\\/.*?\\/.*?\\/.*";//这里有bug!!!少了rdbstore 
            return Pattern.matches(var2, var1); 
        } 
         
        public MetaDataRequestInfo build() { 
            Iterator var1 = this.requestMap.keySet().iterator(); 
 
            while(var1.hasNext()) { 
                String var2 = (String)var1.next(); 
                MetaDataRequestInfo.RequestItem var3 = (MetaDataRequestInfo.RequestItem)this.requestMap.get(var2); 
                if (var3.predicates != null) { 
                    String var4 = var3.dataUri; 
                    String var5; 
                    if (var4.startsWith("dataability")) { 
                        if (var3.predicates.getType() != Type.DATA_ABILITY_PREDICARES) { 
                            var5 = "MetaDataRequestInfo-build: Invalid predicates type for request " + var3.name + ", should be DataAbilityPredicates object"
                            Log.error(var5); 
                            throw new DataSourceNotFoundException(var5); 
                        } 
                    } else if (var4.startsWith("rdbstore")) { 
                        if (var3.predicates.getType() != Type.RDB_PREDICATES) { 
                            var5 = "MetaDataRequestInfo-build: Invalid predicates type for request " + var3.name + ", should be RdbPredicates object"
                            Log.error(var5); 
                            throw new DataSourceNotFoundException(var5); 
                        } 
                    } else if (!var4.startsWith("dataservice") && var4.startsWith("datahandler") && var3.predicates.getType() != Type.PACMAP_PREDICATES) { 
                        var5 = "MetaDataRequestInfo-build: Invalid predicates type for request " + var3.name + ", should be PacMap object"
                        Log.error(var5); 
                        throw new DataSourceNotFoundException(var5); 
                    } 
                } 
            } 
 
            return new MetaDataRequestInfo(this); 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.

 发现boolean isIllegalDataUri(String var1)有bug,解决方法就是跳过这个有bug的验证方法:

package wang.unclecat.mylibrary; 
 
public class MetaDataFrameworkExtra { 
 
    public static void setRequestSource(MetaDataRequestInfo.Builder builder, String var1, String var2) { 
        MetaDataRequestInfo.RequestItem requestItem = getOrCreateRequest(builder, var1); 
        if (requestItem!=null) { 
            requestItem.dataUri = var2; 
        } 
    } 
 
    //call the method using reflection 
    private static MetaDataRequestInfo.RequestItem getOrCreateRequest(MetaDataRequestInfo.Builder builder, String var1) { 
        try { 
            Class<?> clazz = Class.forName("ohos.mp.metadata.binding.databinding.MetaDataRequestInfo$Builder"); 
            Method method = clazz.getDeclaredMethod("getOrCreateRequest", String.class); 
            method.setAccessible(true); 
            return (MetaDataRequestInfo.RequestItem) method.invoke(builder, var1); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
 
        return null
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

思考五:关于RdbStoreMetaData的问题二

在使用RdbStoreMetaData的过程中,我发现对rdb的操作只有查询是成功的,插入、删除、修改都失败。

反编译:

package ohos.mp.metadata.binding.dao.inner.rdbstore; 
 
public class RdbStoreDao extends MetaDataDao implements DataObserver { 
    //代码有省略。。。。 
     
    public void notifyDataAdd(MetaData var1) { 
        //空?所以ui层的改变,不会通过MetaData通知到数据层 
    } 
 
    public void notifyDataDelete(MetaData var1) { 
        //空?所以ui层的改变,不会通过MetaData通知到数据层 
    } 
 
    public void notifyChange(MetaData var1, String var2, Object var3) { 
        if (var1 instanceof RdbStoreMetaData) { 
            RdbStoreMetaData var4 = (RdbStoreMetaData)var1; 
            RdbStore var5 = this.helper.getRdbStore(); 
            if (var5.isOpen()) { 
                var5.beginTransaction(); 
                RdbPredicates var6 = this.createIdentityPredicate(var4); 
 
                try { 
                    var5.update(var4.createValuesBucketWithoutKeys(this.helper.getMetaDataIdentity()), var6); 
                    var5.markAsCommit(); 
                } catch (Exception var11) { 
                    Log.error("RdbStore Update Failed: Exception"); 
                } finally { 
                    var5.endTransaction(); 
                } 
            } 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

发现void notifyDataAdd(MetaData var1)和void notifyDataDelete(MetaData var1)是空实现,所以ui上插入、删除对MetaData的操作不会作用到数据层,也许是这个原因吧。当然,也有可能是我的用法不对。

ui上对MetaData的修改操作也同样不生效,我怀疑RdbStoreDao类中对MetaData没有调用addInnerObserver(),这只是和DataAbilityDao的实现对比后的猜测。

思考六:自定义MetaData都有哪几种方法?

在demo中,目前我只是简单通过setCustomDao()来设置自定义的SimpleDao并实现SimpleDao.ISimpleMetaDataHandler来完成对数据的绑定。

通过了解。自定义MetaData总结一下,有如下两种方法:

设置SimpleDao并实现相应的SimpleDao.ISimpleMetaDataHandler

设置"datahandler:///com.huawei.metadatabindingdemo.custom_data_source.handler.MyDataHandler"这样的uri,并实现相应的CustomDao.ICustomMetaDataHandler//codelabs中有演示, 本示例代码中没有实现

思考七:抽象类MetaDataDao的作用

此类封装了对数据源的直接操作(连接、增删改查等)。和常见的数据库orm框架中Dao的概念一致。

默认有五个实现类,前三个和内置的MetaData对应,后两个用于我们自定义MetaData

DataAbilityDao 
RdbStoreDao 
RemoteServiceDao 
SimpleDao 
CustomDao 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

顺便看一下 MetaDataDaoHelper的反编译结果:

public class MetaDataDaoHelper { 
    public MetaDataDaoHelper() { 
    } 
 
    public static MetaDataDao createDao(RequestItem var0, IMetaDataObserver var1) { 
        String var2 = var0.dataUri; 
        Object var3 = null
        if (var0.customDao != null) { 
            var3 = var0.customDao; 
        } else if (var2.startsWith("dataability")) { 
            var3 = new DataAbilityDao(); 
        } else if (var2.startsWith("rdbstore")) { 
            var3 = new RdbStoreDao(); 
        } else if (var2.startsWith("dataservice")) { 
            var3 = new RemoteServiceDao(); 
        } else if (var2.startsWith("datahandler")) { 
            var3 = createFromUri(var2); 
        } 
 
        if (var3 == null) { 
            throw new DataSourceNotFoundException("Request name:[" + var0.name + "],Request dataUri:[" + var2 + "]"); 
        } else { 
            ((MetaDataDao)var3).setRequestItem(var0); 
            ((MetaDataDao)var3).setMetaDataObserver(var1); 
            Log.info("createDao: dao = " + var3.getClass().getName()); 
            return (MetaDataDao)var3; 
        } 
    } 
 
    private static MetaDataDao createFromUri(String var0) { 
        String var1 = var0.substring("datahandler:///".length()); 
 
        try { 
            Class var2 = Class.forName(var1); 
            Object var3 = var2.newInstance(); 
            if (var3 instanceof ICustomMetaDataHandler) { 
                return CustomDao.create((ICustomMetaDataHandler)var3); 
            } else if (var3 instanceof ISimpleMetaDataHandler) { 
                return SimpleDao.create((ISimpleMetaDataHandler)var3); 
            } else { 
                throw new DataSourceNotFoundException("Class type of [" + var1 + "] doesn't match:" + ICustomMetaDataHandler.class.getTypeName() + "or" + ISimpleMetaDataHandler.class.getTypeName()); 
            } 
        } catch (ClassNotFoundException var4) { 
            throw new DataSourceNotFoundException("Create custom metadata dao fail missing class in " + var0, var4); 
        } catch (InstantiationException | IllegalAccessException var5) { 
            throw new DataSourceNotFoundException("Instance custom metadata dao fail with for " + var0, var5); 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.

可知MetaDataDao的实例化依赖uri的判断结果

思考八:跨进程、跨设备访问dataability类型的uri数据源

在同设备安装 喵叔catuncle / TestDataCenter(配合主程序演示跨进程、跨设备特性),使用体验上和本地访问dataability完全一致,本示例代码中已经实现。

理论上讲跨设备也应该是一样的,不过跨设备访问DataAbility会报如下的错。可是我已经注册了,并且本地(或本机跨进程)访问都是好的。

java.lang.IllegalArgumentException: DataAbility register failed, there is no corresponding dataAbility 
 
java.lang.IllegalStateException: No corresponding dataAbility, query failed 
  • 1.
  • 2.
  • 3.

求大神指点跨设备访问DataAbility的方法!!!

求大神指点跨设备访问DataAbility的方法!!!

求大神指点跨设备访问DataAbility的方法!!!

思考九: 如何开启元数据绑定框架的日志

ohos.mp.metadata.binding.common.Log是框架的日志工具类,默认是关闭的,开发过程中,我们可以打开,方法如下:

try { 
    Field field = ohos.mp.metadata.binding.common.Log.class.getDeclaredField("enableDebug"); 
    field.setAccessible(true); 
    field.set(nulltrue); 
} catch (Exception e) { 
    e.printStackTrace(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

思考十:ui销毁之后,绑定关系需要解绑吗?

答案:需要,框架已自动完成,开发者无需干预。

还是反编译:

public abstract class MetaDataBinding implements IMetaDataObserver, LifecycleStateObserver { 
 
    //子类的requestBinding()会调用此方法 
    protected static <T extends MetaDataBinding> T createBinding(AbilitySlice var0, int layoutId, MetaDataRequestInfo var2, IMetaDataObserver var3) throws DataSourceConnectionException { 
        if (var0 == null) { 
            Log.warn("slice is null when creating binding"); 
            return null
        } else { 
            Lifecycle var4 = var0.getLifecycle(); 
            if (var4 == null) { 
                Log.warn("lifecycle is null when creating binding"); 
                return null
            } else { 
                MetaDataBinding var5 = MetaDataFramework.getMetaDataBinding(layoutId); 
                var5.createComponent(var0, var4);//inflat布局 
                var5.requestBind(var2, var3);//绑定数据 
                return var5; 
            } 
        } 
    } 
 
    protected Component createComponent(Context var1, Lifecycle var2) { 
        this.context = var1; 
        Component var3 = LayoutScatter.getInstance(var1).parse(this.getLayoutId(), (ComponentContainer)nullfalse); 
        this.mComponent = new WeakReference(var3); 
        this.lifecycle = var2; 
        this.lifecycle.addObserver(this);//监听生命周期 
        return var3; 
    } 
     
    //响应生命周期变化,自动解绑 
    public void onStateChanged(Event var1, Intent var2) { 
        if (var1 == Event.ON_STOP) { 
            this.unbind();//解绑 
        } 
    } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

 由上可知,框架会监听AbilitySlice的生命周期变化,并在其销毁时解绑

思考十一:元数据绑定的内部实现

上面那么多次反编译,我们也不难看出,元数据绑定基于APT(Annotation Processing Tool)即注解处理器。并且框架也由三部分组成(和ButterKinfe这类用到APT技术的框架一样):

  • ohos-metadata-annotation定义注解
  • ohos-metadata-processor注解处理器
  • ohos-metadata-binding工具类

生成类似如下的文件:

wang.unclecat.dataability.metadatabinding.AlarmrowMetaDataBinding//主要完成数据的绑定、数据变化的监听 
wang.unclecat.dataability.metadatabinding.Alarm_rowLayoutMapper//完成Ui操作(get和set) 
//对ui的操作,详细可见package ohos.mp.metadata.binding.uioperate 
  • 1.
  • 2.
  • 3.

思考十二:每个页面都对应一个DataAbility可以吗?

我的回答:可以,但是不建议这样做。

因为DataAbility类似Android的ContentProvider,会跟着应用一起启动。如果有太多的DataAbility会占用太多内存资源。那怎么使用元数据绑定的DataAbility呢?

我们看官方文档是怎么介绍URI的:

详见:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-data-concept-0000000000043058


  • query:查询参数。
  • fragment:可以用于指示要访问的子资源。

所以,我们只需要定义一个DataAbility,通过查询参数决定要访问的数据资源。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com