鸿蒙南向开发实战:电话服务开发指南

201 篇文章 1 订阅
126 篇文章 0 订阅

场景介绍

Modem厂商库初始化是指在厂商库里实现const HRilOps *RilInitOps(const struct HRilReport *reportOps)函数,在该函数里处理三个重要的功能:

  • 接收RIL Adapter事件回调的函数指针,当Modem有业务事件上报时,调用对应的函数指针,把事件上报给RIL Adapter。

  • 创建读取Modem设备节点的线程,在该线程里会循环地读取Modem上报的事件,并把接收的Modem信息解析为具体业务相关的事件进行上报。

  • 返回业务请求接口的函数指针给RIL Adapter。

接口说明

Modem厂商库初始化接口。

表1 Modem厂商库初始化接口功能介绍

接口名描述
const HRilOps *RilInitOps(const struct HRilReport * reportOps)接口功能:Modem厂商库运行的入口。
参数reportOps:RIL Adapter传入的事件回调函数指针。
返回值:业务请求接口的函数指针。

开发步骤

  1. RilInitOps接口中设置RIL Adapter传入的事件回调函数指针。
    // 定义Modem厂商库回调函数指针
    static struct HRilReport g_reportOps = {
        OnCallReport,    // 通话相关业务回调函数
        OnDataReport,    // 蜂窝数据相关业务回调函数
        OnModemReport,   // Modem相关业务回调函数
        OnNetworkReport, // 搜网相关业务回调函数
        OnSimReport,     // SIM卡相关业务回调函数
        OnSmsReport      // 短信相关业务回调函数
    };
  1. 创建主线程g_reader,开启消息循环。
    pthread_attr_t t;
    pthread_attr_init(&t);
    pthread_attr_setdetachstate(&t, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&g_reader, &t, ReaderLoop, &t); // 创建线程
  1. 在g_eventListeners线程用open()打开Modem设备节点,并创建g_reader线程循环读取处理Modem上报的消息。
    g_fd = open(g_devicePath, O_RDWR); // 打开设备节点,入参g_devicePath是Modem设备节点
    pthread_attr_init(&attr);   
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);   
    ret = pthread_create(&g_eventListeners, &attr, EventListeners, NULL);
  1. 返回业务请求接口的函数指针。

    // call模块业务请求接口结构体
    typedef struct {
        // 获取呼叫列表
        void (*GetCallList)(ReqDataInfo *requestInfo, const void *data, size_t dataLen);
        // 拨打电话
        void (*Dial)(ReqDataInfo *requestInfo, const void *data, size_t dataLen);
        // 挂断电话
        void (*Hangup)(ReqDataInfo *requestInfo, const void *data, size_t dataLen);
        // 拒接来电
        void (*Reject)(ReqDataInfo *requestInfo, const void *data, size_t dataLen);
        // 接听来电
        void (*Answer)(ReqDataInfo *requestInfo, const void *data, size_t dataLen);
    } HRilCallReq;
    
    // call模块回调函数指针
    static const HRilCallReq g_callReqOps = { 
        .GetCallList = ReqGetCallList, // 获取呼叫列表接口
        .Dial = ReqDial,               // 拨打电话接口
        .Hangup = ReqHangup,           // 挂断电话接口
        .Reject = ReqReject,           // 拒接来电接口
        .Answer = ReqAnswer,           // 接听来电接口
    };
    
    // 业务请求结构体
    typedef struct { 
        const HRilCallReq *callOps;       // 通话相关业务请求结构体指针
        const HRilSimReq *simOps;         // SIM卡相关业务请求结构体指针
        const HRilSmsReq *smsOps;         // 短彩信相关业务请求结构体指针
        const HRilDataReq *dataOps;       // 蜂窝数据相关业务请求结构体指针
        const HRilNetworkReq *networkOps; // 搜网相关业务请求结构体指针
        const HRilModemReq *modemOps;     // Modem相关业务请求结构体指针
    } HRilOps;  
      
    // 业务请求接口定义
    HRilOps g_hrilOps = {
        .callOps = &g_callReqOps,       // 定义通话业务请求接口
        .simOps = &g_simReqOps,         // 定义SIM卡业务请求接口
        .smsOps = &g_smsReqOps,         // 定义短彩信业务请求接口
        .networkOps = &g_networkReqOps, // 定义蜂窝数据业务请求接口
        .dataOps = &g_dataReqOps,       // 定义搜网业务请求接口
        .modemOps = &g_modemReqOps,     // 定义Modem业务请求接口
    };

