摘要:本文介绍移植开发板时如何适配服务启动引导部件bootstrap_lite,并介绍相关的运行机制原理。

本文分享自华为云社区《OpenHarmony移植案例与原理 - startup子系统之bootstrap_lite服务启动引导部件(1)》,作者:zhushy。

bootstrap_lite服务启动引导组件提供了各服务和功能的启动入口标识。在SAMGR(System ability manager,系统服务管理)启动时,会调用bootstrap_lite标识的入口函数,并启动系统服务。本文介绍下移植开发板时如何适配服务启动引导部件bootstrap_lite,并介绍下相关的运行机制原理。bootstrap_lite启动引导部件定义在build\lite\components\startup.json。bootstrap_lite启动引导部件源代码目录如下:

base/startup/bootstrap_lite/    # 启动引导组件
└── services└── source                  # 启动引导组件源文件目录

1、bootstrap_lite服务启动引导部件适配示例

1.1 产品解决方案配置启用部件

移植开发板适配startup子系统之bootstrap_lite服务启动引导部件时,需要在产品解决方案的config.json增加下述配置项,可以参考vendor\bestechnic\display_demo\config.json中的配置示例:

    {"subsystem": "startup","components": [{"component": "bootstrap_lite"},......]},

1.2 使用bootstrap服务启动部件提供的初始化宏函数

然后就可以使用bootstrap服务启动部件提供的初始化宏函数SYS_SERVICE_INIT、APP_SERVICE_INIT等来自动初始化服务,示例代码可以参考device\board\fnlink\v200zr\liteos_m\at\at_wifi.c中的用法,片段如下,可以看到调用了宏函数来初始化RegisterCustomATCmd函数实现的服务。device\board\bearpi\bearpi_hm_nano\app\目录下有更多的使用示例。下文分析实现机制原理。

static void RegisterCustomATCmd()
{cmd_tbl_t cmd_list[] = {{"AT+IFCFG", 8, at_lwip_ifconfig, "AT+IFCFG - ifconfig\n"},{"AT+STARTAP", 7, at_start_softap, "AT+STARTAP - start wifi softap\n"},{"AT+STOPAP", 1, at_stop_softap, "AT+STOPAP - stop wifi softap\n"},{"AT+STARTSTA", 1, at_start_wifista, "AT+STARTSTA - start wifi sta\n"},{"AT+STOPSTA", 1, at_stop_wifista, "AT+STOPSTA - stop wifi sta\n"},{"AT+DHCP", 3, at_setup_dhcp, "AT+DHCP - dhcp\n"},{"AT+DHCPS", 3, at_setup_dhcps, "AT+DHCPS - dhcps\n"},};for (int i = 0; i < sizeof(cmd_list) / sizeof(cmd_tbl_t); i++) {console_cmd_add(&cmd_list[i]);}
}
SYS_SERVICE_INIT(RegisterCustomATCmd);

1.3 链接脚本中增加zInit代码段

适配bootstrap_lite部件时,还需要在链接脚本文件中手动新增如下段,链接脚本示例可以参考//device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/out/best2600w_liteos/_best2001.lds,还可以参考device\soc\hisilicon\hi3861v100\sdk_liteos\build\link\link.ld.S。从链接脚本片段中可以看出,有.zinitcall.bsp、.zinitcall.device、.zinitcall.core、.zinitcall.sys.service、.zinitcall.sys.feature、.zinitcall.run、.zinitcall.app.service、.zinitcall.app.feature、.zinitcall.test和.zinitcall.exit等几种类型的段。

