1 NativeAbilityFwk框架
对于hi3321芯片的brandy sdk而言,ui的开发是基于NativeAbilityFwk应用框架;
NativeAbilityFwk是基于穿戴产品的特点从鸿蒙原生Native中裁剪定制的轻量级UI;
JSAbility由JavaScript编写;通过ACE注册到AMS中管理,先放着;
NativeAbility由C++编写;通过LauncherAbility注册到AMS中管理;NativeAbility只有一个,可以管理多个Slice的生命周期;
2 添加sliceID
添加slice的时候,首先为待添加页面定义一个id,在NativeAbility中通过ID来管理和切换页面和视图;
//need_add
//AppViewIDs.h
typedef enum : uint16_t {
VIEW_INVALID = 0,
VIEW_MAIN = 1,
//...
VIEW_MAX_INTER_APP,
} AppViewId;
3 添加slice信息
添加slice的时候,需要将slice信息添加到NativeAbility中管理;
3.1 NativeAbilityFwk的入口函数
这部分是程序ui入口执行的逻辑流,不用修改,只是为了结构清晰;
//don't need add;
//NativeAbility.cpp
void NativeAbility::InstallAbility(void){
InstallNativeAbility(nullptr, this);
NativeLauncher::GetInstance().InitNativeLauncher();
return;
}
//NativeLauncher.cpp
void NativeLauncher::InitNativeLauncher() {
GraphicStartUp::InitFontEngine(MEM_POOL_UI_FONT, MEM_POOL_UI_FONT_SIZE,\
OHOS::VECTOR_FONT_DIR, DEFAULT_VECTOR_FONT_FILENAME);
InitBtHeadsetEvent();
//cae:把每个initSlices函数都调用了一遍,把创建的实例和ID对应上
InitAllNativeSlices();
CallerLogModel::GetInstance()->InitPhoneService();
PresenterTask::GetInstance()->Init();
PlayersModel::GetInstance()->AudioInit();
ActivityModel::GetInstance()->Init();
ScreenModels::GetInstance()->InitCardConfig();
VocassistModel::GetInstance()->Init();
HeartModel::GetInstance()->Init();
BluetoothModel::GetInstance()->Init();
ScreenModels::GetInstance()->PreLoadAppimages();
NativeAbility::GetInstance().SetDefaultSliceId(VIEW_MAIN_SAMPLE);
}
void NativeLauncher::InitAllNativeSlices(void){
uint16_t size = sizeof(SLICE_MAPPER) / sizeof(SLICE_MAPPER[0]);
for (int index = 0; index < size; index++) {
if ((SLICE_MAPPER[index].id >= VIEW_MAX_INTER_APP)) continue;
//cae:将SLICE_MAPPER[]中的每个地址以函数指针的形式调用;
( this->*(SLICE_MAPPER[index].func) )();
}
return;
}
3.2 添加slice字符串ID
添加slice的时候,为页面添加对应的字符串ID;
//need add
//NativeLauncher.cpp 感觉作用不大,先放着;
struct ViewIdToString{
AppViewId id;
char idForUikit[256];
};
static const ViewIdToString VIEW_MAPPER[] = {
{VIEW_MAIN, "main"},
{VIEW_SETTING, "setting"},
//...
//attention: static const data;
};
3.3 添加slice的实例化地址
添加slice的时候,为页面添加对应的实例化函数地址;
至此slice在程序启动后执行到此,将slice实例添加进NativeAbility中管理;添加slice结束,后面是编写slice模块;
//need add
//NativeLauncher.cpp
struct AppViewMapper{
AppViewId id;
void (NativeLauncher::*func)(void);
};
static const AppViewMapper SLICE_MAPPER[] = {
{VIEW_MAIN, &NativeLauncher::InitMainSlice},
{VIEW_SETTING, &NativeLauncher::InitSettingSlice},
//...
//attention: static const data;
};
//new一个模块实例,将实例同ID对应上,添加到NativeAbility;
void NativeLauncher::InitMainSlice(void){
SliceProxy *mainSlice = new AbilitySliceProxy<MainView, MainPresenter>;
if (mainSlice != nullptr) {
NativeAbility::GetInstance().AddSliceProxy(VIEW_MAIN, mainSlice);
}
}
//需要将上面实例化函数用到的VIEW_MAIN、MainView、MainPresenter所在的xx.h添加到当前文件中;
//编写VIEW_MAIN slice,就是编写 MainView.h、 MainPresenter.h 以及 MainView.cpp、MainPresenter.cpp;
4 slice页面构建
slice页面构建使用mvp (model view presenter)框架来构建;
框架原理详见Sparta OpenHarmony Native Slice开发用户指南;
这些mvp模块,更应该将其理解为是一种属性和结构;只有创建了他们的存储实例化后,才是具体的对象;(重要)
4.1 model模块
用于获取数据;目前只是创建页面,暂时不用先放着;
4.2 presenter模块
用于处理页面监听,数据交互;需要用户实现如下4个生命周期接口;
#ifndef SLEEPDETAIL_PRESENTER_H
#define SLEEPDETAIL_PRESENTER_H
#include "Presenter.h"
#include "SleepDetailView.h"
namespace OHOS
{
class SleepDetailView;
class SleepDetailPresenter : public Presenter<SleepDetailView>,
public UIView::OnClickListener,
public UIView::OnDragListener
{
public:
SleepDetailPresenter();
virtual ~SleepDetailPresenter();
void OnStart() override;
void OnPause() override;
void OnResume() override;
void OnStop() override;
static SleepDetailPresenter *GetInstance();
private:
//因为继承了OnClickListener,所以有OnClick属性,至于参数,原函数也这样直接继承;
bool OnClick(UIView &view, const ClickEvent &event) override;
bool OnDrag(UIView &view, const DragEvent &event) override;
};
} //namespace OHOS
#endif //SLEEPDETAIL_PRESENTER_H
#include "AppViewIDs.h"
#include "NativeAbility.h"
#include "SleepDetailPresenter.h"
namespace OHOS{
//当前Presenter属性实例化的内存指针
static SleepDetailPresenter *g_pSleepDetailPresenter = nullptr;
SleepDetailPresenter::SleepDetailPresenter(){
g_pSleepDetailPresenter = this;
}
SleepDetailPresenter::~SleepDetailPresenter(){
g_pSleepDetailPresenter = nullptr;
}
SleepDetailPresenter *SleepDetailPresenter::GetInstance(){
return g_pSleepDetailPresenter;
}
void SleepDetailPresenter::OnStart(){
Presenter<SleepDetailView>::OnStart();
}
void SleepDetailPresenter::OnPause(void){
Presenter<SleepDetailView>::OnPause();
}
void SleepDetailPresenter::OnResume(void){
Presenter<SleepDetailView>::OnResume();
}
void SleepDetailPresenter::OnStop(void){
Presenter<SleepDetailView>::OnStop();
}
bool SleepDetailPresenter::OnDrag(UIView &view, const DragEvent &event){
if (event.GetDragDirection() == DragEvent::DIRECTION_LEFT_TO_RIGHT)
{
NativeAbility::GetInstance().ChangeSlice(VIEW_APPLIST);
}
return true;
}
bool SleepDetailPresenter::OnClick(UIView &view, const ClickEvent &event){
// 根据控件id查找是哪个控件触发了点击回调
// if (strcmp(view.GetViewId(), 控件ID) == 0)
return true;
}
}
4.3 view模块
用于视图构建和显示;需要用户实现如下四个生命周期接口;
#ifndef SLEEPDETAIL_VIEW_H
#define SLEEPDETAIL_VIEW_H
#include "BaseView.h"
#include "View.h"
#include "components/ui_view_group.h"
#include "components/ui_label.h"
#include "components/ui_image_view.h"
namespace OHOS
{
class SleepDetailPresenter;
class SleepDetailView : public View<SleepDetailPresenter>{
public:
SleepDetailView();
virtual ~SleepDetailView();
static SleepDetailView *GetInstance(void);
void OnStart() override;
void OnSetUpView() override;
void OnTearDownView() override;
void OnStop() override;
private:
UIView::OnDragListener *draglistener{nullptr};
void InitImage(void);
void InitLable(void);
//以下控件属性通过new实例化后,add到对应root属性上;
UIViewGroup *container_ = nullptr; //container属性指针
UILabel *titleLabel{nullptr}; //label属性指针
UIImageView *icon{nullptr}; //image属性指针
};
} //namespace OHOS
#endif //SLEEPDETAIL_PRESENTER_H
#include "SleepDetailView.h"
#include "SleepDetailPresenter.h"
#include "common/image_cache_manager.h"
#include "UiConfig.h"
#include "ui_resource_image.h"
namespace OHOS{
static SleepDetailView *g_pSleepDetailView{nullptr};
SleepDetailView::SleepDetailView(){
g_pSleepDetailView = this;
}
SleepDetailView::~SleepDetailView(){
container_->RemoveAll();
delete container_;
container_ = nullptr;
if (titleLabel != nullptr){
}
if (icon != nullptr){
delete icon;
icon = nullptr;
}
g_pSleepDetailView = nullptr;
}
SleepDetailView *SleepDetailView::GetInstance(){
return g_pSleepDetailView;
}
void SleepDetailView::InitLable(){
//label属性 new
titleLabel = new UILabel();
//label属性 扩展
titleLabel->SetPosition(62, 288, 369, 200);
titleLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30U);
titleLabel->SetText("No record.wear watch to monitor sleep quality.");
titleLabel->SetStyle(STYLE_TEXT_COLOR, Color::Gray().full);
//label属性 add
container_->Add(titleLabel);
}
void SleepDetailView::InitImage(){
//image属性 new
icon = new UIImageView();
//image属性 扩展
ImageInfo *imageInfo = ImageCacheManager::GetInstance().LoadOneInMultiRes(SSLEEP_SCREMINDSLEEP_396X292, SSLEEP);
if (imageInfo == nullptr){
return;
}
icon->SetPosition(35, 17, 396, 292);
icon->SetSrc(imageInfo);
//image属性 add
container_->Add(icon);
}
void SleepDetailView::OnStart(){
//container_容器属性 new
container_ = new UIViewGroup();
if (container_ == nullptr){
return;
}
//container_容器属性 扩展
container_->SetPosition(0, 0);
container_->SetWidth(Screen::GetInstance().GetWidth());
container_->SetHeight(Screen::GetInstance().GetHeight());
draglistener = static_cast<UIView::OnDragListener *>(SleepDetailPresenter::GetInstance());
if (draglistener == nullptr){
return;
}
container_->SetOnDragListener(draglistener);
container_->SetTouchable(true);
container_->SetDraggable(true);
InitImage();
InitLable();
//container_容器属性 add
RootView::GetInstance()->Add(container_);
//label、image >> container_ ;
//container_ >> 当前RootView ;
}
void SleepDetailView::OnSetUpView(){
View<SleepDetailPresenter>::OnSetUpView();
Draw();
}
void SleepDetailView::OnTearDownView(){
View<SleepDetailPresenter>::OnTearDownView();
return;
}
void SleepDetailView::OnStop(){
View<SleepDetailPresenter>::OnStop();
RootView::GetInstance()->RemoveAll();
return;
}
}
5 CmakeList增加
在对应目录下的cmakelist中添加xx.cpp路径,以及头文件路径;
如果编译有报错,改一下报错;页面添加流程至此完成;
如果是因为内存不够想要注释掉界面,那么只要在该界面的InitxxSlice函数中,注释掉new Slice即可;
如果是因为makefile list太长,想要注释掉界面可以反推如何注释呀;
在makefile中把xx.c和include目录注释掉,那么哪里用到了xx.c和xx.h呢?可不就是InitxxSlice函数new的时候需要头文件和源文件么。
那么哪里调用了InitxxSlice函数呢?倒推全部注释掉即可;
6 先放着
/***
一开始的问题: slice的内存是在new该slice方法的时候分配的;new的时候顺便调用构造函数;
那么问题来了: slice的内存在delete的时候释放的么? delete的时候顺便调用析构函数?
我没有看到delete slice的;在onStop切换时,会调用析构函数,然后delete自身内存么?
大约因为slice由NativeAbility管理,所以slice的方法都继承自AbilitySlice.h中的方法;
Slice1在切换的时候,会自动调用slice1的onStop()方法;
如果没有重写slice1的onStop()的话,那么就会继承AbilitySlice.h中的onStop()如下:
//AbilitySlice.h
void OnStop() override
{
if (view_) {
view_->OnStop();
delete view_; //这里调用view_的析构函数,然后delete view_ 的内存?
view_ = nullptr;
}
if (presenter_) {
presenter_->OnStop();
delete presenter_;
presenter_ = nullptr;
}
}
合理的解释是每次onstop的时候都把slice的view视图给清空释放了,但是slice的内存还是在的;
其实这些slice从一开始Initxx new的时候,new了内存一直没释放;
onstop没有删除自身,它会调用继承父类的onstop函数,清除slice上的控件内存,但是slice还是存在的;
整个ability框架中的slice内存从Initxx函数中new了方法后就开始存在,直到程序结束咯?
***/