调测验证

  1. hdc_std工具连接调试设备,把编译生成的libril_vendor.z.so库文件(参见Modem厂商库集成指导)通过以下命令推到/system/lib/目录下。

    hdc_std file send libril_vendor.z.so /system/lib/
  2. 执行hdc_std shell sync,hdc_std shell reboot重启设备。

    hdc_std shell sync
    hdc_std shell reboot
  3. 执行hdc_std shell hilog,根据日志查看函数RilInitOps()是否正确执行完成。如下调测验证日志供参考:

    09-02 07:40:47.807   455   455 I 01f08/HrilHdf: [LoadVendor-(hril_hdf.c:148)] RilInit LoadVendor start with rilLibPath:libril_vendor.z.so
    09-02 07:40:47.830   455   455 I 01f08/HrilHdf: [LoadVendor-(hril_hdf.c:163)] HRilRegOps completed

Modem业务请求及响应开发指导

场景介绍

Modem业务请求及响应是指RIL Adapter收到电话服务具体业务请求后,调用Modem厂商库初始化获得的函数指针,把具体业务请求发送给厂商库,厂商库根据业务请求ID做相应的业务处理。

接口说明

Modem业务请求及响应接口。

表2 Modem业务请求及响应接口功能介绍(以拨号功能模块为例)

接口名描述
void ReqDial(ReqDataInfo *requestInfo, const void *data, size_t dataLen);接口功能:对拨号请求进行处理。
参数requestInfo:请求类型信息。
参数data:被叫号码信息。
参数dataLen:数据长度。
返回值:无。
void (*OnCallReport)(struct ReportInfo reportInfo, const void *data, size_t dataLen);接口功能:对通话业务执行结果进行响应,即当请求业务执行完成后,Modem将该请求执行的结果上报给RIL Adapter。
参数reportInfo:返回类型信息。
参数data:返回数据。
参数dataLen:数据长度。
返回值:无。

开发步骤

  1. 在ReqDial()接口中对拨号请求进行处理。

    // 拨号请求接口实现
    void ReqDial(ReqDataInfo *requestInfo, const void *data, size_t dataLen)
    {
        HRilDial *pDial = NULL;
        char cmd[MAX_BUFF_SIZE] = {0};
        const char *clir = NULL;
        int ret;
        int err = HRIL_ERR_SUCCESS;
        struct ReportInfo reportInfo = {};
        ResponseInfo *pResponse = NULL;
        if (data == NULL) {
            TELEPHONY_LOGE("data is null!!!");
            err = HRIL_ERR_INVALID_PARAMETER;
            reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);
            OnCallReport(reportInfo, NULL, 0);
            return;
        }
        pDial = (HRilDial *)data;
        switch (pDial->clir) {
            case CALL_CLIR_INVOCATION:
                clir = "I";
                break; /* invocation */
            case CALL_CLIR_SUPPRESSION:
                clir = "i";
                break; /* suppression */
            case CALL_CLIR_SUBSCRIPTION_DEFUALT:
            default:
                clir = "";
                break; /* subscription default */
        }
        (void)sprintf_s(cmd, MAX_BUFF_SIZE, "ATD%s%s;", pDial->address, clir);
        ret = SendCommandLock(cmd, NULL, 0, &pResponse); // 发送AT指令
        ......
    }
  2. 在Modem执行完拨号命令后,调用OnCallReport()回调函数,把该请求执行的结果上报给RIL Adapter。

    ret = SendCommandLock(cmd, NULL, 0, &pResponse);
    if (ret != 0 || (pResponse != NULL && pResponse->success == 0)) {
        TELEPHONY_LOGE("ATD send failed");
        err = HRIL_ERR_GENERIC_FAILURE;
    }
    reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);
    OnCallReport(reportInfo, NULL, 0); // 调用通话相关回调函数