/* zInit code and data - will be freed after init */.zInit (.) :{__zinitcall_bsp_start = .;KEEP (*(.zinitcall.bsp0.init))KEEP (*(.zinitcall.bsp1.init))KEEP (*(.zinitcall.bsp2.init))KEEP (*(.zinitcall.bsp3.init))KEEP (*(.zinitcall.bsp4.init))__zinitcall_bsp_end = .;. = ALIGN(4);__zinitcall_device_start = .;KEEP (*(.zinitcall.device0.init))KEEP (*(.zinitcall.device1.init))KEEP (*(.zinitcall.device2.init))KEEP (*(.zinitcall.device3.init))KEEP (*(.zinitcall.device4.init))__zinitcall_device_end = .;. = ALIGN(4);__zinitcall_core_start = .;KEEP (*(.zinitcall.core0.init))KEEP (*(.zinitcall.core1.init))KEEP (*(.zinitcall.core2.init))KEEP (*(.zinitcall.core3.init))KEEP (*(.zinitcall.core4.init))__zinitcall_core_end = .;. = ALIGN(4);__zinitcall_sys_service_start = .;KEEP (*(.zinitcall.sys.service0.init))KEEP (*(.zinitcall.sys.service1.init))KEEP (*(.zinitcall.sys.service2.init))KEEP (*(.zinitcall.sys.service3.init))KEEP (*(.zinitcall.sys.service4.init))__zinitcall_sys_service_end = .;. = ALIGN(4);__zinitcall_sys_feature_start = .;KEEP (*(.zinitcall.sys.feature0.init))KEEP (*(.zinitcall.sys.feature1.init))KEEP (*(.zinitcall.sys.feature2.init))KEEP (*(.zinitcall.sys.feature3.init))KEEP (*(.zinitcall.sys.feature4.init))__zinitcall_sys_feature_end = .;. = ALIGN(4);__zinitcall_run_start = .;KEEP (*(.zinitcall.run0.init))KEEP (*(.zinitcall.run1.init))KEEP (*(.zinitcall.run2.init))KEEP (*(.zinitcall.run3.init))KEEP (*(.zinitcall.run4.init))__zinitcall_run_end = .;. = ALIGN(4);__zinitcall_app_service_start = .;KEEP (*(.zinitcall.app.service0.init))KEEP (*(.zinitcall.app.service1.init))KEEP (*(.zinitcall.app.service2.init))KEEP (*(.zinitcall.app.service3.init))KEEP (*(.zinitcall.app.service4.init))__zinitcall_app_service_end = .;. = ALIGN(4);__zinitcall_app_feature_start = .;KEEP (*(.zinitcall.app.feature0.init))KEEP (*(.zinitcall.app.feature1.init))KEEP (*(.zinitcall.app.feature2.init))KEEP (*(.zinitcall.app.feature3.init))KEEP (*(.zinitcall.app.feature4.init))__zinitcall_app_feature_end = .;. = ALIGN(4);__zinitcall_test_start = .;KEEP (*(.zinitcall.test0.init))KEEP (*(.zinitcall.test1.init))KEEP (*(.zinitcall.test2.init))KEEP (*(.zinitcall.test3.init))KEEP (*(.zinitcall.test4.init))__zinitcall_test_end = .;. = ALIGN(4);__zinitcall_exit_start = .;KEEP (*(.zinitcall.exit0.init))KEEP (*(.zinitcall.exit1.init))KEEP (*(.zinitcall.exit2.init))KEEP (*(.zinitcall.exit3.init))KEEP (*(.zinitcall.exit4.init))__zinitcall_exit_end = .;. = ALIGN(4);} > FLASH

1.4 配置编译时链接bootstrap库

另外,bootstrap_lite部件会编译//base/startup/bootstrap_lite/services/source/bootstrap_service.c,该文件中,通过SYS_SERVICE_INIT将Init函数符号指定到__zinitcall_sys_service_start和__zinitcall_sys_service_end段中,由于没有显式调用Init函数,所以需要将它强制链接到最终的镜像。可以参考device\board\goodix\gr5515_sk\liteos_m\config.gni文件中的链接选项。恒玄的开发板适配时,是配置到vendor\bestechnic\display_demo\config.json文件中的自己定义的配置项force_link_libs里,该配置项在device\soc\bestechnic\bes2600\BUILD.gn中被解析、链接。

board_ld_flags = [
...."-lbootstrap",

1.5 调用OHOS_SystemInit接口

函数void OHOS_SystemInit(void)定义在文件base\startup\bootstrap_lite\services\source\system_init.c中,在移植适配时,需要调用该接口。可以参考文件device\soc\bestechnic\bes2600\liteos_m\sdk\bsp\rtos\liteos\liteos_m\board.c中的使用示例。

int main(void);
extern void OHOS_SystemInit(void);......OHOS_SystemInit();......while (1) {osDelay(1000);TRACE(0, "main idle");}
}

