1

从Android到HarmonyOS迁移之路

一、前言

​ 接触HarmonyOS的时间其实不是很长,23年社区、交流群都在讨论HarmonyOS。我当初也是抱着好奇、验证的心态去接触的HarmonyOS。然后学习它,使用它、开发它、分享它。到如今也算是半个身子探入了HarmonyOS中了,称得上HarmonyOS开发者。也积攒了许许多多的经验。对于当初听到的HarmonyOS到底行不行、有没有前景也是有了自己的答案。

二、Android人Android的优势

  1. 类似的开发工具和环境

    • Android Studio和DevEco Studio,如果你用过New UI之前的Android Studio的话,你对于DevEco Studio会有一种,这是亲兄弟的感觉。

      实际上他们也的确是亲兄弟。因为两者都是基于IDEA开发的IDE。所以很多东西都是相似的,切换使用起来也是非常快的。

      <img src="https://zyc-essay.oss-cn-beijing.aliyuncs.com/picgo/image-20240717202044663.png" alt="image-20240717202044663" style="zoom:50%;" />

      <img src="https://zyc-essay.oss-cn-beijing.aliyuncs.com/picgo/image-20240717202049916.png" alt="image-20240717202049916" style="zoom:50%;" />

      两张IDE的截图,各位能分出来嘛~

      既然两者如此相像,那很多工具也是DevEco有的:

      熟悉的日志系统,熟悉的过滤规则:

      image-20240717202349347熟悉的UI Inspector:

      image-20240717202620715

      熟悉的调试工具:

      image-20240717202836968

      以及更多熟悉的东西~需要我们在实际的使用中感受了

  2. 性能优化经验的适用性

    • 只要是代码,肯定逃不过性能优化。在性能有限的设备上,性能调优肯定是重中之重的。

      • 资源管理

        • Android和HarmonyOS都需要管理内存泄漏、避免内存溢出。在开发过程中,可以使用类似的工具如Memory Profiler、LeakCanary等来监控和优化内存使用。
      • UI优化

        • 一般来说,Android通过减少布局层次、使用更高效的布局(如ConstraintLayout)来优化UI性能。这些方法同样适用于HarmonyOS的UI设计(如RelativeContainer

    ​ 我们可以借鉴以往的经验,从资源管理、UI优化、网络优化、电池管理等方面入手,结合HarmonyOS的特性,进一步提升应用的性能。

  3. ArkUI的MVVM模式

    ​ 应用通过状态去渲染更新UI是程序设计中相对复杂,但又十分重要的,往往决定了应用程序的性能。对于一位Android开发来说,如果你是远古项目那你可能用的MVP,如果你是现代开发那你用的可能是MVVM,如果你是未来开发那你用的可能是MVVM。

    ​ ArkUI天生就是MVVM,在Android中定义MVVM,可以这么做:

    Android
    • Model

      Model负责处理应用的数据部分,包括从数据库、网络或其他数据源获取数据。它只包含数据的逻辑和业务规则。

      data class User(val name: String, val age: Int)
      
      class UserRepository {
          fun getUser(userId: String): User {
              // 从数据源获取用户数据(例如网络请求或数据库查询)
              return User("John Doe", 25)
          }
      }
    • View

      View是应用的UI部分,负责展示数据和处理用户交互。在Android中,View通常是Activity或FragmentView通过观察ViewModel来获得数据的更新。(当然你可以是Compose)

      <!-- activity_main.xml -->
      <layout xmlns:android="http://schemas.android.com/apk/res/android">
          <data>
              <variable
                  name="viewModel"
                  type="com.example.app.UserViewModel" />
          </data>
          <RelativeLayout
              android:layout_width="match_parent"
              android:layout_height="match_parent">
              
              <TextView
                  android:id="@+id/userName"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{viewModel.user.name}" />
              
              <TextView
                  android:id="@+id/userAge"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_below="@id/userName"
                  android:text="@{viewModel.user.age}" />
          </RelativeLayout>
      </layout>
    • ViewModel

      ViewModel负责准备和管理数据以供UI使用。它处理应用的业务逻辑,并与Model进行交互。ViewModel不引用View

      class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
          private val _user = MutableLiveData<User>()
          val user: LiveData<User> get() = _user
      
          fun loadUser(userId: String) {
              // 从仓库获取用户数据并更新LiveData
              _user.value = userRepository.getUser(userId)
          }
      }
    • 数据绑定(Data Binding)

      (当然也可用各种框架和工具类)

      class MainActivity : AppCompatActivity() {
          private lateinit var binding: ActivityMainBinding
          private lateinit var viewModel: UserViewModel
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              // 设置数据绑定
              binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
              
              // 初始化ViewModel
              viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
              binding.viewModel = viewModel
              binding.lifecycleOwner = this
      
              // 加载用户数据
              viewModel.loadUser("userId")
          }
      }
      

      对于HarmonyOS来说:

      #### HarmonyOS

      model没什么不同。

      • Model
      class User {
        name: string
        age: number
      
        constructor(name: string, age: number) {
          this.name = name
          this.age = age
        }
      }
      • View

        声明式~

        build() {
          Column() {
            Text(`${this.user?.age}`)
            Text(`${this.user?.age}`)
          }
        }
      • 数据请求
        class UserRepository {
          static async getUser(userId: String): Promise<User> {
            // 从数据源获取用户数据(例如网络请求或数据库查询)
            return new User("John Doe", 25)
          }
        }
        //这里是在Index中
        async aboutToAppear(): Promise<void> {
          this.user = await UserRepository.getUser("123")
        }
      • ViewModel

        @Entry
        @Component
        struct Index {
          @State user?: User = undefined
        
          async aboutToAppear(): Promise<void> {
            this.user = await UserRepository.getUser("123")
          }
        
          build() {
            Column() {
              Text(`${this.user?.age}`)
              Text(`${this.user?.age}`)
            }
          }
        }

        可以看到,在ArkUI中,ViewModel是存储在自定义组件的状态变量(@State等)、LocalStorageAppStorage中的数据。

        自定义组件通过执行其build()方法或者@Builder装饰的方法来渲染UI,即ViewModel可以渲染View

        image-20240717203146602

        其提高我们对ViewModel的阅读能力,代码也更加的清晰。

  4. 一致的开发思维

    ​ 刚刚提到了这么多,其实就是说,对于一个Android开发者来讲,HarmonyOS的开发工具和环境相似,使得我们无需重新学习新的开发环境,可以迅速上手,降低了学习曲线。

    ​ 使用相同的设计模式和开发思维,能够将已有的经验和技巧无缝迁移到HarmonyOS开发中,减少适应时间。开发者可以直接应用在Android开发中积累的调试经验和性能优化技巧,在HarmonyOS开发中快速定位和解决问题,提高开发效率。

    ​ 通过一致的开发思维和设计模式,开发者能够编写高质量、可维护的代码,提升应用的稳定性和可扩展性,同时利用在Android开发中积累的性能优化经验,打造高性能的应用,提升用户体验。

    ​ Android开发赢麻了。