调测验证

  1. hdc_std工具工具连接调试设备,把编译生成的libril_vendor.z.so库文件通过以下命令推到/system/lib/目录下。

    hdc_std file send libril_vendor.z.so /system/lib/
  2. 执行hdc_std shell sync,hdc_std shell reboot重启设备。

    hdc_std shell sync
    hdc_std shell reboot
  3. hdc_std shell后执行./system/bin/ril_adapter_test,输入编号1,根据提示输入电话号码,测试拨打电话功能。

    hdc_std shell
    # ./system/bin/ril_adapter_test
    ----> Test Enter  --------->Call---------------------
     
    1----> RilUnitTest::OnRequestCallDialTest
    2----> RilUnitTest:: OnRequestCallHangupTest
    3----> RilUnitTest:: OnRequestCallAnswerTest
    4----> RilUnitTest::OnRequestCallGetCurrentCallsStatusTest
    5----> RilUnitTest::OnRequestRefusedCallTest
       
    1
  4. 另开一个终端窗口,执行hdc_std shell hilog,通过日志查看函数ReqDial()是否正确执行完成。如下调测验证日志供参考:

    09-02 07:55:09.073   455  2059 I 01f08/RilVendor: [SendCommandLock-(at_support.c:226)] command ATD18675231804;, NeedATPause:0, atCmd:AT
    09-02 07:55:09.099   455  2053 I 01f08/Rilvendor: [ProcessResponse-(at_support.c:159)] processLine line = OK
    09-02 07:55:09.100   455  2053 E 01f08/RilVendor: [ReportStrWith-(vendor_util.c:63)] str or prefix parameter is null.
    09-02 07:55:09.100   455  2053 E 01f08/RilVendor: [ProcessLastResponse-(vendor_channel.c:77)] g_bufferCur endLine is null
    09-02 07:55:09.100   455  2059 I 01f08/RilVendor: [SendCommandLock-(at_support.c:243)] err = 0
    09-02 07:55:09.100   455  2053 I 01f08/RilVendor: [ProcessResponse-(at_support.c:159)] processLine line = ^ORIG:1,0

Modem事件上报开发指导

场景介绍

Modem事件上报是指在厂商库的Modem设备节点读取线程,循环读取到Modem主动上报的消息后,对Modem上报事件进行解析,然后上报给RIL Adapter。

接口说明

Modem事件上报接口。

表3 Modem事件上报接口功能介绍

接口名描述
void OnNotifyOps(const char *s, const char *smsPdu)接口功能:对Modem上报的事件进行分发处理。
参数s:AT指令前缀。
参数smsPdu:短信PDU信息。
返回值:无。

