ActiveData在HarmonyOS中的原理分析和运用

Posted HarmonyOS技术社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ActiveData在HarmonyOS中的原理分析和运用相关的知识,希望对你有一定的参考价值。

在讲解ActiveData实现原理之前,我们有必要先了解一下两个重要的类Lifecycle以及DataObserver,这两个类在ActiveData整个运行过程中扮演了非常重要的角色。

  • Lifecycle提供了观察Ability和AbilitySlice的生命周期能力
  • DataObserver通过持有一个Lifecycle对象来观察Ability或者AbilitySlice的生命周期变化,同时DataObserver还允许ActiveData观察其生命周期变化,因此DataObserver和ActiveData相互观察,DataObserver观察ActiveData的数据变化,ActiveData观察DataObserver的生命周期变化。

ActiveData作用和特点

ActiveData是一个具有感知生命周期能力变化的数据通知类组件,非常适合在一些对数据同步性较高的场景下使用,它具有以下三个特点。

  • 基于观察者模式:
    ActiveData是一个持有可被观察数据的类,ActiveData需要一个观察者对象,一般是DataObserver类的具体实现。
  • 感知生命周期:
    ActiveData具有生命周期感知能力,目前ActiveData具有两种通知模式,一种是Ability/AbilitySlice生命周期是活跃(ACTIVE)状态时才更新数据,另一种是Ability/AbilitySlice生命周期处于任何存活状态(即只要没有被销毁)都可以更新数据。
  • 自动解除数据订阅:
    ActiveData必须配合实现了Lifecycle的对象使用。当Ability/AbilitySlice被销毁(STOP状态)后,会自动解除订阅,这在一定程度上可以避免内存泄漏等问题。

实践

  1. 基础用法

    public class MainAbilitySlice extends AbilitySlice {
    private ActiveData<String> activeData;
    
    private Text mText;
    
    private final DataObserver<String> dataObserver = new DataObserver<String>() {
        @Override
        public void onChanged(String s) {
            mText.setText(s);
        }
    };
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        activeData = new ActiveData<>();
        dataObserver.setLifecycle(getLifecycle());
    
        mText = (Text) findComponentById(ResourceTable.Id_text_helloworld);
    
        subscribe();
    }
    
    private void subscribe() {
        activeData.addObserver(dataObserver, true);
    }
    
    @Override
    public void onActive() {
        super.onActive();
        activeData.setData("New Hello World");
    }
    }

    运行之后的截图:

::: hljs-center

:::

从运行结果可以看出,setData调用后会立即触发onChanged回调方法

  1. 主线程手动调用
    // 添加如下代码测试DataObserver的onChanged方法是否会执行
    findComponentById(ResourceTable.Id_button)
                .setClickedListener(component -> activeData.setData("I Love China"));

    运行结果如下:

::: hljs-center

:::