2、bootstrap_lite服务启动引导部件实现原理之system_init

bootstrap_lite服务启动部件实现了服务的自动初始化,即服务的初始化函数无需显式调用,它是使用宏定义的方式申明,在系统启动时自动被执行。实现原理是将服务启动的函数通过宏申明之后,放在预定义好的zInit代码段中,系统启动的时候调用OHOS_SystemInit接口,遍历该代码段并调用其中的函数。因此在适配移植时,需要在device/soc/下面具体的芯片的链接脚本中添加zInit段,并且在main函数里调用OHOS_SystemInit接口。

2.1 bootstrap_lite服务启动引导部件的初始化宏

bootstrap_lite服务启动引导部件的初始化宏定义在文件utils\native\lite\include\ohos_init.h,片段如下。初始化函数宏SYS_SERVICE_INIT(func)用于标识核心系统服务的初始化入口,该宏识别的函数在启动过程中核心系统服务优先级2阶段被调用;初始化宏SYS_SERVICE_INIT_PRI(func, priority)可以指定优先级数值,优先级的取值范围为[0,5),调用顺序为0, 1, 2, 3, 4。

  /*** @brief Identifies the entry for initializing and starting a core system service by the* priority 2.** This macro is used to identify the entry called at the priority 2 in the core system* service phase of the startup process. \n** @param func Indicates the entry function for initializing and starting a core system service.* The type is void (*)(void).*/#define SYS_SERVICE_INIT(func) LAYER_INITCALL_DEF(func, sys_service, "sys.service")/*** @brief Identifies the entry for initializing and starting a core system service by the* specified priority.** This macro is used to identify the entry called at the specified priority in the core system* service phase of the startup process. \n** @param func Indicates the entry function for initializing and starting a core system service.* The type is void (*)(void).* @param priority Indicates the calling priority when starting the core system service in the* startup phase. The value range is [0,5), and the calling sequence is 0, 1, 2, 3, and 4.*/#define SYS_SERVICE_INIT_PRI(func, priority) LAYER_INITCALL(func, sys_service, "sys.service", priority)

更多的初始化宏见下文的列表,处理这些初始化宏,还有可以指定优先级的版本XXX_PRI。

2.2 LAYER_INITCALL宏定义

从上文已知,bootstrap_lite服务启动引导部件的初始化宏会调用LAYER_INITCALL_DEF和LAYER_INITCALL宏。这些宏的定义在文件utils\native\lite\include\ohos_init.h,代码片段如下。⑴处声明函数类型,无参无返回值。⑵处处理定义分层初始化共享库宏LAYER_INIT_SHARED_LIB的情况,如果没有定义该宏,则执行⑹。在文件foundation/distributedschedule/samgr_lite/samgr/BUILD.gn中定义了该宏,在移植适配芯片开发板时,没有定义这个宏。⑶处定义5个分层初始化级别,⑷处定义7个构建值(constructor value,简称CTOR Value)。⑸处是宏LAYER_INITCALL的定义,该宏需要4个参数,分别是初始化服务或功能函数func;layer是分层名称,支持的取值为device、core、sys_service、sys_feature、app_service、app_feature和run,拼装为CTOR_VALUE_XXX;clayer参数在定义宏LAYER_INIT_SHARED_LIB时未使用;priority是优先级参数。attribute((constructor))表示这段代码将在main函数前调用。当传入参数为(myFunc, sys_feature, “sys.feature”, 2)时,函数宏替换为:static __attribute__((constructor(130 + 2))) void BOOT_sys_featurer2myFunc {myFunc();}。等于定义个一个新的启动引导函数BOOT_sys_featurer2myFunc()。

当没有定义LAYER_INIT_SHARED_LIB宏时,执行⑹,当传入参数为(myFunc, sys_feature, “sys.feature”, 2)时,函数宏替换为:static const InitCall __attribute__((used)) __zinitcall_sys_feature_myFunc __attribute__((section(".zinitcall.sys.feature2.init"))) = myFunc,除了__attribute__部分,等于声明一个函数类型InitCall的变量__zinitcall_sys_feature_myFunc。该函数变量放入section段".zinitcall.sys.feature2.init"内,所以移植适配时,需要在芯片开发板的链接脚本里添加zInit代码段。