三、Android人的挑战

​ 换到一个新的环境,不可能一帆风顺,总会有不一样的、不习惯的。作为开发,这都是我们的挑战。

  1. 架构差异

    • Activity、Fragment、View

      这三家大家都是非常熟悉的,对于Fragment的怨言也是无穷无尽的。简单的复述下:

      Activity:是Android应用的基本组件之一,负责创建窗口并管理用户界面。每个Activity都代表应用中的一个屏幕,包含用户可以与之交互的界面元素。

      Fragment:是Activity的一部分,提供了一种在Activity中构建模块化UI的方式。Fragment可以独立存在,也可以与其他Fragment组合在一个Activity中,以实现动态和灵活的UI设计。

      View:是Android UI框架的基础构建块,表示屏幕上的基本元素,例如按钮、文本框和图像视图。View可以组合成复杂的布局,用于构建应用的界面。

      1. Ability、Compoment

        Ability:是HarmonyOS应用的核心组件,类似于Android的Activity。

        Component:是HarmonyOS中构建UI的基础元素,类似于Android的View。

        ​ 在HarmonyOS中是没有Fragment的概念的,因为Component承接了所有的UI。使用不同的注解@Entry、@CustomDialog等。使用多个Component可以组合成复杂的布局和界面。

    ​ 对于Android开发来说,需要注意的是Ability的生命周期,以及Component的

    生命周期。之前在Fragment、Acitivity某个生命周期做得事情,在HarmonyOS中如何实现,对应的生命周期有什么不同,有没有类似的生命周期等。这都是容易弄错、忽略的。但是某种程度上,也是我们Android开发的优势。毕竟我们也是饱受生命周期折磨的人了。

  2. API和库的差异

    ​ 需要直面的一个很重要的事情就是,一些Android特有的API、三方库在HarmonyOS中可能没有直接的替代方案。

    ​ 这时候就需要你自己发动聪明的大脑去造轮子了。

    ​ 但是通常官方都提供了非常丰富的用例,比如TPC仓库,官方的社区,Sample仓库等。而且官方也是提供了提工单的方式,直面开发来解决问题。

    ​ 很多库,现在开发者和官方也是做了很多迁移工作的。

    ​ 总而言之就是,只要多问、多想、多找。都是有解决方案的。

  3. 语言和框架的差异

    1. 类和对象的处理

      image-20240719115724993

      ​ 在Java和Kotlin中,类是构建对象的蓝图,通过类可以创建对象,这些对象是类的实例,具有类定义的属性和方法。

      ​ 在ArkTs中,类实际上是一个函数(构造函数),其方法定义在原型上,具有高度的动态特性。这种设计会导致有时类无法调用到它的方法,主要原因如下:

      方法未绑定到实例:如果方法在调用时没有正确绑定到实例,会导致 this 关键字指向错误。

      方法定义在原型上:方法定义在原型上,可能导致方法在某些情况下无法访问,特别是在不同的作用域或上下文中调用时。

      ​ 这对于我们这种使用Java和Kotlin为主力开发语言的人来说,这种动态性也可能导致类的方法在某些情况下无法正确调用,需要我们特别注意方法的绑定和调用上下文。

      ​ (你也不想天天看到无法找到方法的错误吧。)

    2. 变量作用域和生命周期管理

      ArkTS中的闭包在某些方面与 Kotlin 和 Java 的处理有所不同,这主要体现在变量作用域和生命周期的管理上:

      1. 变量作用域
      • Kotlin: Kotlin 采用了类似于 Java 的作用域管理,但支持更灵活的闭包处理。Kotlin 允许通过 let, apply, run, with, 和 also 等函数来创建闭包,变量作用域在这些函数的范围内有效。
      • Java: Java 中的闭包主要通过匿名内部类来实现。变量的作用域是基于匿名内部类的上下文的。Java 中闭包的使用通常较为简单,但变量在闭包中的生命周期较长,可能会引入额外的内存开销。
      • ArkTS: 在 ArkTS 中,需要注意变量是在函数内部还是外部定义的,以及是否通过闭包引用。闭包的作用域遵循 JavaScript/TypeScript 的规则

        特别注意:

        • 函数的作用域由函数定义的位置决定。内部函数可以访问其外部函数的变量
        • 当内部函数访问外部函数的变量时,闭包会持有对这些变量的引用
        • 如果闭包中持有对变量的引用,并且闭包仍然存在,那么这些变量不会被垃圾回收

      需要充分的理解ArkTS的闭包,才能避免一些奇怪的问题出现,也能扩展我们解决问题的思路。

    3. 组件构建方式

      对于Android来说,我们想写一个Custom View一般会采用继承现有的Group View来实现,但是在ArkUI中,继承UI是不被允许的。需要多多使用组合~

      ​ wrapBuilder、ComponentContent等。

      当然了如果你是Compose使用者,当我没说~

  4. 分布式能力

    ​ 为什么要把分布式单独拿出来说呢?因为大多数android开发对于分布式的认识是欠缺的。

    ​ 不只是Android 开发主要集中在单一设备上的应用开发,还有Android的分布式开发非常的复杂。需要我们去额外的考虑许许多多的东西。

    ​ 所以作为一个Android开发去做HarmonyOS开发,我们需要去主动的认识HarmonyOS的分布式。

    ​ 如何充分利用HarmonyOS自身的跨端迁移多端协同能力,去应用到我们的APP中,发挥最大的用处。是值得我们去认真研究的。

    ​ 当然你可看看 <HarmonyOS第一课>自由流转

