1. 前言
HarmonyOS 2.0发布已经有一段时间了,周边也有一些小伙伴的华为手机升级了,因此本文就来谈一谈HarmonyOS 应用开发相关的一些内容。
首先说下这里说的HarmonyOS,并非OpenHarmony,以目前官方开源的代码来看,这两者不是一回事,通过对OpenHarmony官方开源的代码分析,当前华为手机上运行的HarmonyOS 2.0系统并不是基于OpenHarmony来开发的,并且OpenHarmony当前开放的源码中也没有找到任何Java虚拟机相关代码,没有Java虚拟机怎么能运行Java应用呢。
通过从HarmonyOS 2.0的手机设备上导出framework包反编译分析,目前能确定的是HarmonyOS 2.0是基于AOSP(Android Open Source Project 安卓开源项目)开发的,不过鸿蒙在framework层增加了很多特性,比如支撑hap文件(鸿蒙应用打包后的格式)的启动和运行,支持js UI以及鸿蒙系统最核心的分布式框架(包含分布式软件总线、虚拟设备等内容)等。
因此本文的内容主要聚焦在华为手机上运行的HarmonyOS 2.0系统的应用开发。
2. 应用开发框架
HarmonyOS(后文简称鸿蒙)提供的sdk包含了全面的API供开发者使用,开发鸿蒙应用过程中不会用到原生Android sdk,也没有四大组件、AndroidManifest.xml等相关内容。使用的IDE也是基于IDEA开发的,和AndroidStudio基本类似。鸿蒙sdk主要API框架可以看下图,后面会针对几个特性展开介绍。
2.1. Ability框架
Ability框架是开发者接触最密切的API,表示应用所具备的能力。Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型,是应用的基本组成单元。FA提供与用户交互的能力,PA提供后台服务和数据访问能力。一个应用可包含一个或多个FA和PA,具体如下图所示。
FA和PA作为分发平台分发的最小单位,支持同一个应用在不同的平台下发不同的FA或PA,比如视频通话应用在手机设备下发一个FA和三个PA,但是在智慧屏设备只下发一个FA和一个PA,如下图所示。
下面我们来看一下FA/PA和原生Android系统的关系。其实Ability框架的几个重要部分都需要依赖AOSP的四大组件来支撑,这对Android开发者来说再熟悉不过了,具体看下图。
这个依赖关系是怎么建立的呢?由于在鸿蒙应用开发过程中,对AOSP的任何特性和接口都是透明的,没办法直接使用Android原生的四大组件,因此这个依赖过程发生在编译期,鸿蒙开发插件会在编译过程生成对应Android四大组件相关的一些模板代码(为什么称为模板代码,后面会再次提到),这些模板代码会通过一个配置文件(config.json)来获取Ability相关的界面(FA)和服务(PA),然后产生关联。
这里的FA也有类似Activity的生命周期,具体咱们还是来看下图。
2.2. UI框架
鸿蒙应用的UI框架支持两种开发语言:Java和JS,Java UI框架使用鸿蒙AGP框架,AGP框架并不使用Android原生的任何现成的UI控件,其内部主要是基于SurfaceView重新自定义了一套UI控件;JS UI框架主要依赖于ACE框架,一套跨平台的UI框架,那么这个跨平台要跨到哪里呢?就是前面提到的OpenHarmony,其内部也有ACE框架。如果一个鸿蒙应用是通过纯JS编写的,那么理论上是可以快速迁移到OpenHarmony平台。鸿蒙应用开发工具同时支持两种平台的App开发,但是要切换SDK。
2.3. 服务卡片
服务卡片是FA的一种界面展示形式,将FA的重要信息或操作展示到卡片上,目的是服务直达和减少体验层级。服务卡片也是鸿蒙官方最近一直在推的一种。其实跟原生Android的桌面挂件类似。但是也有不一样的地方,服务卡片有单独的入口,让用户可以更快到达。并且提供了更方便的接入方式——原子化服务。
2.4. 原子化服务
服子化服务,实际是一种轻量级的应用(免安装)。类似于Google的instant APP。主要是想提供一种新的服务体验,让应用开发更简单、服务的获取和使用更便捷,有单独的入口。相比传统应用,原子化服务具备如下一些特点。
特点 | 原子化服务 | 传统应用 |
---|---|---|
分发平台 | 原子化服务平台 | 应用商店 |
桌面icon | 无桌面icon,但可手动添加到桌面,显示形式为服务卡片 | 有桌面icon |
安装要求 | 免安装(包体不超过10MB) | 需要安装 |
2.5. 流转 - 多设备分布式操作
流转是鸿蒙平台分布式能力的一个很重要的体现,其实现原理就是鸿蒙的分布式框架。
流转主要有两种,跨端迁移和多端协同。
跨端迁移:指在A端运行的FA迁移到B端上,完成迁移后, B端FA继续任务,而A端应用退出。
比如一个同事下班后乘坐地铁回家,在地铁上用手机观看一部电影,但是电影还没看完,他已经回到家里,回家后打开电视就能继续看。
多端协同:多端上的不同或相同FA/PA同时运行,或交替运行实现完整的业务。
比如现在很多学生都要上网课,它可以将平板侧应用A做答题板,智慧屏侧应用B做直播。不同的端共用完成同一个任务。
鸿蒙系统内置了分布式框架,应用只需要通过其sdk提供的API去开发即可快速地支持这种分布式特性,不过这种流转当前仅支持多端登录同一个华为账号 + 局域网内。
除了以上介绍的这些,还有很多鸿蒙平台特有的能力,比如卡片分享、分布式文件服务、分布式数据服务和AI接口,总体而言鸿蒙对分布式的支持是比较全面的,这也体现了全场景和超级终端的概念。
3. 发布包文件解析
3.1. 项目结构和发布包格式
鸿蒙应用项目的结构和发布包形态如下图所示,一个鸿蒙应用主要包含两种模块:
Entry模块:应用的入口模块。一个应用项目中有且只有一个entry模块。
Feature模块:应用的动态特性模块。一个应用项目可以包含零个或多个feature模块。
鸿蒙的应用架构默认实现了类似Android上的组件化架构,每个模块支持独立编译、打包、安装和调试。对于大型应用可以有效提升开发效率。
鸿蒙应用发布包是APP包(.app后缀),内部由一个或多个HAP(HarmonyOS Ability Package)包组成。每个模块编译后对应生成一个hap文件。
3.2. HAP文件结构
每个hap文件就对应项目中的一个模块,这里我们具体看下hap的文件结构。HAP是Ability的部署包,每个hap文件支持单独安装和调试,hap文件的内部结构如下图所示,下面我们逐一介绍。
apk文件:这个apk文件的内容跟一个常规apk文件没什么区别。包含AndroidManifest.xml、res、resources.arsc和classes.dex。这里的classes.dex文件内部包含了应用打包过程自动产生的模板代码。这个模板代码就是让Android原生四大组件和Ability产生关联的关键。
config.json:配置文件,它的作用相当于原生Android应用的Androidmanifest.xml,FA/PA和权限申请等都需要在该文件里面配置。
assets目录:包含了Java代码用到的资源文件以及js代码。
libs目录:包含依赖库,与常规apk文件内部的libs类似。
pack.info:一些打包信息。
classes.dex:apk外部的classes.dex,开发者写的业务代码。
为什么这里要把Java代码拆分到两个地方,一个在apk内一个在apk外呢?猜测可能是基于两方面的考虑,一方面是这两份代码的含义不一样,apk内部的是编译过程产生的模板代码,apk外部是开发者写的业务代码。一方面是层次不一样,apk内部的代码是在原生Android 应用运行框架用到,apk外部的是鸿蒙应用运行框架加载的。
3.3. 应用安装
通过上面内容我们知道,一个鸿蒙APP文件是多个HAP文件的压缩包,那么它会如何被安装到手机上呢。鸿蒙应用安装时系统会从每个hap文件中提取出apk文件,entry.hap内部的apk文件会被重命名为base.apk(作为应用的入口),其它hap内部的apk会被重命名为split_featureName.apk,安装后的目录结构如下所示。
/data/app/package.name-xxx==/base.apk
/data/app/package.name-xxx==/split_featureName.apk
/data/app/package.name-xxx==/feature_entry-debug-rich-signed.hap
/data/app/package.name-xxx==/feature_featureName-entry-debug-rich-signed.hap
这个安装过程看起来像AAB(Android App Bundle)。但有个地方不太一样,原生的split APKs机制在安装aab包时会合并每个apk内部的AndroidManifest.xml文件到base.apk文件里面,但鸿蒙这里的feature.apk的AndroidManifest.xml内容没有被合并到base.apk内部。
4. 应用启动和运行
4.1. HAP启动流程
对于AOSP而言,启动一个应用必然需要apk文件。前文提到的hap文件里面包含的apk文件就是启动一个鸿蒙应用的入口(安装后就是base.apk)。该apk内部包含一个ShellMyApplication类,该类就是鸿蒙应用启动的入口代码。ShellMyApplication的代码如下所示:
import ohos.abilityshell.HarmonyApplication;
public class ShellMyApplication extends HarmonyApplication {
@Override
public void onCreate() {
super.onCreate();
}
}
这个代码就是我们前面提到的鸿蒙应用在编译过程中产生的模板代码,主要作用是作为一层代理将逻辑转移到鸿蒙的运行框架。这个HarmonyApplication继承了原生Android的Application,并且在AndroidManifest.xml里面注册。所以当Android原生系统启动一个App的时候,首先就会加载这个Application。启动的大概流程如下图所示。
在Application的attach阶段,会去加载鸿蒙应用运行框架,解析config.json并全局缓存,因为该配置信息整个应用生命周期都会使用,然后再去配置类加载器和底层库的路径(包括所有apk文件和hap文件),这样才能确保其它apk文件和hap文件里面的类和so文件能被正常加载。在onCreate阶段会进行DataAbility的初始化,这跟原生Android的Provider的初始化时机是类似的。
4.2. HAP运行框架
这里以一个有界面的DemoAbility为例来看一下鸿蒙的应用运行框架,如下图所示。其它的Ability的流程都是类似的。
这里我们分成了四个层次,上面两层可以认为是原生Android APK的运行框架。下面两层是鸿蒙应用的运行框架。
当启动一个界面时,原生Android会加载一个Activity文件(DemoAbilityShellActivity,也是模板代码),DemoAbilityShellActivity只是简单的实现了AbilityShellActivity,而AbilityShellActivity也只是简单实现了Activity基类,然后把所有方法转移到Activity的代理层,这个代理层已经脱离了Activity,后续就正式进入了鸿蒙应用运行框架,然后再层层回调到AOSP的View框架,
通过这个流程我们大概知道,鸿蒙Framework在这边除了自己实现了一套UI框架之外,主要承担的就是代理作用。如果以后HarmonyOS拿掉AOSP后,鸿蒙应用项目是不是只需要切换一下鸿蒙sdk即可适配新系统呢?
5. Android项目适配
通过上文我们大致了解了鸿蒙应用开发知识,现在有一个很现实的问题,对于原Android项目,我们要如何使用鸿蒙平台的这些特殊能力呢。鸿蒙致力于打造一套全新的开发框架,但如果想将现有Android老项目迁移到鸿蒙,就需要重新开发,其成本不言而喻。 以目前来看鸿蒙与AOSP有着千丝万缕的联系,因此是不是有一种折中的方案,直接基于现有的Android项目去适配鸿蒙,去使用鸿蒙的平台特性呢。
当前网上流传一种混合打包的解决方案能解决这个问题,这里简要介绍下该方案。核心就是将entry模块的模板apk文件替换成现在有Android项目编译出来的apk文件,由于entry模块的apk是整个应用的入口,当该apk被替换成原Android项目打包出来的apk文件后,当该应用启动时就会直接加载Android apk文件。而Android apk与其它Hap模块的调用方式可以通过反射或四大组件。替换后的APP包结构如下。
虽然该方案有成功上线的案例,但也存在一些不足:
未来不确定性:一种官方支持但没有公开的临时方案。
打包步骤繁琐:需要两次打包,先打包出apk文件再打包鸿蒙应用。
代码交互能力弱:安卓应用和鸿蒙应用的代码是不可见的,主要通过Android四大组件和反射进行互调通信。
对原项目入侵性:需要修改现有Android项目的Application类继承于HarmonyApplication。
6. 总结
鸿蒙平台具有丰富的特性:流转,原子化服务,系统级AI接口(文字识别,语音识别,关键字提取等),分布式数据服务,分布式文件服务等,这些大部分是当前Android平台所没有的。
当然如果需要基于鸿蒙平台重新开发一个完整应用,成本也是很大的,特别是对于一些大型应用。因此除了上述的混合打包方案之外,目前官方主推的一种快速适配方案是单独开发一个独立服务卡片,然后通过服务卡片与原生应用通信。不过独立服务卡片的能力有限,不能充分使用鸿蒙平台的特性。因此一般的做法是在独立服务卡片提供一些简单的功能,然后引导用户跳转到Android原生的应用去。
总的来说,当前还没有较为方便可靠的方案支持Android原生应用适配鸿蒙平台,或许我们应该重新理解一下鸿蒙,其打造一套全新的开发框架和开发工具,提供丰富的平台特性和鸿蒙系的跨平台能力,目的就是希望未来能完全脱离原生Android系统。