⑴  typedef void (*InitCall)(void);#define USED_ATTR __attribute__((used))⑵  #ifdef LAYER_INIT_SHARED_LIB
⑶  #define LAYER_INIT_LEVEL_0 0#define LAYER_INIT_LEVEL_1 1#define LAYER_INIT_LEVEL_2 2#define LAYER_INIT_LEVEL_3 3#define LAYER_INIT_LEVEL_4 4
⑷  #define CTOR_VALUE_device 100#define CTOR_VALUE_core 110#define CTOR_VALUE_sys_service 120#define CTOR_VALUE_sys_feature 130#define CTOR_VALUE_app_service 140#define CTOR_VALUE_app_feature 150#define CTOR_VALUE_run  700
⑸  #define LAYER_INITCALL(func, layer, clayer, priority)                                     \static __attribute__((constructor(CTOR_VALUE_##layer + LAYER_INIT_LEVEL_##priority))) \void BOOT_##layer##priority##func() {func();}#else
⑹  #define LAYER_INITCALL(func, layer, clayer, priority)            \static const InitCall USED_ATTR __zinitcall_##layer##_##func \__attribute__((section(".zinitcall." clayer #priority ".init"))) = func#endif// Default priority is 2, priority range is [0, 4]#define LAYER_INITCALL_DEF(func, layer, clayer) \LAYER_INITCALL(func, layer, clayer, 2)

2.3 OHOS_SystemInit函数

函数OHOS_SystemInit()定义在文件base\startup\bootstrap_lite\services\source\system_init.c,代码如下。调用宏函数MODULE_INIT、SYS_INIT和函数SAMGR_Bootstrap()进行初始化启动。

void OHOS_SystemInit(void)
{MODULE_INIT(bsp);MODULE_INIT(device);MODULE_INIT(core);SYS_INIT(service);SYS_INIT(feature);MODULE_INIT(run);SAMGR_Bootstrap();
}

我们详细分析下宏函数MODULE_INIT和SYS_INIT的源代码,这2个宏函数在文件base\startup\bootstrap_lite\services\source\core_main.h中定义,代码如下。这些宏函数的参数大都为name和step,name取值为bsp、device、core、service、feature、run,step取值为0。从⑺和⑻处可以看出,分别调用SYS_CALL、MODULE_CALL两个宏,第二个参数设置为0。⑸处定义的MODULE_BEGIN函数,用于返回链接脚本中定义的代码段的开始地址,当传入参数为bsp时,返回&__zinitcall_bsp_start,MODULE_BEGIN函数被展开为如下片段:

{        extern InitCall __zinitcall_sys_start;      \InitCall *initCall = &__zinitcall_bsp_start; \(initCall);                                      \
}

⑹处定义的MODULE_END函数,用于返回链接脚本中定义的代码段的结束地址,当传入参数为bsp时,返回&__zinitcall_bsp_end,MODULE_END函数被展开为如下片段:

{        extern InitCall __zinitcall_bsp_end;      \InitCall *initCall = &__zinitcall_bsp_end; \(initCall);                                    \
}    

⑶和⑷处定义的SYS_BEGIN、SYS_END代码类似,分于返回链接脚本中定义的标记系统服务或特性的代码段的开始、结束地址,即&__zinitcall_sys_service_start、&__zinitcall_sys_service_end、&__zinitcall_sys_feature_start、&__zinitcall_sys_feature_end。⑴处定义的SYS_CALL函数,分别获取链接脚本中zInit代码段的开始initcall和结束地址initend,这些地址存放的是初始化函数的地址,语句 (*initcall)()会循环调用执行这些初始化函数。⑵处MODULE_CALL宏类似。

