当前位置: 首页 > news >正文

Android OpenGL库加载过程源码分析

Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形;OpenGL源码位于:

frameworks/native/opengl


frameworks/base/opengl

本文简单介绍OpenGL库的加载过程。OpenGL以动态库的方式提供,因此在使用OpenGL的接口函数绘图前,需要加载OpenGL库,并得到接口函数指针。函数EGLBoolean egl_init_drivers()就是负责OpenGL库的加载。

[cpp] view plaincopy
  1. EGLBoolean egl_init_drivers() {  
  2.     EGLBoolean res;  
  3.     pthread_mutex_lock(&sInitDriverMutex);  
  4.     res = egl_init_drivers_locked();  
  5.     pthread_mutex_unlock(&sInitDriverMutex);  
  6.     return res;  
  7. }  
为保证多线程访问的安全性,使用线程锁来放完另一个接口函数egl_init_drivers_locked

[cpp] view plaincopy
  1. static EGLBoolean egl_init_drivers_locked() {  
  2.     if (sEarlyInitState) {  
  3.         // initialized by static ctor. should be set here.  
  4.         return EGL_FALSE;  
  5.     }  
  6.     // 得到Loader对象实例  
  7.     Loader& loader(Loader::getInstance());  
  8.     //加载EGL库  
  9.     egl_connection_t* cnx = &gEGLImpl;  
  10.     if (cnx->dso == 0) {  
  11.         cnx->hooks[egl_connection_t::GLESv1_INDEX] =&gHooks[egl_connection_t::GLESv1_INDEX];  
  12.         cnx->hooks[egl_connection_t::GLESv2_INDEX] =&gHooks[egl_connection_t::GLESv2_INDEX];  
  13.         cnx->dso = loader.open(cnx);  
  14.     }  
  15.     return cnx->dso ? EGL_TRUE : EGL_FALSE;  
  16. }  
函数首先定义指针cnx指向全局变量gEGLImpl,并且将cnx的域hooks指向gHooks,最后通过loader对象的open函数打开EGL动态库,因此最后从EGL库中加载的接口函数指针都保存到了变量gEGLImpl和gHooks中了。

frameworks\native\opengl\libs\EGL\Loader.cpp

[cpp] view plaincopy
  1. Loader::Loader()  
  2. {  
  3.     char line[256];  
  4.     char tag[256];  
  5.     /* Special case for GLES emulation 针对模拟器处理*/  
  6.     if (checkGlesEmulationStatus() == 0) {  
  7.         ALOGD("Emulator without GPU support detected. ""Fallback to software renderer.");  
  8.         mDriverTag.setTo("android");  
  9.         return;  
  10.     }  
  11.     /*打开/system/lib/egl/egl.cfg文件 */  
  12.     FILE* cfg = fopen("/system/lib/egl/egl.cfg""r");  
  13.     if (cfg == NULL) {  
  14.         //如果打开失败,mDriverTag ="android"  
  15.         ALOGD("egl.cfg not found, using default config");  
  16.         mDriverTag.setTo("android");  
  17.     } else {//否则读取文件内容  
  18.         while (fgets(line, 256, cfg)) {  
  19.             int dpy, impl;  
  20.             if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) {  
  21.                 // /system/lib/egl/egl.cfg文件内容为:0 1 mali  
  22.                 if (tag != String8("android")) {  
  23.                     mDriverTag = tag; //mDriverTag = "mali"  
  24.                 }  
  25.             }  
  26.         }  
  27.         fclose(cfg);  
  28.     }  
  29. }  
如果/system/lib/egl/egl.cfg文件不存在,则默认配置为0 0 android,否则就读取/system/lib/egl/egl.cfg文件内容,内容定义如下:


因此变量dpy=0, impl = 1, tag = mali

[cpp] view plaincopy
  1. void* Loader::open(egl_connection_t* cnx)  
  2. {  
  3.     void* dso;  
  4.     driver_t* hnd = 0;  
  5.     char const* tag = mDriverTag.string();//tag="mali"  
  6.     if (tag) {  
  7.         //加载GLES库函数  
  8.         dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2);  
  9.         if (dso) {  
  10.             hnd = new driver_t(dso);  
  11.         } else {//如果加载GLES库失败,则加载EGL,GLESv1_CM,GLESv2三个库  
  12.             dso = load_driver("EGL", tag, cnx, EGL);  
  13.             if (dso) {//只有EGL库加载成功,才加载GLESv1_CM,GLESv2库  
  14.                 hnd = new driver_t(dso);  
  15.                 // TODO: make this more automated  
  16.                 hnd->set(load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM );  
  17.                 hnd->set(load_driver("GLESv2",    tag, cnx, GLESv2),    GLESv2 );  
  18.             }  
  19.         }  
  20.     }  
  21.     return (void*)hnd;  
  22. }  
