从Android到HarmonyOS迁移之路
一、前言
接触HarmonyOS的时间其实不是很长,23年社区、交流群都在讨论HarmonyOS。我当初也是抱着好奇、验证的心态去接触的HarmonyOS。然后学习它,使用它、开发它、分享它。到如今也算是半个身子探入了HarmonyOS中了,称得上HarmonyOS开发者。也积攒了许许多多的经验。对于当初听到的HarmonyOS到底行不行、有没有前景也是有了自己的答案。
二、Android人Android的优势
类似的开发工具和环境
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有的:
熟悉的日志系统,熟悉的过滤规则:
熟悉的UI Inspector:
熟悉的调试工具:
以及更多熟悉的东西~需要我们在实际的使用中感受了
性能优化经验的适用性
只要是代码,肯定逃不过性能优化。在性能有限的设备上,性能调优肯定是重中之重的。
资源管理
- Android和HarmonyOS都需要管理内存泄漏、避免内存溢出。在开发过程中,可以使用类似的工具如Memory Profiler、LeakCanary等来监控和优化内存使用。
UI优化
- 一般来说,Android通过减少布局层次、使用更高效的布局(如
ConstraintLayout
)来优化UI性能。这些方法同样适用于HarmonyOS的UI设计(如RelativeContainer
)
- 一般来说,Android通过减少布局层次、使用更高效的布局(如
我们可以借鉴以往的经验,从资源管理、UI优化、网络优化、电池管理等方面入手,结合HarmonyOS的特性,进一步提升应用的性能。
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或Fragment
。View
通过观察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
等)、LocalStorage
和AppStorage
中的数据。自定义组件通过执行其
build()
方法或者@Builder
装饰的方法来渲染UI,即ViewModel
可以渲染View
。其提高我们对
ViewModel
的阅读能力,代码也更加的清晰。
一致的开发思维
刚刚提到了这么多,其实就是说,对于一个Android开发者来讲,HarmonyOS的开发工具和环境相似,使得我们无需重新学习新的开发环境,可以迅速上手,降低了学习曲线。
使用相同的设计模式和开发思维,能够将已有的经验和技巧无缝迁移到HarmonyOS开发中,减少适应时间。开发者可以直接应用在Android开发中积累的调试经验和性能优化技巧,在HarmonyOS开发中快速定位和解决问题,提高开发效率。
通过一致的开发思维和设计模式,开发者能够编写高质量、可维护的代码,提升应用的稳定性和可扩展性,同时利用在Android开发中积累的性能优化经验,打造高性能的应用,提升用户体验。
Android开发赢麻了。
三、Android人的挑战
换到一个新的环境,不可能一帆风顺,总会有不一样的、不习惯的。作为开发,这都是我们的挑战。
架构差异
Activity、Fragment、View
这三家大家都是非常熟悉的,对于
Fragment
的怨言也是无穷无尽的。简单的复述下:Activity:是Android应用的基本组件之一,负责创建窗口并管理用户界面。每个Activity都代表应用中的一个屏幕,包含用户可以与之交互的界面元素。
Fragment:是Activity的一部分,提供了一种在Activity中构建模块化UI的方式。
Fragment
可以独立存在,也可以与其他Fragment
组合在一个Activity
中,以实现动态和灵活的UI设计。View:是Android UI框架的基础构建块,表示屏幕上的基本元素,例如按钮、文本框和图像视图。
View
可以组合成复杂的布局,用于构建应用的界面。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开发的优势。毕竟我们也是饱受生命周期折磨的人了。
API和库的差异
需要直面的一个很重要的事情就是,一些Android特有的API、三方库在HarmonyOS中可能没有直接的替代方案。
这时候就需要你自己发动聪明的大脑去造轮子了。
但是通常官方都提供了非常丰富的用例,比如TPC仓库,官方的社区,Sample仓库等。而且官方也是提供了提工单的方式,直面开发来解决问题。
很多库,现在开发者和官方也是做了很多迁移工作的。
总而言之就是,只要多问、多想、多找。都是有解决方案的。
语言和框架的差异
类和对象的处理
在Java和Kotlin中,类是构建对象的蓝图,通过类可以创建对象,这些对象是类的实例,具有类定义的属性和方法。
在ArkTs中,类实际上是一个函数(构造函数),其方法定义在原型上,具有高度的动态特性。这种设计会导致有时类无法调用到它的方法,主要原因如下:
方法未绑定到实例:如果方法在调用时没有正确绑定到实例,会导致
this
关键字指向错误。方法定义在原型上:方法定义在原型上,可能导致方法在某些情况下无法访问,特别是在不同的作用域或上下文中调用时。
这对于我们这种使用Java和Kotlin为主力开发语言的人来说,这种动态性也可能导致类的方法在某些情况下无法正确调用,需要我们特别注意方法的绑定和调用上下文。
(你也不想天天看到无法找到方法的错误吧。)
变量作用域和生命周期管理
ArkTS中的闭包在某些方面与 Kotlin 和 Java 的处理有所不同,这主要体现在变量作用域和生命周期的管理上:
1. 变量作用域
- Kotlin: Kotlin 采用了类似于 Java 的作用域管理,但支持更灵活的闭包处理。Kotlin 允许通过
let
,apply
,run
,with
, 和also
等函数来创建闭包,变量作用域在这些函数的范围内有效。 - Java: Java 中的闭包主要通过匿名内部类来实现。变量的作用域是基于匿名内部类的上下文的。Java 中闭包的使用通常较为简单,但变量在闭包中的生命周期较长,可能会引入额外的内存开销。
ArkTS: 在 ArkTS 中,需要注意变量是在函数内部还是外部定义的,以及是否通过闭包引用。闭包的作用域遵循 JavaScript/TypeScript 的规则
特别注意:
- 函数的作用域由函数定义的位置决定。内部函数可以访问其外部函数的变量
- 当内部函数访问外部函数的变量时,闭包会持有对这些变量的引用
- 如果闭包中持有对变量的引用,并且闭包仍然存在,那么这些变量不会被垃圾回收
需要充分的理解ArkTS的闭包,才能避免一些奇怪的问题出现,也能扩展我们解决问题的思路。
- Kotlin: Kotlin 采用了类似于 Java 的作用域管理,但支持更灵活的闭包处理。Kotlin 允许通过
组件构建方式
对于Android来说,我们想写一个Custom View一般会采用继承现有的Group View来实现,但是在ArkUI中,继承UI是不被允许的。需要多多使用组合~
wrapBuilder、ComponentContent等。
当然了如果你是Compose使用者,当我没说~
分布式能力
为什么要把分布式单独拿出来说呢?因为大多数android开发对于分布式的认识是欠缺的。
不只是Android 开发主要集中在单一设备上的应用开发,还有Android的分布式开发非常的复杂。需要我们去额外的考虑许许多多的东西。
所以作为一个Android开发去做HarmonyOS开发,我们需要去主动的认识HarmonyOS的分布式。
如何充分利用HarmonyOS自身的跨端迁移和多端协同能力,去应用到我们的APP中,发挥最大的用处。是值得我们去认真研究的。
当然你可看看 <HarmonyOS第一课>自由流转
三、实际开发中的体验
总体来说,HarmonyOS的开发,给我带来了非常多的惊喜的。
状态管理的优势
丰富的状态管理装饰器,的确让开发变得更加的顺手了起来。减少样板代码不可多得的好手。
独立的状态管理逻辑使得测试变得更加容易。大大简化了复杂应用的状态管理过程~
丰富的组件
很多时候,我们都在烦恼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的开发痛点了。
一致性和统一性
相信每一个Android人都被各种无休止的屏幕、系统适配折磨疯了。当时iOS在旁边偷笑我们,现在HarmonyOS来了,也开始偷笑Android。HarmonyOS 一次开发,多端部署, 一多~它来了
- 不同设备间的屏幕尺寸、色彩风格等存在差异,页面如何适配。
- 不同设备的系统能力有差异,如智能穿戴设备是否具备定位能力、智慧屏是否具备摄像头等,功能如何兼容。
通过布局能力、交互归一、多台组件、资源能力、系统能力全面的支持,我们只需要少量的代码,就可以在多个平台部署,得到相似的UI、交互~
四、结尾
HarmonyOS对于Android、iOS这些老前辈,尽管相对年轻,但是结合自身的创新和独特优势,正在迅速崛起。跨设备互联、分布式技术、微内核架构和统一开发平台使得HarmonyOS在某些方面具有独特的竞争力。HarmonyOS到底行不行、有没有前景。行的。有的。
五、最后
如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。