三、实际开发中的体验

​ 总体来说,HarmonyOS的开发,给我带来了非常多的惊喜的。

  1. 状态管理的优势

    img

    丰富的状态管理装饰器,的确让开发变得更加的顺手了起来。减少样板代码不可多得的好手。

    ​ 独立的状态管理逻辑使得测试变得更加容易。大大简化了复杂应用的状态管理过程~

  2. 丰富的组件

    很多时候,我们都在烦恼Android某些UI交互(Compose不算),比如获取Android RecycleView当前看见列表的中间Item:

    (伪代码)

    recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val layoutManager = recyclerView.layoutManager as? LinearLayoutManager
            layoutManager?.let {
                val firstVisibleItemPosition = it.findFirstVisibleItemPosition()
                val lastVisibleItemPosition = it.findLastVisibleItemPosition()
                val middlePosition = (firstVisibleItemPosition + lastVisibleItemPosition
                // 获取中间项的 ViewHolder
                val middleViewHolder = recyclerView.findViewHolderForAdapterPosition(middlePosition)
                middleViewHolder?.let { viewHolder ->
                    // 处理中间项
                    handleMiddleItem(viewHolder)
                }
            }
        }
    })

    而在ArkUI中:

    (伪代码)

    List().onScrollIndex((start: number, end: number, center: number) =>{
            handleMiddleItem()
          })

    非常的简介,总体来说,ArkUI提供了许多我比较喜欢的API,也是解决了一些Android的开发痛点了。

  3. 一致性和统一性

    ​ 相信每一个Android人都被各种无休止的屏幕、系统适配折磨疯了。当时iOS在旁边偷笑我们,现在HarmonyOS来了,也开始偷笑Android。HarmonyOS 一次开发,多端部署, 一多~它来了

    • 不同设备间的屏幕尺寸、色彩风格等存在差异,页面如何适配。
    • 不同设备的系统能力有差异,如智能穿戴设备是否具备定位能力、智慧屏是否具备摄像头等,功能如何兼容。

    通过布局能力、交互归一、多台组件、资源能力、系统能力全面的支持,我们只需要少量的代码,就可以在多个平台部署,得到相似的UI、交互~

四、结尾

​ HarmonyOS对于Android、iOS这些老前辈,尽管相对年轻,但是结合自身的创新和独特优势,正在迅速崛起。跨设备互联、分布式技术、微内核架构和统一开发平台使得HarmonyOS在某些方面具有独特的竞争力。HarmonyOS到底行不行、有没有前景。行的。有的。

五、最后

​ 如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏


猫猫头啊
1.4k 声望781 粉丝

猫猫头,用狗子头像。只是想说,别给自己画圈。我可以、我都行、我什么都是。