这个函数首先去加载/system/lib/egl/libGLES_mali.so,如果加载成功,那么对EGL | GLESv1_CM | GLESv2三个函数库,进行初始化。如果加载不成功,那么就加载libEGL_mali.so,libGLESv1_CM_mali.so,libGLESv2_mali.so这三个库,/system/lib/egl目录下的库如下:


由于libGLES_mali.so库不存在,因此最终加载的是libEGL_mali.so,libGLESv1_CM_mali.so,libGLESv2_mali.so三个库

[cpp] view plaincopy
  1. void *Loader::load_driver(const char* kind, const char *tag,  
  2.         egl_connection_t* cnx, uint32_t mask)  
  3. {  
  4.     char driver_absolute_path[PATH_MAX];  
  5.     const charconst search1 = "/vendor/lib/egl/lib%s_%s.so";  
  6.     const charconst search2 = "/system/lib/egl/lib%s_%s.so";  
  7.     //首先从/vendor/lib/egl/目录下查找对应的库,如果该目录下没有,则查找/system/lib/egl/目录  
  8.     snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag);  
  9.     //driver_absolute_path="/vendor/lib/egl/libEGL_mali.so"  
  10.     //driver_absolute_path="/vendor/lib/egl/libGLESv1_CM_mali.so"  
  11.     //driver_absolute_path="/vendor/lib/egl/libGLESv2_mali.so"  
  12.     //判断该路径下的动态库是否可以访问  
  13.     if (access(driver_absolute_path, R_OK)) {  
  14.         snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag);  
  15.         //driver_absolute_path="/system/lib/egl/libEGL_mali.so"  
  16.         //driver_absolute_path="/system/lib/egl/libGLESv1_CM_mali.so"  
  17.         //driver_absolute_path="/system/lib/egl/libGLESv2_mali.so"  
  18.         if (access(driver_absolute_path, R_OK)) {  
  19.             return 0;  
  20.         }  
  21.     }  
  22.     //打开so动态库到cnx->egl中  
  23.     void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);  
  24.     if (dso == 0) {  
  25.         const char* err = dlerror();  
  26.         ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");  
  27.         return 0;  
  28.     }  
  29.     ALOGD("loaded %s", driver_absolute_path);  
  30.     //从动态库中加载EGL函数库到cnx->egl中,EGL库函数定义在egl_names数组中  
  31.     if (mask & EGL) {  
  32.         //读取动态库中eglGetProcAddress函数指针  
  33.         getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");  
  34.         ALOGE_IF(!getProcAddress, "can't find eglGetProcAddress() in %s", driver_absolute_path);  
  35. #ifdef SYSTEMUI_PBSIZE_HACK  
  36. #warning "SYSTEMUI_PBSIZE_HACK enabled"  
  37.         /* 
  38.          * Here we adjust the PB size from its default value to 512KB which 
  39.          * is the minimum acceptable for the systemui process. 
  40.          */  
  41.         const char *cmdline = getProcessCmdline();  
  42.         if (strstr(cmdline, "systemui")) {  
  43.             void *imgegl = dlopen("/vendor/lib/libIMGegl.so", RTLD_LAZY);  
  44.             if (imgegl) {  
  45.                 unsigned int *PVRDefaultPBS =(unsigned int *)dlsym(imgegl, "PVRDefaultPBS");  
  46.                 if (PVRDefaultPBS) {  
  47.                     ALOGD("setting default PBS to 512KB, was %d KB", *PVRDefaultPBS / 1024);  
  48.                     *PVRDefaultPBS = 512*1024;  
  49.                 }  
  50.             }  
  51.         }  
  52. #endif  
  53.         egl_t* egl = &cnx->egl;  
  54.         __eglMustCastToProperFunctionPointerType* curr =  
  55.             (__eglMustCastToProperFunctionPointerType*)egl;  
  56.        /*定义指向egl_names数组的指针 
  57.         * char const * const egl_names[] = { 
  58.         *   #include "egl_entries.in" 
  59.         *   NULL 
  60.         * }; 
  61.         * 文件frameworks/native/opengl/libs/EGL/egl_entries.in声明了EGL库中的所有函数 
  62.         */  
  63.         char const * const * api = egl_names;  
  64.         //遍历数组egl_names,将数组中定义的函数指针保存到cnx->egl中  
  65.         while (*api) {  
  66.             char const * name = *api;  
  67.             //根据函数名称从动态库EGL中取得函数指针  
  68.             __eglMustCastToProperFunctionPointerType f =   
  69.                 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);  
  70.             if (f == NULL) {  
  71.                 //如果在动态库中查找不到该函数指针,则使用getProcAddress函数来获取  
  72.                 f = getProcAddress(name);  
  73.                 if (f == NULL) {//如果依然得不到该函数指针,则设置为0  
  74.                     f = (__eglMustCastToProperFunctionPointerType)0;  
  75.                 }  
  76.             }  
  77.             *curr++ = f;  
  78.             api++;  
  79.         }  
  80.     }  
  81.     //char const * const gl_names[] = {  
  82.     // #include "entries.in"  
  83.     // NULL  
  84.     //};  
  85.     //从动态库中加载GLESv1_CM函数库到cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl中,GLESv1_CM库函数定义在gl_names数组中  
  86.     if (mask & GLESv1_CM) {  
  87.         init_api(dso, gl_names,(__eglMustCastToProperFunctionPointerType*)  
  88.                 &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,  
  89.             getProcAddress);  
  90.     }  
  91.     //从动态库中加载GLESv2函数库到cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl中,GLESv2库函数定义在gl_names数组中  
  92.     if (mask & GLESv2) {  
  93.       init_api(dso, gl_names,(__eglMustCastToProperFunctionPointerType*)  
  94.                 &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,  
  95.             getProcAddress);  
  96.     }  
  97.     return dso;  
  98. }  