⑴  #define SYS_CALL(name, step)                                      \do {                                                          \InitCall *initcall = (InitCall *)(SYS_BEGIN(name, step)); \InitCall *initend = (InitCall *)(SYS_END(name, step));    \for (; initcall < initend; initcall++) {                  \(*initcall)();                                        \}                                                         \} while (0)⑵  #define MODULE_CALL(name, step)                                      \do {                                                             \InitCall *initcall = (InitCall *)(MODULE_BEGIN(name, step)); \InitCall *initend = (InitCall *)(MODULE_END(name, step));    \for (; initcall < initend; initcall++) {                     \(*initcall)();                                           \}                                                            \} while (0)......
⑶  #define SYS_BEGIN(name, step)                                 \({        extern InitCall __zinitcall_sys_##name##_start;       \InitCall *initCall = &__zinitcall_sys_##name##_start; \(initCall);                                           \})⑷  #define SYS_END(name, step)                                 \({        extern InitCall __zinitcall_sys_##name##_end;       \InitCall *initCall = &__zinitcall_sys_##name##_end; \(initCall);                                         \})⑸  #define MODULE_BEGIN(name, step)                          \({        extern InitCall __zinitcall_##name##_start;       \InitCall *initCall = &__zinitcall_##name##_start; \(initCall);                                       \})
⑹  #define MODULE_END(name, step)                          \({        extern InitCall __zinitcall_##name##_end;       \InitCall *initCall = &__zinitcall_##name##_end; \(initCall);                                     \})⑺  #define SYS_INIT(name)     \do {                   \SYS_CALL(name, 0); \} while (0)⑻  #define MODULE_INIT(name)     \do {                      \MODULE_CALL(name, 0); \} while (0)

3、 bootstrap_lite服务启动引导部件实现原理之bootstrap_service

在文件base\startup\bootstrap_lite\services\source\bootstrap_service.h中定义了2个宏函数INIT_APP_CALL和INIT_TEST_CALL,分别用来调用代码段&__zinitcall_app_XXX_start、&__zinitcall_app_XXX_end和&__zinitcall_test_start、&__zinitcall_test_end之间的初始化启动函数。bootstrap_service.h文件中的宏和base\startup\bootstrap_lite\services\source\core_main.h文件中的宏类似,不再一一分析。

3.1 结构体struct Bootstrap

在文件base\startup\bootstrap_lite\services\source\bootstrap_service.c中定义了结构体struct Bootstrap,如下代码⑵处。其中结构体中的INHERIT_SERVICE定义在文件foundation/distributedschedule/samgr_lite/interfaces/kits/samgr/service.h,见代码片段⑴处。

⑴   #define INHERIT_SERVICE                                          \const char *(*GetName)(Service * service);                   \BOOL (*Initialize)(Service * service, Identity identity);    \BOOL (*MessageHandle)(Service * service, Request * request); \TaskConfig (*GetTaskConfig)(Service * service)......
⑵  typedef struct Bootstrap {INHERIT_SERVICE;Identity identity;uint8 flag;} Bootstrap;

结构体Identity定义在文件foundation\distributedschedule\samgr_lite\interfaces\kits\samgr\message.h中,用于标识一个服务或特性。

/*** @brief Identifies a service and feature.** You can use this structure to identity a {@link IUnknown} feature to which messages will be* sent through the asynchronous function of {@link IUnknown}. \n**/
struct Identity {/** Service ID */int16 serviceId;/** Feature ID */int16 featureId;/** Message queue ID */MQueueId queueId;
};

3.2 Init(void)函数

讲解移植适配示例时,我们已经知道,bootstrap_lite部件会编译//base/startup/bootstrap_lite/services/source/bootstrap_service.c,该文件通过函数宏SYS_SERVICE_INIT将Init()函数符号灌段到__zinitcall_sys_service_start和__zinitcall_sys_service_end代码段中,代码片段如下。下文再详细分析GetName、Initialize、MessageHandle和GetTaskConfig函数。

  static const char *GetName(Service *service);static BOOL Initialize(Service *service, Identity identity);static TaskConfig GetTaskConfig(Service *service);static BOOL MessageHandle(Service *service, Request *request);static void Init(void){static Bootstrap bootstrap;bootstrap.GetName = GetName;bootstrap.Initialize = Initialize;bootstrap.MessageHandle = MessageHandle;bootstrap.GetTaskConfig = GetTaskConfig;bootstrap.flag = FALSE;SAMGR_GetInstance()->RegisterService((Service *)&bootstrap);}SYS_SERVICE_INIT(Init);

3.3 GetName和Initialize函数

GetName函数代码如下,其中BOOTSTRAP_SERVICE定义在文件foundation\distributedschedule\samgr_lite\interfaces\kits\samgr\samgr_lite.h中,取值为"Bootstrap",表示启动引导服务。