开发步骤

  1. 在Modem设备节点读取线程g_reader里调用OnNotifyOps()解析具体的Modem上报事件,判断命令类型,并调用OnXxxReport()把解析得到的各模块事件上报给hril业务层。

    // 将Modem上报数据解析为对应模块的主动上报事件
    void OnNotifyOps(const char *s, const char *smsPdu)
    {
        int ret = 0;
        struct ReportInfo reportInfo = {0};
        reportInfo.error = HRIL_ERR_SUCCESS;
        reportInfo.type = HRIL_NOTIFICATION;
        if (GetRadioState() == HRIL_RADIO_POWER_STATE_UNAVAILABLE) {
            return;
        }
        TELEPHONY_LOGD("enter to [%{public}s]:%{public}s", s, smsPdu);
        // 通过AT指令判断主动上报命令类型
        if (ReportStrWith(s, "+CRING:") || ReportStrWith(s, "RING") || ReportStrWith(s, "IRING") ||
            ReportStrWith(s, "NO CARRIER") || ReportStrWith(s, "+CCWA") || ReportStrWith(s, "^CCALLSTATE") ||
            ReportStrWith(s, "^CEND") || ReportStrWith(s, "^CCWA")) {
            reportInfo.notifyId = HNOTI_CALL_STATE_UPDATED;
            OnCallReport(reportInfo, NULL, 0);
        } else if (ReportStrWith(s, "+CMT:")) {
            reportInfo.notifyId = HNOTI_SMS_NEW_SMS;
            OnSmsReport(reportInfo, (void *)smsPdu, strlen(smsPdu));
        }
        // 将各模块事件上报给hril业务层
        ......
    }
  2. hril业务层将上报事件分发给Telephony Service。

    // 呼叫状态主动上报
    int32_t HRilCall::CallStateUpdated(
        int32_t slotId, int32_t notifyType, const HRilErrno e, const void *response, size_t responseLen)
    {
        struct HdfSBuf *dataSbuf = HdfSBufTypedObtain(SBUF_IPC);
        if (serviceCallbackNotify_ == nullptr) {
            TELEPHONY_LOGE("RilAdapter serviceCallbackNotify_ is null");
            HdfSBufRecycle(dataSbuf);
            return HDF_FAILURE;
        }
        // 分发处理
        int32_t ret = serviceCallbackNotify_->dispatcher->Dispatch(
            serviceCallbackNotify_, HNOTI_CALL_STATE_UPDATED, dataSbuf, nullptr);
        if (ret != HDF_SUCCESS) {
            HdfSBufRecycle(dataSbuf);
            return HDF_FAILURE;
        }
        HdfSBufRecycle(dataSbuf);
        return HDF_SUCCESS;
    }

调测验证

  1. hdc_std工具工具连接调试设备,把编译生成的libril_vendor.z.so库文件通过以下命令推到/system/lib/目录下。

    hdc_std file send libril_vendor.z.so /system/lib/
  2. 执行hdc_std shell sync,hdc_std shell reboot重启设备。

    hdc_std shell sync
    hdc_std shell reboot
  3. hdc_std shell后执行./system/bin/ril_adapter_test,输入编号1,根据提示输入电话号码,测试拨打电话功能。

    hdc_std shell
    # ./system/bin/ril_adapter_test
    ----> Test Enter  --------->Call---------------------
     
    1----> RilUnitTest::OnRequestCallDialTest
    2----> RilUnitTest:: OnRequestCallHangupTest
    3----> RilUnitTest:: OnRequestCallAnswerTest
    4----> RilUnitTest::OnRequestCallGetCurrentCallsStatusTest
    5----> RilUnitTest::OnRequestRefusedCallTest
       
    1
  4. 另开一个终端窗口,执行hdc_std shell hilog,通过日志查看函数OnNotifyOps()是否正确执行完成。如下调测验证日志供参考:

    01-01 00:08:01.334   546   551 D 02b01/TelRilTest: [DialResponse-(tel_ril_call.cpp:280)] DialResponse --> radioResponseInfo->serial:2, radioResponseInfo->error:0
    01-01 00:08:01.334   546   557 D 02b01/TelRilTest: [ProcessEvent-(tel_ril_test.cpp:1262)] TelRilTest::DemoHandler::ProcessEvent --> eventId:101
    01-01 00:08:01.334   143   512 D 02b01/Rilvendor: [ReadResponse-(channel.c:93)] g_bufferCur : 
    01-01 00:08:01.334   143   512 D 02b01/Rilvendor: ^ORIG:1,0
    01-01 00:08:01.334   143   512 D 02b01/Rilvendor: [ReadResponse-(channel.c:108)] AT< ^ORIG:1,0
    01-01 00:08:01.334   143   512 D 02b01/Rilvendor: [ProcessResponse-(at_support.c:137)] processLine line = ^ORIG:1,0
    01-01 00:08:01.334   143   512 D 02b01/Rilvendor: [OnNotifyOps-(vendor_report.c:126)] enter to [^ORIG:1,0]:(null)
    01-01 00:08:01.335   143   512 W 02b01/Rilvendor: [OnNotifyOps-(vendor_report.c:167)] enter to  is unrecognized command: ^ORIG:1,0
    01-01 00:08:01.335   143   512 D 02b01/Rilvendor: [ProcessLastResponse-(channel.c:37)] last data more than one line , FindEndOfLine  g_bufferCur: 
    01-01 00:08:01.335   143   512 E 02b01/Rilvendor: [ProcessLastResponse-(channel.c:39)] g_bufferCur endLine is null
    01-01 00:08:01.336   143   512 D 02b01/Rilvendor: [ReadResponse-(channel.c:93)] g_bufferCur : 
    01-01 00:08:01.336   143   512 D 02b01/Rilvendor: ^CCALLSTATE: 1,0,1
    01-01 00:08:01.336   143   512 D 02b01/Rilvendor: [ReadResponse-(channel.c:108)] AT< ^CCALLSTATE: 1,0,1
    01-01 00:08:01.336   143   512 D 02b01/Rilvendor: [ProcessResponse-(at_support.c:137)] processLine line = ^CCALLSTATE: 1,0,1
    01-01 00:08:01.336   143   512 D 02b01/Rilvendor: [OnNotifyOps-(vendor_report.c:126)] enter to [^CCALLSTATE: 1,0,1]:(null)
    01-01 00:08:01.336   546   551 D 02b01/CoreService: [OnRemoteRequest-(tel_ril_manager.cpp:80)] RilManager OnRemoteRequest code:1001
    01-01 00:08:01.336   546   551 D 02b01/CoreService: [NotifyObserver-(observer_handler.cpp:76)] handler->SendEvent:8