该函数就是从动态库libEGL_mali.so中查询egl_names数组,即frameworks/native/opengl/libs/EGL/egl_entries.in文件中声明的接口函数,及从动态库libGLESv1_CM_mali.so,libGLESv2_mali.so中查询gl_names数组,即frameworks/native/opengl/libs/entries.in文件中声明的接口函数,函数声明如下:


[cpp] view plaincopy
  1. void Loader::init_api(void* dso, char const * const * api,   
  2.         __eglMustCastToProperFunctionPointerType* curr,   
  3.         getProcAddressType getProcAddress)   
  4. {  
  5.     const ssize_t SIZE = 256;  
  6.     char scrap[SIZE];  
  7.     while (*api) {  
  8.         char const * name = *api;  
  9.         //根据函数名称从动态库中查找函数指针  
  10.         __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);  
  11.         if (f == NULL) {  
  12.             //使用eglGetProcAddress()得到指定函数指针  
  13.             f = getProcAddress(name);  
  14.         }  
  15.         if (f == NULL) {  
  16.             //将函数名称去掉后缀OES  
  17.             ssize_t index = ssize_t(strlen(name)) - 3;  
  18.             if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) {  
  19.                 strncpy(scrap, name, index);  
  20.                 scrap[index] = 0;  
  21.                 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);  
  22.             }  
  23.         }  
  24.         if (f == NULL) {  
  25.             //将函数名称增加后缀OES   
  26.             ssize_t index = ssize_t(strlen(name)) - 3;  
  27.             if (index>0 && strcmp(name+index, "OES")) {  
  28.                 snprintf(scrap, SIZE, "%sOES", name);  
  29.                 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);  
  30.             }  
  31.         }  
  32.         if (f == NULL) {  
  33.             f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;  
  34.             if (!strcmp(name, "glInsertEventMarkerEXT")) {  
  35.                 f = (__eglMustCastToProperFunctionPointerType)gl_noop;  
  36.             } else if (!strcmp(name, "glPushGroupMarkerEXT")) {  
  37.                 f = (__eglMustCastToProperFunctionPointerType)gl_noop;  
  38.             } else if (!strcmp(name, "glPopGroupMarkerEXT")) {  
  39.                 f = (__eglMustCastToProperFunctionPointerType)gl_noop;  
  40.             }  
  41.         }  
  42.         *curr++ = f;  
  43.         api++;  
  44.     }  
  45. }  
load_driver函数所做的工作:首先通过dlopen加载/system/lib/egl/libGLES_mali.so库,然后从/system/lib/egl/libGLES_mali.so库中提取 EGL的各个API函数的地址放到cnx->egl中,从libGLES_mali.so获取GLESv1_CM的API保存到cnx->hooks[GLESv1_INDEX]->gl中,从libGLES_mali.so获取GLESv1_CM的API保存到cnx->hooks[GLESv2_INDEX]->gl 。提取EGLAPI地址的方法是首先通过dlsym函数获得一个获取函数地址的函数eglGetProcAddress的地址,然后遍历EGL的API所在文件frameworks/base/opengl/libs/EGL/egl_entries.in。先通过dlsym获取各个API地址,如果返回NULL再利用eglGetProcAddress去获得,如果依旧为空就把函数地址赋值为0;提取GLESv1_CM和GLESv1_CM库中函数地址方法和提取EGL差不多,只是他们的函数文件保存在frameworks/base/opengl/libs/entries.in中。还有它们把函数地址复制给了cnx->hooks[GLESv1_INDEX]->gl和cnx->hooks[GLESv2_INDEX]->gl。