从运行结果我们可以看到,onChanged方法会一直触发,并不会因为值相同而不执行,虽然暂时看不了鸿蒙源码,但我们可以大胆猜测,鸿蒙底层维护了一个类似于版本号的标记,每次setData,该标记会自动+1,从而通过此版本号来判断data是否有变化,进而决定是否触发onChanged回调方法。

  1. 子线程调用

    @Override
    public void onActive() {
        super.onActive();
        new Thread(() -> activeData.setData("New Hello World")).start();
    } 

    运行后发现没有问题,可以正常调用,说明setData方法可以在子线程调用。

  2. activeData.addObserver(dataObserver, true/false)对比

    public class MainAbilitySlice extends AbilitySlice {
    private ActiveData<String> activeData;
    private ActiveData<String> activeData2;
    
    private Text mText;
    
    private final DataObserver<String> dataObserver = new DataObserver<String>() {
        @Override
        public void onChanged(String s) {
            mText.setText(s);
            System.out.println("ActiveData:---onChange:"+s);
        }
    };
    
    private final DataObserver<String> dataObserver2 = new DataObserver<String>() {
        @Override
        public void onChanged(String s) {
            mText.setText(s);
            System.out.println("ActiveData:---onChange:"+s);
        }
    };
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        activeData = new ActiveData<>();
        activeData2 = new ActiveData<>();
    
        dataObserver.setLifecycle(getLifecycle());
        dataObserver2.setLifecycle(getLifecycle());
    
        mText = (Text) findComponentById(ResourceTable.Id_text_helloworld);
        findComponentById(ResourceTable.Id_button)
                .setClickedListener(component -> activeData.setData("I Love China"));
    
        findComponentById(ResourceTable.Id_addObserver_true).setClickedListener(component -> {
            System.out.println("ActiveData:-------------");
            Intent intent1 = new Intent();
            Operation operation = new Intent.OperationBuilder()
                    .withDeviceId("")
                    .withBundleName(getBundleName())
                    .withAbilityName(SecondAbility.class.getName())
                    .build();
    
            intent1.setOperation(operation);
            startAbility(intent1);
            // 此处是为了验证Ability在inActive状态的值的变化情况
            new EventHandler(EventRunner.getMainEventRunner()).postTask(() -> activeData.setData("New Hello World"), 2000);
        });
    
        findComponentById(ResourceTable.Id_addObserver_false).setClickedListener(component -> {
            System.out.println("ActiveData:-------------");
            Intent intent1 = new Intent();
            Operation operation = new Intent.OperationBuilder()
                    .withDeviceId("")
                    .withBundleName(getBundleName())
                    .withAbilityName(SecondAbility.class.getName())
                    .build();
    
            intent1.setOperation(operation);
            startAbility(intent1);
            // 此处是为了验证Ability在inActive状态的值的变化情况
            new EventHandler(EventRunner.getMainEventRunner()).postTask(() -> activeData2.setData("New Hello World"), 2000);
        });
    
        subscribe();
    }
    
    private void subscribe() {
        activeData.addObserver(dataObserver, true);
        activeData2.addObserver(dataObserver, false);
    }
    
    @Override
    public void onActive() {
        super.onActive();
        System.out.println("ActiveData:---onActive");
    }
    
    @Override
    protected void onInactive() {
        super.onInactive();
        System.out.println("ActiveData:---onInactive");
    }
    
    @Override
    protected void onBackground() {
        super.onBackground();
        System.out.println("ActiveData:---onBackground");
    }
    
    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    System.out.println("ActiveData:---onForeground");
    }
    }

    运行效果如下:

::: hljs-center

:::

从以上运行结果,可以看出addObserver(dataObserver, true/false)方法的特点,当为true是表示无论Ability/AbilitySlice处于任何生命周期状态,均会触发onChanged回调方法,当为false时
表示Ability/AbilitySlice只有处于ACTIVE状态时才会触发onChanged方法。

总结

  • ActiveData内部是依靠Lifecycle来感知组件的生命周期,从而可以避免内部泄漏
  • 开发者无需维护observer对象,当Ability/AbilitySlice被销毁时,相关联的observer会被自动移除
  • 当Ability/AbilitySlice处于活跃(ACTIVE)状态时,当ActiveData数据源发生变化时onChanged方法会立即触发,去更新UI或者执行我们想要的任何操作
  • setData方法可在任意线程中去调用,开发者无需关心调用者是否在主线程中
  • setData方法即使设置同样的数据对象,onChanged方法仍然会被触发

作者:高建明

更多原创内容请关注:中软国际 HarmonyOS 技术学院
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于鸿蒙的内容,请访问:

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

https://harmonyos.51cto.com/#bkwz

以上是关于ActiveData在HarmonyOS中的原理分析和运用的主要内容,如果未能解决你的问题,请参考以下文章

HarmonyOS实战[一]——原理概念介绍安装:基础篇

鲁大师6月新机流畅榜:HarmonyOS跑分亮相

#夏日挑战赛# HarmonyOS 实现一个手绘板

HarmonyOS 实战——万字分析并学习 JsFACard 项目

#夏日挑战赛# HarmonyOS - 自定义组件之slider滑块

HarmonyOS原子化服务新赛道:系统级入口服务中心