wordpress主题lovephoto2.0.1,宁波seo高级方法,山东电商运营公司排名,广告设计专业前景分析01 引言 目前#xff0c;移动端主流跨平台方案有Flutter、React Native、uni-app等等#xff0c;还有刚推出不久的Compose-Multiplatform#xff0c;真所谓是百花齐放。这些框架各有特点#xff0c;技术实现各有差异#xff0c;比如Flutter通过Dart编写的UI描述对接Flutte… 01 引言 目前移动端主流跨平台方案有Flutter、React Native、uni-app等等还有刚推出不久的Compose-Multiplatform真所谓是百花齐放。这些框架各有特点技术实现各有差异比如Flutter通过Dart编写的UI描述对接Flutter渲染引擎React Native 则是借助大前端成熟的发展背景利用JS引擎生成UI描述渲染时转化为原生控件复用了原生渲染能力。至于选择哪种框架实现跨平台取决于项目的具体需求、开发团队的技能和偏好。今天我们探索一个新的框架——ArkUI-X。 02 ArkTS、ArkUI、ArkUI-X 在探索ArkUI-X之前先了解一下ArkTS、ArkUI、ArkUI-X三者关系 ArkTS 是华为基于TypeScript自研的开发语言主要用于Harmony应用层开发。ArkTS在保持原 TS 基本语法风格的基础上对 TS 的动态类型特性施加了更严格的约束引入静态类型减少运行时的类型检查有助于性能提升ArkUI 是一套声明式UI开发框架。包含一系列UI组件Text、Image、状态管理State、LocalStorage、界面绘制、交互事件以及实时界面预览工具ArkUI-X 进一步将 ArkUI 扩展到多个 OS 平台目前支持 OpenHarmony、HarmonyOS、Android、iOS后续会逐步增加更多平台支持。 简单来说开发者基于ArkUI框架使用ArkTS语言进行编码构建OpenHarmony/HarmonyOS应用为了让应用运行到Android iOS上利用 ArkUI-X 框架实现各OS平台的适配和构建。接下来我们将创建一个 ArkUI-X 简易Demo通过Demo来探索ArkUI如何绘制组件到屏幕ArkUI-X如何扩展ArkUI到Android平台ArkUI-X如何与Android系统能力交互。 03 快速上手 3.1 环境搭建 DevEco Studio 首先从DevEco Studio官方网站https://developer.huawei.com/consumer/cn/deveco-studio/下载并安装DevEco Studio需要选择4.0.0以上版本以支持 ArkUI-X 套件。然后在DevEco Studio内部分别下载HarmonyOS SDK 和 ArkUI-X非合作企业开发者下载OpenHarmony SDK 和 ArkUI-X。 下载OpenHarmony SDK示例 下载ArkUI-X示例 Android Studio 因为最终要打出Android的apk实现跨平台所以需要安装Android Studio。安装步骤对客户端小伙伴都已经轻车熟路了强调一点需要额外配置ANDROID_HOMEAndroid SDK安装路径到系统环境变量中这是步骤一中DevEco Studio编译项目的必备条件之一。 Xcode 同理使用Xcode导入iOS项目打IPA格式的安装包。 3.2 创建工程 完成上述的环境搭建后就可以创建工程了。可以通过 ArkUI-X 基础模板进行创建。 创建工程 创建完成后得到这样一个工程结构 项目结构 entry存放的是应用程序入口、核心业务逻辑以及资源文件。跨平台的公共源代码放在entry下这点和纯Harmony项目结构一致.arkui-x/android是一个标准的Android项目结构。但目前文件还不完整需要Build App/Hap之后才会生成更为完整的Android项目.arkui-x/iOS: 是一个标准的iOS项目结构包含project.pbxproj。 Build App/Hap之后在build目录下生成的后缀名.hap文件可安装到HarmonyOS平台上。其它平台需要单独打包.arkui-x下的Android项目导入到Android Studio中打出.apk.arkui-x下的iOS项目导入到Xcode中打出.IPA。至此完成三个平台的打包工作。也就是说在Build App过程中ArkUI-X框架会把entry里公用ArkTS代码和resource打入到各平台的安装包或项目文件中。 应用调试方面目前只支持HarmonyOS调试Android和iOS暂不支持ArkTS调试。 其实上述的环境搭建、新建项目、编译打包和安装调试还有另一种方式实现——ACE Tools是一套为ArkUI-X开发者提供的命令行工具。详情参见ACE Tools快速指南https://gitee.com/arkui-x/docs/blob/master/zh-cn/application-dev/quick-start/start-with-ace-tools.md 04 窥探ArkUI的绘制过程 在了解 ArkUI-X 实现跨平台之前我们先简单过一遍ArkTS编写的UI界面是如何绘制到OpenHarmony/HarmonyOS上的以上面的Demo为例 // Index.ets
Entry
Component
struct Index {State message: string 今天星期五build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width(100%)}.height(100%)}
} Build App / Hap 之后得到hap包和Android apk类似对hap包进行解压得到modules.abc字节码文件 (Ark Byte Code)再对字节码文件进行16进制解析可以看到这样一段代码 class Index extends ViewPU {constructor(parent, params, __localStorage, elmtId -1, paramsLambda undefined, extraInfo) {super(parent, __localStorage, elmtId, extraInfo);this.__message new ObservedPropertySimplePU(今天星期五, this, message);this.setInitiallyProvidedValue(params);}setInitiallyProvidedValue(params: Index_Params) {if (params.message ! undefined) {this.message params.message;}}aboutToBeDeleted() {this.__message.aboutToBeDeleted();SubscriberManager.Get().delete(this.id__());}private __message: ObservedPropertySimplePUstring;initialRender() { // 编译器根据ArkTS中对组件的描述部分重新生成jsthis.observeComponentCreation2((elmtId, isInitialRender) {Row.create();Row.height(100%);}, Row);this.observeComponentCreation2((elmtId, isInitialRender) {Column.create();Column.width(100%);}, Column);this.observeComponentCreation2((elmtId, isInitialRender) {Text.create(this.message);Text.fontSize(50);Text.fontWeight(FontWeight.Bold);}, Text);Text.pop();Column.pop();Row.pop();}
} 可以看出我们用Component编写的组件经过ArkCompiler编译后会生成一个继承自ViewPU的js类这个过程和kotlin经过kotlin编译器生成java有点类似。ViewPU位于ArkUI框架的arkui_ace_engine https://gitee.com/openharmony/arkui_ace_engine/tree/master仓 部分代码如下 // 位于 frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_view.ts
abstract class ViewPU extends PUV2ViewBase implements IViewPropertiesChangeSubscriber, IView {constructor(parent: IView, localStorage: LocalStorage, elmtId: number UINodeRegisterProxy.notRecordingDependencies, extraInfo: ExtraInfo undefined) {super(parent, elmtId, extraInfo); this.id_ SubscriberManager.MakeId() : elmtId;if (localStorage) {this.localStorage_ localStorage;}SubscriberManager.Add(this);}public initialRenderView(): void {this.obtainOwnObservedProperties();this.initialRender();...}// 编译器生成的js类会实现该方法主要是对组件的描述protected abstract initialRender(): void;// implements IMultiPropertiesChangeSubscriber UI状态变化回调viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Setnumber): void {if (dependentElmtIds.size !this.isFirstRender()) { // 第一次走initialRenderView()if (!this.dirtDescendantElementIds_.size !this.runReuse_) {this.markNeedUpdate(); // 更新UI 最终调到C}...}let cb this.watchedProps.get(varName);if (cb typeof cb function) {// ArkTS中Prop Watch(xxx) value 的invokecb.call(this, varName);}}
} 从上面ViewPU的部分代码可以提取几点信息 构造方法第一个参数parent: IView说明整个UI结构中存在子父组件概念其内部维护一个子父组件关系链这点和其它UI框架一样构造逻辑依次是 生成element Id用于局部刷新定义localStorage对象用于页面状态共享SubscriberManager.Add(this)添加订阅监听状态变化。 状态变化后回调viewPropertyHasChanged更新UI并执行Watch装饰器逻辑。 更多实现可查看源码当然只需要看看流程即可因为代码的commit频次比较高每次打开看细节都可能有所变化而且还存在很多同名v2类、v2方法。总之ViewPU 是所有组件的基类ViewPU 继承自 PUV2ViewBasePUV2ViewBase 继承自 NativeViewPartialUpdate。从下面代码块的注释得知UI渲染将在 C 里面实现将通过NAPINAPI 和 JNI 类似完成js与C的交互。 // 位于 frameworks/bridge/declarative_frontend/state_mgmt/src/lib/puv2_common/puv2_view_base.ts 和 puv2_view_native_base.d.ts
// implemented in C for release
abstract class PUV2ViewBase extends NativeViewPartialUpdate {...
}/*** NativeViewPartialUpdate aka JSViewPartialUpdate C class exposed to JS* all definitions in this file are framework internal*/
declare class NativeViewPartialUpdate {constructor();markNeedUpdate(): void; // 更新UIfinishUpdateFunc(elmtId: number): void;...static create(newView: NativeViewPartialUpdate): void;
} markNeedUpdate()主要负责UI刷新追踪一下它的实现逻辑 // 位于 frameworks/bridge/declarative_frontend/jsview/js_view.cpp
void JSViewPartialUpdate::JSBind(BindingTarget object)
{JSClassJSViewPartialUpdate::Declare(NativeViewPartialUpdate);JSClassJSViewPartialUpdate::StaticMethod(create, JSViewPartialUpdate::Create, opt);JSClassJSViewPartialUpdate::Method(markNeedUpdate, JSViewPartialUpdate::MarkNeedUpdate);
}void JSViewPartialUpdate::MarkNeedUpdate()
{needsUpdate_ ViewPartialUpdateModel::GetInstance()-MarkNeedUpdate(viewNode_);
} // 位于 frameworks/bridge/declarative_frontend/jsview/models/view_partial_update_model_impl.cpp
bool ViewPartialUpdateModelImpl::MarkNeedUpdate(const WeakPtrAceType node)
{auto weakElement AceType::DynamicCastComposedElement(node);auto element weakElement.Upgrade();if (element) {element-MarkDirty();}return true;
} ComposedElement 是 pipeline 记录组件信息的对象weakElement.Upgrade() 将 ComposedElement 放入 pipeline 中最终通过图形渲染引擎OpenGL ES、Skia完成显示。详细代码参考frameworks/core/pipeline/base/composed_element.cpp。 05 Android跨平台的实现 以上是一个hap包通过ArkUI完成渲染的大致过程回到跨平台ArkUI-X相同的ArkTS代码是如何运行在Android设备上的呢 打开Android项目看到assets下存放着ArkCompiler编译产物和上一节中对.hap包解压后得到的文件一模一样。这是在编译环节中编译脚本copy一份modules.abc字节码和resource到Android工程下作为Android应用资源打包时将以assets形式打入apk。 src/main/assets/arkui-x├── entry| ├── ets| | ├── modules.abc| | └── sourceMaps.map| ├── resouces.index| ├── resouces| └── module.json└── systemres 如何在Android上执行modules.abc字节码呢打开libs发现ArkUI相关的so动态库和jar包。 libs├── armabi-v7a| ├── libarkui_android.so| └── libhilog.so└── arkui_android_adapter.jar 其中libarkui_android.so 是 arkui_ace_engine、arkui_napi 、foundation/appframework、arkui_for_android... 所编译出的动态库是运行和界面渲染的必要环境。另一个arkui_android_adapter.jar的功能是Android Application需要继承arkui_android_adapter.jar包所提供的StageApplication。StageApplication用于初始化资源路径以及加载配置信息。Activity需要继承arkui_android_adapter.jar包所提供的StageActivityStageActivity主要功能是将Android中Activity的生命周期与Harmony中Ability的生命周期进行映射。除此之外arkui_android_adapter.jar适配了系统平台能力如粘贴板、软键盘、字体、存储、日志。这里有个疑问ArkUI-X是如何实现与Android系统之间的交互呢ArkTS和Java没有相互调用的能力为了实现ArkTS和Java交互需要ArkTS与C交互C再与Java交互调用链为ArkTS - NAPI - C - JNI - Java反之亦然看起来十分复杂。ArkUI-X提供一套桥接能力对于开发者来说并不用关心这些封装逻辑实际开发过程中就像是ArkTS和Java直接交互。 下面通过粘贴板的例子探究它的具体实现过程。我们给系统粘贴板设置数据——明天星期六使用ArkTS实现如下看看最终是如何调到Android Framework 给粘贴板设置数据的apiClipboardManager#setPrimaryClip()。 import pasteboard from ohos.pasteboard;Button(拷贝到粘贴板).onClick(() {let pasteData: pasteboard.PasteData pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, 明天星期六)let systemPasteboard: pasteboard.SystemPasteboard pasteboard.getSystemPasteboard()systemPasteboard.setData(pasteData)}) 这段代码对接ArkUI的调用链和上一节中渲染UI类似就不再赘述了。粘贴板相关api 在 arkui_ace_engine中定义成抽象接口如下 // 位于 arkui_ace_engine/frameworks/core/common/clipboard/clipboard.h
namespace OHOS::Ace {class Clipboard : public AceType {DECLARE_ACE_TYPE(Clipboard, AceType);public:~Clipboard() override default;virtual void SetData(const std::string data, CopyOptions copyOption CopyOptions::InApp, bool isDragData false) 0;}
} ArkUI-X 分平台对接口进行不同实现Android的实现在arkui_for_android仓库中其定义和实现如下可见粘贴板设置数据api SetData()最终到ClipboardJni::SetData(data) // 位于 arkui_for_android/capability/java/jni/clipboard/clipboard_impl.h
#include core/common/clipboard/clipboard.hnamespace OHOS::Ace::Platform {class ClipboardImpl final : public Clipboard {public:explicit ClipboardImpl(const RefPtrTaskExecutor taskExecutor) : Clipboard(taskExecutor) {}~ClipboardImpl() override default;void SetData(const std::string data, CopyOptions copyOption CopyOptions::InApp, bool isDragData false) override;}
} // 位于 arkui_for_android/capability/java/jni/clipboard/clipboard_impl.cpp
#include adapter/android/capability/java/jni/clipboard/clipboard_impl.h
#include adapter/android/capability/java/jni/clipboard/clipboard_jni.hnamespace OHOS::Ace::Platform {void ClipboardImpl::SetData(const std::string data, CopyOptions copyOption, bool isDragData){// 对 SetData 实现最终ClipboardJni::SetData(data)taskExecutor_-PostTask([data] { ClipboardJni::SetData(data); }, TaskExecutor::TaskType::PLATFORM, ArkUI-XClipboardImplSetData);}
} 再通过JNI实现C和Java交互 // 位于 arkui_for_android/capability/java/jni/clipboard/clipboard_jni.cpp
static const char CLIPBOARD_PLUGIN_CLASS_NAME[] ohos/ace/adapter/capability/clipboard/ClipboardPluginBase;
static const JNINativeMethod METHODS[] {{ .name nativeInit, .signature ()V, .fnPtr reinterpret_castvoid*(ClipboardJni::NativeInit) },
};
static const char METHOD_SET_DATA[] setData;
static const char SIGNATURE_SET_DATA[] (Ljava/lang/String;)V;// JNI_OnLoad
bool ClipboardJni::Register(std::shared_ptrJNIEnv env)
{// 动态注册 nativeInit 方法java侧调用jclass clazz env-FindClass(CLIPBOARD_PLUGIN_CLASS_NAME);bool ret env-RegisterNatives(clazz, METHODS, ArraySize(METHODS)) 0;return true;
}void ClipboardJni::NativeInit(JNIEnv* env, jobject object)
{jclass clazz env-GetObjectClass(object);g_pluginMethods.setData env-GetMethodID(clazz, METHOD_SET_DATA, SIGNATURE_SET_DATA);
}bool ClipboardJni::SetData(const std::string data)
{auto env JniEnvironment::GetInstance().GetJniEnv();jstring jData env-NewStringUTF(data.c_str());// 反射调用 ClipboardPluginAosp.java setData方法env-CallVoidMethod(g_clipboardObj.get(), g_pluginMethods.setData, jData);if (jData ! nullptr) {env-DeleteLocalRef(jData);}return true;
} 最终通过ClipboardManager#setData() 将ArkTS中设置的内容给到Android的系统粘贴板。 // 位于 arkui_for_android 仓库打出的 arkui_android_adapter.jar 包中
public class ClipboardPluginAosp extends ClipboardPluginBase {private final ClipboardManager clipManager;public ClipboardPluginAosp(Context context) {this.clipManager (ClipboardManager context.getSystemService(Context.CLIPBOARD_SERVICE);nativeInit();}Overridepublic void setData(String data) {if (clipManager ! null) {ClipData clipData ClipData.newPlainText(null, data);clipManager.setPrimaryClip(clipData);}}
} iOS平台的实现和Android平台类似原理都是相通的。 06 小结 通过这一章我们学到了ArkUI-X的环境搭建、项目创建和打包流程探索了ArkTS编写的项目编译后字节码文件如何与ArkUI对接了解了ArkUI-X在Android平台上的实现方案以及ArkUI-X如何适配系统平台能力。ArkUI-X 属于后来者设计之初应该借鉴过其它跨平台方案汲取了优秀设计才形成目前的形态。今后在跨平台的实现上我们又多了一种选择。 参考 ArkUI-X仓库地址https://gitee.com/arkui-xArkUI-arkui_ace_engine仓库地址https://gitee.com/openharmony/arkui_ace_engine深入理解arkui_ace_enginehttps://juejin.cn/post/7305235970286485515