相关文章:

  • Android OpenGL 硬件加速.
  • onmeasure被调用了但是onlayout没有被调用
  • OpenMax在Android上的实现
  • Android多媒体开发【10】-- android中OpenMax的实现【1】整体框架
  • Android mediaPlayer学习笔记http://blog.sina.com.cn/s/articlelist_1937350040_0_2.html
  • Ubuntu mysql 开启远程访问
  • 网上比较好的机器学习技术文章
  • Android基本功:跨进程调用Services(AIDL Service)
  • android6.0源码分析之Camera API1.0框架简介
  • Linux驱动开发之Platform_device和Platform_driver
  • 一、MTK6580 平台 Camera 驱动整体框架
  • Android5.0 MTk Camera HAL层代码分析
  • Android 按键流程分析笔记
  • SurfaceView及Surface分析
  • MediaRecord 分析
  • Android5.0 Camera HAL3 源代码分析
  • Ubuntu下Docker安装
  • TensorFlow中的一个重要op---MatMul的实现(一)
  • TensorFlow中的一个重要ops---MatMul的实现(二)
  • Android 窗口的计算过程
  • 基于docker安装spark
  • Android 渲染过程分析
  • 神经网络中 BP 算法的原理与 PYTHON 实现源码解析
  • TensorFlow word2vec_basic 代码分析
  • ibatis执行过程
  • freeswitch 在redhat 7.0/centos 7.0上的编译安装
  • freeswitch 使用的问题
  • coredump分析
  • freeswitch.Session的实现过程
  • freeswitch lua/luarun的执行过程
  • freeswitch Session dtmf callback的触发过程
  • NAT穿越原理——STUN
  • MRCP UA的创建及SIP信令处理
  • (转)WebRTC手记之初探
  • libnice simple-example 分析
  • libsrtp rtpw 例子代码分析
  • WebRTC直播技术(二)-ICE/STUN/TURN
  • Android binder 设计思想
  • 在Java中使用堆排序求解TopK问题
  • SIP穿越NAT的rport机制
  • SIP的路由机制
  • FreeSWITCH代码分析(一)
  • FreeSWITCH代码分析(二)
  • FreeSwitch全局配置文件处理流程
  • 媒体与媒体处理
  • APR内存池的学习与实现
  • B2B在FreeSwitch中的呼叫流程
  • Freeswitch常见问题解决办法
  • switch_core_session_run函数处理流程分析
  • Socket:读写处理及连接断开的检测
  • 解决xlite和freeswitch通话没有语音的问题
  • 路由有关的SIP消息头的简单小结
  • open source project 之 callback 函数
  • Asterisk 拨号计划之匹配规则和优先级详解
  • Asterisk鉴权算法
  • freeswitch 内核研究笔记1
  • freeswitch 媒体早期协商模式分析
  • CentOS 6.5 64位系统OpenSips 1.11部属配置
  • P2P NAT穿越
  • Chapter 3. Locking system
  • opensips架构图
  • freeswitch配置文件目录结构
  • 路由数据结构图
  • struct sock、struct inet_sock、strct inet_connection_sock和struct tcp_sock数据结构
  • 抖动和延迟之间的区别
  • 分布式知识学习计划
  • 高性能高并发服务的瓶颈及突破思路
  • 白话TCP运输控制(一) -- TCP的拥塞控制
  • 白话TCP运输控制(二) -- TCP的流量控制
  • 白话讲解Nagle算法和延迟确认
  • Linux I/O多路复用——select函数中的算法
  • 使用ffmpeg将实时流保存为AVI
  • Hash表为什么要扩容?
  • 记一次阿里云服务器中毒事件
  • 1.Class文件
  • ASN.1学习
  • 领域驱动设计(一)
  • 系统分析教辅资料
  • (一)问题领域
  • (二)涉众分析
  • 使用双栈(参数栈、状态栈)实现树的前、中、后序遍历
  • Array题型:双指针Two Pointers套路
  • FPGA:硬件描述语言简介
  • 喜格临床实验室设计方案
  • 生物安全实验室建设布局SICOLAB
  • 医院实验室建设基础配置情况SICOLAB
  • 江门医院实验室施工材料选择
  • 医学实验室建设基本布局科普
  • 佛山水质监测实验室布局
  • 广州水质检测实验室装修要求
  • 肇庆水质检测实验室实施方案分享
  • 水质检测实验室仪器大全
  • 河源云浮环境监测实验室工作区设计
  • 环境监测实验室监测知识科普
  • 江门环境监测实验室通风工程设计
  • 广州环境监测站配电工程施工方案
  • 江门环境监测站建设施工注意事项
  • 关于广州环境监测站平面设计原则
  • 环境监测实验室建设准备前构想
  • 韶关企业化验室建设布置方案
  • 梅州企业化验室建设规划细节
  • 揭阳企业化验室建设通风要求
  • 广东企业化验室建设布局事项
  • 潮州企业化验室建设可实行方案
  • 湛江化工厂实验室电气系统设计
  • 清远化工厂实验室初步设计流程
  • 探讨化工厂实验室组规划设计项目
  • 东莞化工厂实验室设计基本程序
  • 深圳新材料实验室建设干货
  • 清远新材料实验室建设布局分析
  • 潮州新材料实验室建设细节
  • 广州电子厂实验室建设施工装修
  • 广州药企检测中心实验室建设指南
  • 汕头食品检测中心实验室施工计划分析
  • 潮州质量检测中心实验室建设|装修方案
  • 食品检测中心实验室洁净计划
  • 怀化检测中心实验室施工结构规划
  • 广州研发实验室建设研究项目
  • 湛江研发实验室装修设计图|布局
  • 南宁药品研发实验室设计要点
  • 钦州研发中心实验室建设准备构想
  • 广州临床实验室设计人员通道布局
  • 揭阳临床实验室建设规范
  • 崇左临床实验室设计布置
  • 上饶临床实验室改造规划要点
  • 吉安临床实验室建设之构成区域
  • 韶关生物实验室建设平面布局
  • 清远生物实验室建设设计七大要点
  • 二级生物实验室防护设计基本要求
  • 九江生物安全实验室建设施工要求
  • 佛山医学实验室建设要求:空间布局
  • 漳州医学实验室建设储存设施
  • 泉州医学实验室设计通风系统建设
  • 医学实验室装修材料要求那些点
  • 医学实验室筹建前的构思
  • 佛山第三方检测机构实验室设计
  • 汕头第三方检测实验室建设之解决方案
  • 湛江第三方检测实验室建设4大事项
  • 参考第三方检测实验室装修布局大全
  • 福州第三方检测实验室设计需求
  • 佛山法医鉴定中心实验室施工方案
  • 法医检验鉴定中心实验室建设思路
  • 三明法医鉴定中心实验室筹建与装修
  • 广州DNA实验室功能区大改造
  • 江门DNA实验室规化方案介绍报告
  • 出入境检验实验室功能区设计大纲
  • 汕头出入境检验实验室建设案例
  • 泉州出入境检验实验室改造设计
  • 新余动物实验室建设注意事项
  • 上饶动物实验室建设大全
  • 佛山植物组织培养实验室必备设备
  • 梅州组织培养实验室之室内布局建设
  • 揭阳植物组培实验室组成设计汇总
  • 柳州植物组织培养实验室建设施工意义
  • 植物组织培养实验室之洗涤室建设
  • 佛山细胞实验室建设标准要求
  • 汕尾细胞实验室建设攻略
  • 潮州细胞实验室改造规划
  • 永州细胞实验室装修计划
  • 柳州细胞实验室局部建设规划
  • 惠州细胞房建设分析
  • 三明细胞房建设可行性分析
  • 崇左细胞房装修建设方案
  • 湘潭细胞房建设工程计划
  • 惠州无菌洁净室建设设计指南
  • 南平无菌洁净室建设方案总结
  • 惠州无菌实验室装修要点
  • 河源无菌实验室建设室内设计
  • 厦门无菌实验室建设施工技术
  • 上饶无菌实验室设计与施工事项
  • 深圳污水处理厂实验室建设手册
  • 广州核酸检测实验室建设关键点
  • 污水处理厂实验室之化验室通风设计
  • 南平污水处理厂实验室建设之设备科普
  • 龙岩核酸检测实验室之PCR建设
  • 佛山疾控中心实验室建设计划
  • 韶关疾病预防控制中心建设要点
  • 揭阳疾控中心实验室装修事项
  • 北海疾控中心实验室建设浅析
  • 梅州医院检验科建设标准:设计规范
  • 深圳实验室装修设计攻略
  • 深圳实验室装修材料注意事项
  • 深圳实验室装修排风合理设计
  • 深圳实验室装修材料考虑大全
  • 深圳实验室装修可施行方案
  • 深圳实验室装修步骤分析
  • 深圳实验室装修改造工程参考
  • 深圳实验室装修室内总结方案
  • 深圳,200平实验室装修布置
  • 实验室装修工程设计内容有哪些
  • 深圳实验室设计装修布置图
  • 深圳实验室设计参考事宜
  • 深圳实验室装修必看4大设想
  • 深圳实验室装修准备工作
  • 深圳实验室装修建设分析
  • 深圳实验室装修:建设借鉴
  • 深圳实验室设计合理化事项
  • 深圳实验室设计理念风格浅析
  • 深圳实验室设计,现场勘查事项
  • 深圳实验室设计新建思路
  • 广州医院病理科设计误区
  • 揭阳医院输血科建设考虑
  • 潮州PCR实验室建设运用范围
  • 肇庆PCR实验室建设消毒事项汇总
  • 河源医院检验科设计:空调设计方案
  • 解锁医院输血科建设之服务范围
  • 深圳第三方检测实验室建设发展分析
  • 广州干细胞实验室改造:洁净空调选择
  • 韶关干细胞实验室设计:要求汇总
  • 中山医院检验科室改造须知要求
  • 肇庆医院病理科室建设心得
  • 深圳实验室装修报价
  • 深圳SPF级动物实验室建设建议
  • 汕头ABSL-2实验室建设:内部规划
  • 汕头肥料检测实验室建设内容分析
  • 汕头GLP实验室建设知识概述
  • 汕头恒温恒湿实验室建设注意事项
  • 汕头实验台材质知识归纳
  • 惠州实验室装修:地面材料选用
  • 惠州实验室设计要考虑因素
  • 惠州实验室装修:地板工艺知识整理
  • 惠州实验室建设选址、勘察事项
  • 惠州中学生物学实验室建设指南
  • 惠州生物实验室建设宝典
  • 惠州药检实验室建设分区说明
  • 惠州生物实验室设计细节,建设大纲
  • 惠州口罩厂无尘车间建设选址概述
  • 惠州环境监测实验室建设面积要求
  • 惠州教学实验室建设与管理
  • 惠州放射性实验室建设:人员管理
  • 惠州GLP实验室建设:工作人员要求
  • 珠海塑料检测实验室建设:仪器配置说明
  • 珠海电池负极材料实验室建设之知识总结
  • 珠海化学分析实验室建设思路
  • 珠海动物实验室建设资料概述
  • 珠海污水处理厂实验室建设总结
  • 珠海植物组培实验室建设布局概述
  • 珠海分子病理实验室建设方案解析
  • 珠海食品理化检测实验室建设方案
  • 珠海废气处理方案大曝光
  • 珠海实验室通风系统建设安装说明
  • 珠海净化实验室建设细节概述
  • 涂料检测实验室建设背后事项
  • 梅州护理实验室建设:设备配置
  • 梅州细胞室建设思路
  • 梅州兽医实验室建设细节概述
  • 梅州临床实验室设计心得整理
  • 梅州发热门诊科室实验室建设内部规划分享
  • 梅州化验室建设细节整理分享
  • 梅州放射性实验室建设布局要求分析
  • 梅州通风柜设计安装那些事
  • 梅州锂电池生产车间建设一囊概括
  • 阳江锂电池实验室建设:除湿方式分享
  • 汽车零部件检测实验室建设考虑概述
  • 潮州疾控中心实验室设计要点整理
  • 潮州GMP车间建设选址科普
  • 潮州化验室建设布置要点总结SICOLAB
  • 潮州二恶英实验室建设布局概述
  • 潮州化妆品检测实验室建设指南
  • 潮州兽医实验室建设构想分析
  • 潮州土壤检测实验室建设内容曝光
  • 潮州恒温恒湿实验室建设要点说明
  • 湛江肥料检测实验室建设浅度解析
  • 湛江实验室建设布局科普
  • 湛江植物细胞实验室建设布局大盘点
  • 湛江SPF级动物实验室建设知识大全
  • 湛江化妆品实验室装修用材归纳
  • 湛江疾控中心实验室设计:物流走向
  • 湛江植物组培实验室建设考虑分析
  • 湛江油品检测实验室建设要点分析
  • 阳江实验室装修工艺大盘点
  • 阳江石油化工实验室建设说明归纳
  • 阳江口罩洁净厂房设计布置总结
  • 阳江医院实验室建设场地要求分析
  • 阳江兽医实验室建设考虑事项说明
  • 江门药检实验室建设知识整理
  • 江门生物安全实验室建设方案总结
  • 江门疾控中心实验室建设划分用房浅析
  • 江门化妆品实验室建设知识归纳
  • 江门GLP实验室建设事项分析
  • 韶关实验室设计要点汇总
  • 韶关实验室设计事项分享
  • 韶关干细胞制备实验室建设基本要求解析
  • 肇庆植物组培实验室建设灭菌说明
  • 肇庆实验室通风系统知识大盘点
  • 肇庆实验室家具安装步骤说明
  • 肇庆GLP实验室建设方案归纳
  • 茂名实验室设计大纲分析
  • 茂名实验室建设内容分析
  • 茂名兽医实验室建设细节化事项说明
  • 茂名植物组培实验室建设方向总结
  • 元宇宙3D设计系统【构思与展望】
  • 茂名HIV实验室建设:环境要求分享
  • 茂名水质检测实验室建设知识科普
  • 河源实验室建设合理化细节探讨
  • 河源实验室建设:选址事项分享
  • 河源微生物实验室建设合理化布置整理
  • 河源医院病理科实验室设计细节分析
  • 河源水质检测实验室建设方案曝光
  • 河源食品安全检测实验室建设细节剖析
  • 揭阳生物制药实验室建设方案归纳
  • 揭阳水质检测实验室建设选址须知事项
  • 河源环境监测站实验室建设布局考虑归纳
  • 清远高校实验室建设要点解析
  • 海南煤炭检测实验室设计要点大曝光
  • 海南疾控中心实验室建设工艺说明
  • 海南化学分析实验室设计工艺归纳
  • 海南饲料肥料检测实验室建设规划方案
  • 海南纺织品检测实验室设计工艺概述
  • 海南PCR实验室建设须知识整理
  • 海南涂料检测实验室建设细节分享
  • 海南油品检测实验室建设要点考虑
  • 海南环境监测站实验室建设:排水系统说明
  • 儋州门诊临床实验室建设原则归纳
  • 福州口罩洁净厂房建设知识概述
  • 福州核酸检测实验室建设须知事项探讨
  • 福州汽车零部件实验建设工作内容整理SICOLAB
  • 梅州二恶英实验室建设注意事项分享
  • 梅州植物组培实验室建设资料整理
  • 福州化工实验室建设注意隐患分析
  • 梅州P2实验室建设方案阐述
  • 三沙日用品检测实验室建设构想分析
  • 三沙纺织品检测实验室建设大纲
  • 三沙GMP车间设计要点探讨
  • 三沙煤炭检测实验室建设内容浅析
  • 三沙第三方检测实验室建设要点科普
  • 儋州清洁级动物实验室建设细节说明
  • 常德植物组培实验室建设一囊概括
  • 郴州干细胞实验室建设:强电系统解析
  • 永州二恶英实验室建设细节查看
  • 永州分析实验室建设选址概述
  • 永州植物细胞实验室建设布局方案
  • 永州水质检测实验室建设:家具说明
  • 永州清洁级动物实验室建设选址注意事项
  • 永州出入境检验实验室建设那些事
  • 永州动力电池实验室建设合理布局方案
  • 娄底妆品实验室建设规划构思
  • 娄底农产品检验实验室建设指南盘点
  • 娄底污水处理厂实验室建设管理
  • 娄底干细胞制备实验室建设须知要求
  • 娄底石油化工实验设计、建设规划概述
  • 娄底植物细胞实验室建设基本组成要点
  • 娄底疾控中心实验室设计理念说明
  • 娄底高校实验室建设材料考虑
  • 娄底无菌室建设须知事项汇总
  • 娄底医药工业洁净厂建设基本要点概述
  • 娄底锂电池隔膜研发实验室建设资料科普
  • 娄底可靠性检测实验室建设知识概述
  • 鹰潭实验室建设细节说明
  • 鹰潭实验室设计要素盘点
  • 鹰潭恒温恒湿实验室设计方案总结
  • 鹰潭病理实验室建设、筹建考虑因素
  • 兽医实验室人员建设:储备解析
  • 岑溪SPF级动物实验室设计平面布局规划查看
  • 岑溪洁净实验室设计布局规划总结
  • 岑溪化验室设计布置原则探讨
  • 岑溪农产品检验实验室建设细节概述
  • 锂电池实验室设计SICOLAB
  • 低露点实验室设计SICOLAB
  • 锂电动力实验室设计方案|SICOLAB
  • 锂电池净化厂房低湿车间装修SICOLAB
  • 锂动力研究院揭牌仪式 | SICOLAB喜格
  • 医院洁净手术室设计装修要点SICOLAB
  • 实验室电气设计有哪些要注意的SICOLAB
  • 恒温恒湿实验室(房)建设、设计SICOLAB
  • 化工厂理化实验室设计、化工厂实验室装修SICOLAB
  • 高校实验室设计SICOLAB高校实验室装修
  • 化学实验室装修验收标准SICOLAB
  • 计算机组成原理 | 第一章:概论
  • 【流媒体】Linux系统搭建Red5服务器
  • 【流媒体】Red5流媒体服务器开发总结
  • 【流媒体】Red5文件结构简介
  • 《剑指offer》源码笔记
  • 【LeetCode】max-points-on-a-line一条线上最多点
  • 【深度学习】---行人检测应用
  • 【LeetCode】binary-tree-postorder-traversal
  • 【LeetCode】recorder-list
  • 【LeetCode】linked-list-cycle-ii
  • 图片测距实验
  • 【LeetCode】palindrome-partitioning iii
  • 【深度学习】---行人检测应用二
  • 【LeetCode】triangle pascals-triangle iii
  • 【LeetCode】populating-next-roght-pointers-in-each-node iii
  • 【LeetCode】path-sum iii
  • 【LeetCode】convert-sorted-link-to-binary-search-tree convert-sorted-array-to-binary-search-tree
  • 【LeetCode】binary-tree-level-order-traversal iiizigzag
  • 【嵌入式开发】DM6467T环境搭建
  • 【LeetCode】partition-list
  • 【转】校园招聘面试-操作系统知识总结
  • verilog实现计算器设计
  • 【转】操作系统常见面试题总结
  • 【转】string和stringstream用法总结
  • 【转】 字符串的全排列和组合算法
  • faster-rcnn在win10+cuda8.0+1080ti+vs2013+matlab2015b下的配置 疑难问题解答
  • KCF环境搭建相关问题
  • LeetCode[200]岛屿数量
  • PAT——1039 到底买不买
  • javacv实现rtsp转rtmp(支持拉一路推多路)
  • javacv拉流保存为视频
  • gb28181简单实现sip信令服务器(java版基于springboot):一、netty创建udp服务器
  • gb28181简单实现sip信令服务器(java版基于springboot):四、sip摄像头心跳保活、推流(tcp/udp)和结束推流
  • gb28181简单解包rtp ps流,推出rtmp(java版基于springboot):五、基于netty创建接收流的udp和tcp服务器
  • gb28181简单解包rtp ps流,推出rtmp(java版基于springboot):六、解包rtp ps流,推出rtmp
  • 反向放大电路并联电容与积分电路并联电阻的区别?
  • 如何安装最新的Debugging Tools for Windows
  • 解决:QT VS tools The following error occured
  • QT安装完毕之后,设置系统环境变量!
  • dockwidget 设置里面的控件填满整个dockwidget的方法
  • 2020-04-19
  • java范型
  • JAVA泛型2
  • JAVA泛型3
  • JAVA泛型%d %f %s
  • idea 默认maven设置
  • idea exclude Dependencies以后如何恢复
  • SpringBoot 2.2.6 不能访问静态资源,以及不能访问默认主页index.html的解决
  • Eclipe source not found的解决办法
  • eclipse中使用“Copy Qualified Name”复制全类名
  • import javax.validation.constraints.Past 包找不到的解决办法
  • wx:key 加不加大括号的问题
  • throwable和Exception的区别
  • 为什么不能直接new Map
  • jeecgboot 同步数据库报错 NoClassDefFoundError: javax/xml/bind/JAXBException
  • jeecgboot上传时的token验证:记录一个蠢坑
  • Cannot resolve plugin org.apache.maven.plugins:maven-site-plugin:3.3
  • window.location对象详解
  • JSP(<%...%>)
  • springboot swagger 不显示接口的问题
  • 解决办法:The POM for org.eclipse.m2e:lifecycle-mapping:jar:1.0.0 is missing
  • 解决eclipse代码的自动提示问题
  • 字符串转化为 List 集合
  • 记录uniapp中view按下后改变背景样色样式的一个坑
  • uniapp 使用app.vue中globalData全局变量的方法 绑定到页面 实时刷新
  • uniapp swiper-item 禁止滑动的方法
  • css一个小问题:为什么写样式的时候有时要用点连着,有时要空格分开?
  • jeecgboot 2.3新建module无效的坑
  • 记录一个iview i-swtich开关组件属性true-value=“0“ false-value=“-1“的问题
  • vue iview中向后端请求springboot后端数据searchForm表单中字段显示undefined的问题。
  • iview 表单验证
  • mybatis ew.sqlSegment @Param(“ew“)
  • mybatis plus 自定义sql查询遇到的坑<where> ${ew.sqlSegment} </where>
  • mybatis plus 添加字段后 字段读取值为null的一个坑
  • 2020-11-7 iview input number 数字验证最新坑
  • DatePicker 编辑时如果无修改动作 时间格式为中国标准时间
  • uniapp中uni.login提示the code is a mock one
  • @Transactional找不到以及Consider defining a bean of type ‘XXX.Dao‘ in your configuration的问题
  • StringUtils.contains()所在的包 pom依赖
  • C++ 多线程编程实战11:std::async
  • eclipse常见错误总结(实习期总结)
  • 快速获得一个文件夹的所有文件的名称技巧
  • 2018.10.18-----2018-11-2小结
  • Timer--预约单
  • Spring boot学习日记 -HelloWorld
  • 预约单
  • @Transactional注解导致service注入失败及解决办法
  • mysql有则更新 没有则插入
  • 有赞预售订单初版
  • @Transactional注解乱用引发的异常
  • springcloud 小结
  • 字符串直接转list集合方法
  • 转载:springboot自定义注解
  • 自定义注解经验
  • socket转http 简单的认证功能
  • Mysql分区表 sql优化
  • 分布式任务调度平台xxl-job demo
  • 重构决定!
  • xxl-job demo
  • redis 缓存击穿事件记录
  • 启动监听ApplicationListener与CommandLineRunner
  • es demo
  • mysql数据同步到es
  • ELK日志监控平台搭建记录
  • macOS Ventura 13.2 (22D49) Boot ISO 原版可引导镜像
  • Excel导出用IE浏览器文件名乱码解决
  • 将逗号分隔的字符串转换为List
  • dozer
  • java导出excel模板,java设置excel模板只能输入时间,数字,excel输入限制
  • java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName
  • Jackson使用手册 https://www.cnblogs.com/miaosj/p/10936451.html
  • 修改菜单排序
  • 根据儿子找爹
  • 字符串转list
  • java导出word 拿来直接用
  • mybatis设置添加新对象数据的时候id使用uuid类型自动写入数据库
  • java 操作ftp服务器
  • 一个list 分批次
  • Gson转换实体
  • 获取指定月份的数据 当月初不是星期天 月末不是星期六的时候,增加前后一周的数据
  • sharding-jdbc Data sources cannot be empty.
  • window下启动多个jar
  • 自定义orm,快速完成增删改查
  • object 反射实体类set属性的值
  • 在项目中用到的转换
  • 写了一个上传excel到解析到数据库的通用类,只能作为参考
  • 反射获取service
  • Java导出浏览器下载
  • 时间公共类
  • sun.security.validator.ValidatorException
  • nacos多个环境下配置文件
  • resttemplate 跳过安全验证