开发实例

  • 去电开发实例 去电的调用流程示例如下图所示: 图1 去电调用时序图

    zh-cn_image_0000001171507146

    当应用触发去电动作时,RIL Adapter会接收到拨打电话的请求,hril调用对应的拨打电话的接口ReqDial()。在该接口里会把电话服务传过来的数据封装为对应的AT指令发送到Modem,Modem执行完拨号命令后通过OnCallReport()接口把响应结果上报给RIL Adapter。

    // call模块回调函数指针
    static const HRilCallReq g_callReqOps = { 
        .GetCallList = ReqGetCallList, // 获取呼叫列表接口
        .Dial = ReqDial,               // 拨打电话接口
        .Hangup = ReqHangup,           // 挂断电话接口
        .Reject = ReqReject,           // 拒接来电接口
        .Answer = ReqAnswer,           // 接听来电接口
    }; 
    
    // 系统业务请求接口定义
    HRilOps g_hrilOps = {
        .callOps = &g_callReqOps,       // 定义通话业务请求接口
        .simOps = &g_simReqOps,         // 定义SIM卡业务请求接口
        .smsOps = &g_smsReqOps,         // 定义短彩信业务请求接口
        .networkOps = &g_networkReqOps, // 定义蜂窝数据业务请求接口
        .dataOps = &g_dataReqOps,       // 定义搜网业务请求接口
        .modemOps = &g_modemReqOps,     // 定义Modem业务请求接口
    };
    
    // 拨号请求接口实现
    void ReqDial(ReqDataInfo *requestInfo, const void *data, size_t dataLen)
    {
        HRilDial *pDial = NULL;
        char cmd[MAX_BUFF_SIZE] = {0};
        const char *clir = NULL;
        int ret;
        int err = HRIL_ERR_SUCCESS;
        struct ReportInfo reportInfo = {};
        ResponseInfo *pResponse = NULL;
        if (data == NULL) {
            TELEPHONY_LOGE("data is null!!!");
            err = HRIL_ERR_INVALID_PARAMETER;
            reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);
            OnCallReport(reportInfo, NULL, 0);
            return;
        }
        pDial = (HRilDial *)data;
        switch (pDial->clir) {
            case CALL_CLIR_INVOCATION:
                clir = "I";
                break; /* invocation */
            case CALL_CLIR_SUPPRESSION:
                clir = "i";
                break; /* suppression */
            case CALL_CLIR_SUBSCRIPTION_DEFUALT:
            default:
                clir = "";
                break; /* subscription default */
        }
        (void)sprintf_s(cmd, MAX_BUFF_SIZE, "ATD%s%s;", pDial->address, clir);
        ret = SendCommandLock(cmd, NULL, 0, &pResponse); // 发送AT命令
        if (ret != 0) {
            err = HRIL_ERR_CMD_SEND_FAILURE;
            TELEPHONY_LOGE("ATD send failed");
        } else {
            if (pResponse != NULL && pResponse->success == 0) {
                TELEPHONY_LOGE("ReqDial return ERROR");
                err = HRIL_ERR_CMD_NO_CARRIER;
            }
        }
        reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);
        OnCallReport(reportInfo, NULL, 0); // 调用通话相关业务回调函数
        FreeResponseInfo(pResponse);
    }
  • 来电开发实例 来电的调用流程示例如下图所示: 图2 来电调用时序图

    zh-cn_image_0000001214727595

    Modem设备节点读取线程g_reader会循环读取Modem上报的消息,当Modem接收到来电时会主动上报来电相关的信息;

    当该线程通过调用OnNotifyOps()解析到Modem上报的数据是以"+CRING"、"RING"等字符开头时,表示有来电事件,然后通过OnCallReport(reportInfo, NULL, 0)上报给RIL Adapter完成来电事件上报。

    // 将Modem上报数据解析为对应模块的主动上报事件
    void OnNotifyOps(const char *s, const char *smsPdu)
    {
        int ret = 0;
        struct ReportInfo reportInfo = {0};
        reportInfo.error = HRIL_ERR_SUCCESS;
        reportInfo.type = HRIL_NOTIFICATION;
        if (GetRadioState() == HRIL_RADIO_POWER_STATE_UNAVAILABLE) {
            return;
        }
        TELEPHONY_LOGD("enter to [%{public}s]:%{public}s", s, smsPdu);
        // 通过AT指令判断主动上报命令类型
        if (ReportStrWith(s, "+CRING:") || ReportStrWith(s, "RING") || ReportStrWith(s, "IRING") ||
            ReportStrWith(s, "NO CARRIER") || ReportStrWith(s, "+CCWA") || ReportStrWith(s, "^CCALLSTATE") ||
            ReportStrWith(s, "^CEND") || ReportStrWith(s, "^CCWA")) {
            reportInfo.notifyId = HNOTI_CALL_STATE_UPDATED;
            OnCallReport(reportInfo, NULL, 0);  // 调用通话相关业务回调函数
        } else if (ReportStrWith(s, "+CMT:")) {
            reportInfo.notifyId = HNOTI_SMS_NEW_SMS;
            OnSmsReport(reportInfo, (void *)smsPdu, strlen(smsPdu));
        } 
        // add your codes
        ......
    }

