如题,在安卓平台上通过JNI调用GO语言编译生成的so库。JNI部分要是做过几次的应该觉得不难吧(动、静态编译,编写规范等待)。
原因在最后给出,可以直接拉到最后
具体的错误如下:
08:43:38 **** Build of configuration Default for project JNIVoiceMM ****
"D:\\adt-bundle-windows-x86-20140702\\android-ndk-r10\\ndk-build.cmd" all
Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 15 in ./AndroidManifest.xml
[armeabi] Compile++ thumb: maina <= maina.cpp
[armeabi] Prebuilt : libwebclient.so <= jni/webclient/
[armeabi] Prebuilt : libmmain.so <= jni/mmain/
[armeabi] StaticLibrary : libstdc++.a
[armeabi] SharedLibrary : libmaina.so
[armeabi] Install : libmaina.so => libs/armeabi/libmaina.so
[armeabi] Install : libmmain.so => libs/armeabi/libmmain.so
[armeabi] Install : libwebclient.so => libs/armeabi/libwebclient.so
08:43:45 Build Finished (took 6s.749ms)
06-07 08:51:31.019: D/dalvikvm(2590): Trying to load lib /data/app-lib/com.wkw.voicemessagectrl-1/libwebclient.so 0x42466a60
06-07 08:51:31.029: E/dalvikvm(2590): dlopen("/data/app-lib/com.wkw.voicemessagectrl-1/libwebclient.so") failed: dlopen failed: could not load library "libpthread.so.0" needed by "libwebclient.so"; caused by library "libpthread.so.0" not found
06-07 08:51:31.029: W/dalvikvm(2590): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/wkw/activity/MainActivity;
06-07 08:51:31.029: W/dalvikvm(2590): Class init failed in newInstance call (Lcom/wkw/activity/MainActivity;)
06-07 08:51:31.029: D/AndroidRuntime(2590): Shutting down VM
06-07 08:51:31.029: W/dalvikvm(2590): threadid=1: thread exiting with uncaught exception (group=0x42195ba8)
06-07 08:51:31.039: E/AndroidRuntime(2590): FATAL EXCEPTION: main
06-07 08:51:31.039: E/AndroidRuntime(2590): Process: com.wkw.voicemessagectrl, PID: 2590
06-07 08:51:31.039: E/AndroidRuntime(2590): java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "libpthread.so.0" needed by "libwebclient.so"; caused by library "libpthread.so.0" not found
06-07 08:51:31.039: E/AndroidRuntime(2590): at java.lang.Runtime.loadLibrary(Runtime.java:364)
06-07 08:51:31.039: E/AndroidRuntime(2590): at java.lang.System.loadLibrary(System.java:526)
06-07 08:51:31.039: E/AndroidRuntime(2590): at com.wkw.activity.MainActivity.(MainActivity.java:92)
06-07 08:51:31.039: E/AndroidRuntime(2590): at java.lang.Class.newInstanceImpl(Native Method)
06-07 08:51:31.039: E/AndroidRuntime(2590): at java.lang.Class.newInstance(Class.java:1208)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.app.Instrumentation.newActivity(Instrumentation.java:1061)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2112)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.app.ActivityThread.access$800(ActivityThread.java:135)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.os.Handler.dispatchMessage(Handler.java:102)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.os.Looper.loop(Looper.java:136)
06-07 08:51:31.039: E/AndroidRuntime(2590): at android.app.ActivityThread.main(ActivityThread.java:5017)
06-07 08:51:31.039: E/AndroidRuntime(2590): at java.lang.reflect.Method.invokeNative(Native Method)
06-07 08:51:31.039: E/AndroidRuntime(2590): at java.lang.reflect.Method.invoke(Method.java:515)
06-07 08:51:31.039: E/AndroidRuntime(2590): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:815)
06-07 08:51:31.039: E/AndroidRuntime(2590): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:631)
06-07 08:51:31.039: E/AndroidRuntime(2590): at dalvik.system.NativeStart.main(Native Method)
static {
// System.loadLibrary("libpthread"); // 因为如上错误,此处尝试导入安卓库层中的libpthread.so但仍然找不到。所以注释掉了。
System.loadLibrary("webclient"); // libwebclient.so是go语言编译生成的,提供登录服务器、上传下载文件等操作。
System.loadLibrary("mmain");//C语言二次编译库,通过C调用GO,因为GO基于C
System.loadLibrary("maina");//二次编译,生成我们想要的最终功能,以及实现数据类型的转换等。
}
public static native int jLogin(String s1, String s2, String s3); // 第三方库函数
public static native String jUpload(String s1, String s2);
public static native int jDownload(String s1, String s2);
public static native void jSend();
public static native void jWsclose();
public static native void Recv();
...
public void postFiles(long startMili2, long endMili2, String filePath) {
// Toast.makeText(getApplicationContext(), Mylib.sayHello(), 1).show();
if (endMili2 - startMili2 > (long) 1000) {
final File file = new File(filePath);
if (file.exists() && file.length() > 0) {
RequestParams params = new RequestParams();
try {
params.put("files", file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (NetworkUtil.isNetworkConnected(MainActivity.this)
&& NetworkUtil.getNetworkType(MainActivity.this) == NETTYPE_WIFI) {
int returnvalue = jLogin("账户", "密码",
"域名");//初始化并执行登录操作,单点登录(SSO)
if (returnvalue == -1) {
Toast.makeText(getApplicationContext(), "登录失败!", 1)
.show();
} else {
Toast.makeText(getApplicationContext(), "登录成功!", 1)
.show();
String jstring = jUpload(filePath,
"http://192.168.202.1:9000/upload");//此服务器为临时的文件服务器,GO语言后台,用来测试上传。
if (jstring != null && !"".equals(jstring)
&& !jstring.equalsIgnoreCase("null")
&& !jstring.equals("-1")) {
Toast.makeText(getApplicationContext(),
"上传成功! " + jstring, 1).show();
file.delete();
} else {
Toast.makeText(getApplicationContext(),
"上传失败! " + jstring, 1).show();
file.delete();
}
}
//如下注释代码是我自己手动搭建的JAVA后台服务器,而现有服务器后台是以GO语言编写。
// client.post(
// "http://192.168.1.129:8080/UploadFiles/UploadFileServlet",
// params, new AsyncHttpResponseHandler() {
// @Override
// public void onSuccess(int arg0, Header[] arg1,
// byte[] arg2) {
// file.delete();
// Toast.makeText(getApplicationContext(),
// "文件上传成功!", 1).show();
// }
//
// @Override
// public void onFailure(int arg0, Header[] arg1,
// byte[] arg2, Throwable arg3) {
// file.delete();
// Toast.makeText(getApplicationContext(),
// "文件上传失败!", 1).show();
// }
// });
} else {
file.delete();
}
}
} else {
File file = new File(filePath);
if (file.exists())
file.delete();
Toast.makeText(getApplicationContext(), "录音时间过短!", 1).show();
}
if (audioFile.exists()) {
audioFile.delete();
}
}
以上就是整个JNI部分了,这里粗糙解释一下库。其实就是封装了一些操作的特定文件,它与静态链接库生成的最大不同之处在于,动态链接库的编译类似于C中的指针,当前库调用原始库中的函数(是两个.so文件,是依赖关系),而静态链接库的生成会将所有会用到的库都包含进来,类似于将所有会用到的库都打包进一个.a文件。这也是为什么运行程序时产生failed: dlopen failed: could not load library “libpthread.so.0” needed by “libwebclient.so”; caused by library “libpthread.so.0” not found的原因之一。
在安卓系统/lib目录下我们可以找到libpthread-2.16.so、libpthread.so.0两个相关文件,但为什么还是会造成”libpthread.so.0” not found,这里介绍大家几个相关问题的网站,可以发现蜘丝马迹:
Linux-Error loading libpthread.so on Android
近期编译android的教训
个人觉得可以着重看这个
Cannot find Shared Library: libpthread.so.0
大家可以搜索了解一下编译链部分,也可以去了解一下如何查看.so信息(windows下查看可以使用Cygwin),其实通过对比可以发现libwebclient.so和自己手动写C文件通过NDK编译出来的.so库信息字段是不同的(已经试过了,这个问题与CPU类型没关系,不管编译出ARM还是ARMV7等的.so库都无济于事)。
得出结论:最终问题定位在了兼容性上,通过更改编译环境,使编译链与安卓兼容才得以解决。幸好老大及时出手,毕竟之前也没遇到过Go开发安卓库的情况。