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

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

2023-02-27

想了解更多内容,请访问:51CTO和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com在讲解ActiveData实现原理之前,我们有必要先了解一下两个重要的类Lifecycle以及DataObserver,这两个类在ActiveData整个运行过程中扮演了非常重要的

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

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

https://harmonyos.51cto.com

在讲解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"); 
    } 

  • 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.

运行之后的截图:


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

2.主线程手动调用

// 添加如下代码测试DataObserver的onChanged方法是否会执行 
findComponentById(ResourceTable.Id_button) 
                .setClickedListener(component -> activeData.setData("I Love China")); 
  • 1.
  • 2.
  • 3.

 运行结果如下:


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

3.子线程调用

@Override 
   public void onActive() { 
       super.onActive(); 
       new Thread(() -> activeData.setData("New Hello World")).start(); 
   }  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

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

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"); 
    } 

  • 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.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.

运行效果如下: 

从以上运行结果,可以看出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方法仍然会被触发

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

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

https://harmonyos.51cto.com