With the continuous increase of App functions, the code size of Native layer is also expanding rapidly. In order to make the code structure of Native layer clear, independent SO library will be constructed according to modules respectively. A SO library of JNI layer will be used to reference other functions to realize specific functions to realize SO library. To indirectly call the function to implement the SO library.
Dependencies are formed between so libraries by referring to header files and specifying shared library dependencies at runtime. But there are some problems.
- We often use third-party so library, if a single library may be ok, if multiple third-party SO library files, loading at the same time may cause conflicts, such as Tencent YSDK and BUGLY.
- When loading THE SO library of JNI layer, even if some functions implemented in the SO library are not called this time, as long as the SO library is declared by the SO library of JNI layer as a shared library that needs to be relied on at runtime, it needs to be loaded together with the SO library of JNI layer, which virtually increases the resident memory of Native layer.
At this time, it is necessary to load the SO library dynamically in the Native layer directly, and realize the SO library by the dynamic loading function of the SO library in the JNI layer. As shown in the figure below, there will be a unified interface so libraries, defined in the library good not easily modify the interface functions, the caller you just need to know these interfaces, don’t need to rely on the header file can call these functions, so that the caller and so there is no direct dependence between the concrete work can give a unified interface so library is complete, It executes functions in the functional SO library through dynamic calls.
So library dynamic loading implementation
In the C/C++ code environment of Native layer, the dynamic loading of so library is realized by using dlopen(), DLsym () and dlclose(). Dlopen () opens a DLL and returns a DLL handle. Dlsym () returns the symbolic address of the dynamic link library based on its handle and symbol name. This address can be a variable pointer or a function pointer. Dlclose () closes the DLL handle and decrement the library reference count by one. When the library reference count reaches zero, the library will be uninstalled.
Generally, C/C++ is used to realize the dynamic loading process of so library as follows:
- First call
dlopen()
Function. The parameters required for this function are the path to the so library and the loading mode. There are two loading modes commonly used:RTLD_NOW
Parse all undefined symbols before returning. If not,dlopen()
returnNULL
;RTLD_LAZY
Only the currently needed symbols are parsed (only for functions; variable definitions are still fully parsed). Obviously, for dynamic loading, the loader only needs to know the function and variable definitions needed by the currently loaded so library, so the latter is selected here. If the call succeeds, it returns a handle to the so library; - This is called after the so library handle is obtained in the previous step
dlsym()
Function, passing in the so library handle and the required function or variable name, and returning the corresponding function or variable pointer; The loader can then use the returned pointer to call the functions and data structures defined in the loaded SO library; - When the call to the so library ends, call
dlclose()
Function to close uninstall so library; - Can be called if there is an error in opening or closing the so library, or in getting a pointer to an operation object in the so library
dlerror()
The function retrieves the specific cause of the error.
Code implementation
For example, there is a function int test_open(int port) in the hardware functionality so library. How do I end up calling this method?
//1
typedef int (*Func_test_open)(int);
int open(int port){
// get the so handle
void *handle = dlopen("libtest.so",RTLD_LAZY);
if(! handle ){ LOGE("%s",dlerror());
return - 1;
}
//3
Func_test_open func_test_open = (Func_test_open) dlsym (handle,"test_open");
if(! func_test_open){ LOGE("%s",dlerror());
dlclose(handle);
return - 1;
}
// call the function
int ret = func_test_open(8080);
// close so
dlclose(handle);
return ret;
}
Copy the code
So the JNI layer only needs to call the open(int port) method to call the test_open(int port) function in the hardware function so library
conclusion
At the beginning, when the scheme of dynamic loading SO library is used, the performance problem will be worried, but in the actual measurement, compared with the direct dependence, there is no obvious impact on the performance. So library and JNI layer are completely decoupled, with a high degree of independent cohesion. Meanwhile, it supports dynamic loading and unloading of SO library, which also reduces resident memory of Native layer to a certain extent.