什么是动态库、静态库
库是共享程序代码的方式,一般分为静态库和动态库
* 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝 * 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。
Linux、Android、Unix
静态库为.a文件,动态库为.so文件
Windows
静态库为.lib文件,动态库为.dll文件
iOS
* 静态库分为两种:.a和.framework * 动态库也有两种:.dylib和.framework * 系统的.framework是动态库,我们自己建立的.framework是静态库 * .a + .h + sourceFile = .framework。
Unity中动态库的使用
动态库是在运行时期间被使用到,如果dll文件是C#代码组成的,那就直接把dll文件直接放到plugin目录下即可,在代码中就可以直接使用dll文件中所包含的类了。另一种情况是dll文件是由C/C++组成,这种情况想要使用dll文件中的函数就需要用到DllImport了。
DllImport
- C/C++导出代码
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API SetTextureFromUnity(void* textureHandle, int w, int h)
{
// A script calls this at initialization time; just remember the texture pointer here.
// Will update texture pixels each frame from the plugin rendering event (texture update
// needs to happen on the rendering thread).
g_TextureHandle = textureHandle;
g_TextureWidth = w;
g_TextureHeight = h;
}
其中宏UNITYINTERFACEEXPORT为__declspec(dllexport),标记SetTextureFromUnity函数将会被导出成dll库
-Unity使用代码
#if UNITY_IPHONE && !UNITY_EDITOR
[DllImport ("__Internal")]
#else
[DllImport ("RenderingPlugin")]
#endif
private static extern void SetTimeFromUnity(float t);
- DLL文件必须位于程序当前目录或系统定义的查询路径中(即:系统环境变量中Path所设置的路径)。 - DLLImport会按照顺序去查找DLL文件(程序当前目录>System32目录>环境变量Path所设置路径)。 - 返回类型变量、方法名称、参数列表一定要与DLL文件中的定义相一致。
如果被调用的库函数是带返回的,将会有以下几种情况:
- 返回的数据类型是系统已定义好的数据类型通常C#和C/C++之间有一个数据的映射关系,如int、float、char、函数指针。
- 返回数据类型是自定义的指针变量那C#中统一是用IntPtr接收,在C#中使用指针操作时需要加上unsafe关键字,通常还需要配合fixed关键字使用。
- 另外还有一种特殊情况是希望传递的数据类型是Struct,需要在C#中定义一个相同Struct类型,且在C#层以引用方式传入,在dll中进行赋值。
-dll中定义的struct
struct Bar
{
public :
int id;
char* name;
};
EXPORT_DLL void GetBar(Bar& bar)
{
//Bar b;
bar.id = 10;
bar.name = "hi bar 中文了";
}
- C#中定义对应的struct
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct Bar
{
/// int
public int id;
/// char*
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string name;
}
class Program
{
[DllImport("CppDll.dll")]
extern static void GetBar(ref Bar bar);
static void Main(string[] args)
{
Bar b = new Bar();
GetBar(ref b);
}
System.Runtime.InteropServices包下的几个重要的类:
Marshal:类提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他方法。
[link](http://www.cnblogs.com/DragonX/p/3474251.html)
GCHandle:获取托管对象的地址,同时“盯”住该对象
[link](https://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.gchandle.aspx)
Android中动态库的使用
Android中动态库的表现形态问.so文件,直接调用System.loadLibrary("abc")函数就可以加载名为libabc.so的库了。
-
C/C++导出代码
#include <jni.h> #define LOG_TAG "HelloWorld" #include <utils/Log.h> /* Native interface, it will be call in java code */ JNIEXPORT jstring JNICALL Java_com_zhangyi_HelloWorld_printJNI(JNIEnv *env, jobject obj) { LOGI("dufresne Hello World From libhelloworld.so!"); } /* This function will be call when the library first be load. * You can do some init in the libray. return which version jni it support. */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { void *venv; LOGI("dufresne----->JNI_OnLoad!"); if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) { LOGE("dufresne--->ERROR: GetEnv failed"); return -1; } return JNI_VERSION_1_4; }
```
-
Java调用库的代码
package com.zhangyi; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class HelloWorld extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.v("dufresne", printJNI("I am HelloWorld Activity")); } static { //加载库文件 System.loadLibrary("HelloWorldJni"); } //声明原生函数 参数为String类型 返回类型为String private native String printJNI(String inputStr); }