static const char *GetName(Service *service)
{(void)service;return BOOTSTRAP_SERVICE;
}

Initialize函数定义如下,用于设置启动引导服务的标识信息。

static BOOL Initialize(Service *service, Identity identity)
{Bootstrap *bootstrap = (Bootstrap *)service;bootstrap->identity = identity;return TRUE;
}

3.4 MessageHandle函数和GetTaskConfig函数

MessageHandle函数和GetTaskConfig函数代码如下,MessageHandle函数用于处理各种请求,请求消息编号定义request->msgId定义在文件foundation\distributedschedule\samgr_lite\interfaces\kits\samgr\samgr_lite.h中的枚举enum BootMessage,如下⑴处所示。在MessageHandle函数中,当请求的消息编号为BOOT_SYS_COMPLETED系统服务初始化完成时,检查应用服务是否加载。当应用没有加载时,执行INIT_APP_CALL宏函数调用zInit代码段上的应用服务和应用特性初始化函数。然后执行⑶,调用函数SAMGR_SendResponseByIdentity进行响应。GetTaskConfig函数用于获取任务配置。

⑴  typedef enum BootMessage {/** Message indicating that the core system service is initialized *//** 标识核心系统服务初始化完成 */BOOT_SYS_COMPLETED,/** Message indicating that the system and application-layer services are initialized *//** 标识应用层服务初始化完成 */BOOT_APP_COMPLETED,/** Message indicating service registration during running *//** 标识运行时的服务注册 */BOOT_REG_SERVICE,/** Maximum number of message IDs *//** 标识消息最大值,butt是烟蒂;屁股;枪托的意思,表示尾部吧 */BOOTSTRAP_BUTT} BootMessage;......static BOOL MessageHandle(Service *service, Request *request){Bootstrap *bootstrap = (Bootstrap *)service;switch (request->msgId) {case BOOT_SYS_COMPLETED:
⑵            if ((bootstrap->flag & LOAD_FLAG) != LOAD_FLAG) {INIT_APP_CALL(service);INIT_APP_CALL(feature);bootstrap->flag |= LOAD_FLAG;}
⑶            (void)SAMGR_SendResponseByIdentity(&bootstrap->identity, request, NULL);break;case BOOT_APP_COMPLETED:(void)SAMGR_SendResponseByIdentity(&bootstrap->identity, request, NULL);break;case BOOT_REG_SERVICE:(void)SAMGR_SendResponseByIdentity(&bootstrap->identity, request, NULL);break;default:break;}return TRUE;}static TaskConfig GetTaskConfig(Service *service){(void)service;// The bootstrap service uses a stack of 2 KB (0x800) in size and a queue of 20 elements.// You can adjust it according to the actual situation.TaskConfig config = {LEVEL_HIGH, PRI_NORMAL, 0x800, 20, SHARED_TASK};return config;}

参考站点

​​​​​​​

  • OpenHarmony / startup_bootstrap_lite
  • HarmonyOS Device > 文档 > 指南 > 基础能力: bootstrap服务启动组件
  • 启动恢复子系统.md
  • 轻量带屏解决方案之恒玄芯片移植案例

小结

本文介绍了startup子系统之bootstrap_lite服务启动引导部件的移植适配案例及原理。因为时间关系,仓促写作,或能力限制,若有失误之处,请各位读者多多指正。感谢阅读,有什么问题,请留言。

点击关注,第一时间了解华为云新鲜技术~​​​​​​​

OpenHarmony移植案例与原理:如何适配服务启动引导部件bootstrap_lite相关推荐

  1. OpenHarmony移植案例:如何适配服务启动引导部件bootstrap_lite

    摘要:本文介绍了startup子系统之bootstrap_lite服务启动引导部件的移植适配案例及原理. 本文分享自华为云社区<OpenHarmony移植案例与原理 - startup子系统之b ...

  2. OpenHarmony移植:XTS子系统之应用兼容性测试套件

    本文分享自华为云社区<移植案例与原理 - XTS子系统之应用兼容性测试套件>,作者:zhushy. XTS(X Test Suite)子系统是OpenHarmony生态认证测试套件的集合, ...

  3. Netflix Eureka 简介、架构原理、及服务发现

    目录 Netflix Eureka 简介 spring-cloud-netflix 简介 Eureka 原理 服务发现 客户端发现模式 服务端发现模式 服务注册表 自注册方式 第三方注册模式 总结 N ...

  4. pc 页面在移动端怎么获取放大倍数、_百度移动搜索开放适配服务的3种方法

    为了提升用户的移动搜索结果浏览体验,百度移动搜索对具有对应手机站的PC站提供"开放适配"服务. 如果您同时拥有PC站和手机站,且二者能够在内容上对应,您可向百度"提交&q ...

  5. android 百度移动搜索 url 参数,百度移动搜索开放适配服务的3种方法

    为了提升用户的移动搜索结果浏览体验,百度移动搜索对具有对应手机站的PC站提供"开放适配"服务. 如果您同时拥有PC站和手机站,且二者能够在内容上对应,您可向百度"提交&q ...

  6. 安卓案例:绑定和解绑服务

    安卓案例:绑定和解绑服务 一.运行效果 二.实现步骤 1.创建安卓应用BindUnbindService

  7. android 绑定服务 解绑服务,安卓案例:绑定和解绑服务

    安卓案例:绑定和解绑服务 一.运行效果 二.实现步骤 1.创建安卓应用BindUnbindService 2.准备背景图片background.jpg,放到mipmap目录里 3.布局资源文件acti ...

  8. Linux系统移植:menuconfig 原理分析

    文章目录 Linux系统移植:menuconfig 原理分析 一.make menuconfig 执行过程 二.Kconfig 语法 2.1 mainmenu 菜单 2.2 调用其他目录 Kconfi ...

  9. OpenHarmony恢复启动子系统init进程之服务启动

    首先执行job,如果开发者的进程在启动之前需要首先执行一些操作(例如创建文件夹),可以把操作放到pre-init中先执行.一般pre-init阶段主要是为后面启动服务做准备的,比如挂载目录,设置权限, ...

最新文章

  1. 正确理解hibernate的inverse属性
  2. 使用dbutils对表进行增删改查
  3. 核心频率个加速频率_仅少数Ryzen 9 3900X能摸到官方最大加速频率,其他处理器也有这问题...
  4. 2017中国开源年会(COSCon'17) 报名正式开启
  5. python gil锁存在的意义_关于python的GIL全局解释器锁的简单理解
  6. 嵌入式常见笔试题总结(5)
  7. LeetCode算法入门- Search Insert Position -day19
  8. 森林病虫防治系统 (十)
  9. [Unity] Canvas 设置为 Screen Space - Camera 时,UI 被场景物体遮挡的解决办法:设置 Canvas 的 Plane Distance 为一个较小的数
  10. java递归 优点缺点_java编程之递归算法总结
  11. 特斯拉、华为等竞争升级,国产芯片实现突破,自动驾驶过去这一年
  12. 深度学习——CNN、RNN、DNN汇总
  13. STL是什么(STL简介)
  14. 01-Mybatis持久层框架快速入门(环境搭建、xml配置文件、注解)
  15. excel 实现随机抽取
  16. 矩阵的符号用计算机怎么打,在Word文档中怎么打出矩阵的符号?
  17. html倒计时面自动跳转,小代码   html 自己网页倒计时跳转
  18. 计算机中b代表的含义是什么意思,表示文件大小的MB,KB,B等是什么意思?
  19. py6_Python 内置函数/普通自定义函数 及参数和返回值
  20. CAD碎片化学习教程 @3. 设置当前字体

热门文章

  1. opensource项目_推出“什么是开放式教育?” Opensource.com上的资源
  2. 图书馆数字化库存管理_将公共领域中的任何图书数字化
  3. 开源社区_建立一个开源社区
  4. SVG基础知识 Adobe Illustrator绘制SVG
  5. java连续mysql_java+mysql,频繁连接报错
  6. ROS笔记(28) Setup Assistant
  7. python爬虫可以用acada_python爬虫从入门到放弃(四)之 Requests库的基本使用(转)...
  8. opencv mat赋值_opencv-python 关于位操作
  9. 小程序 图片上传php后台,微信小程序图片选择、上传到服务器、预览(PHP)实现实例...
  10. 您已到达6300千米深度,速度8 公里/秒,正在穿过地心 [置顶]