Modem厂商库集成指导

编译设置

Modem厂商库可通过BUILD.gn编译为一个动态库,在RIL Adapter启动时用dlopen方式加载到系统中,然后执行厂商库的初始化操作(参见Modem厂商库初始化开发指导),BUILD.gn编写示例如下:

import("//build/ohos.gni")
RIL_ADAPTER = "//base/telephony"
ohos_shared_library("ril_vendor") { // Modem厂商库名称
    sources = [ // 编译源文件
        "at_call.c",
        "at_data.c",
        "xxx.c",
    ]
    include_dirs = [ // 包含的头文件目录
        "$RIL_ADAPTER/ril_adapter/vendor/include",
        "$RIL_ADAPTER/ril_adapter/interfaces/innerkits",
        "include",
    ]
    deps = [ // 内部依赖
        "//drivers/adapter/uhdf2/osal:libhdf_utils",
        "//base/telephony/core_service/utils:libtelephony_common",
    ]
    external_deps = [ "hilog:libhilog" ] // 外部依赖

    part_name = "ril_adapter"  // 部件名称
    subsystem_name = "telephony" // 子系统名称    
}

调测指南验证

  1. 编译代码。

  2. 查看/out/{device_name}/telephony/ril_adapter目录是否存在libril_vendor.z.so,存在证明集成成功。否则检查代码,重新编译验证。

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料

 获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

 有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值