您的位置:首页 > 移动开发 > Android开发

Android JNI打包so文件到apk .

2017-06-30 08:55 411 查看
FROM:http://blog.csdn.net/itluochen/article/details/53784830

Android JNI 的使用,之前很少使用这个,所以这次用到占用了很多时间搞,不熟悉C++

Android JNI如何用呢?

下面这段拷贝别人的,是没问题的:

常用有两种方法:

1、在windows/Linux下,首先把so动态库文件 编译出来(通过NDK),然后再
拷贝到Android的工程里 libs/armeabi/下,如果libs/armeabi 不存在,那就自己创建,最后用eclipse编译本项目,这样将自动把so文件打包到apk里;eclipse在打包的时候会根据文件名的命名规则(lib****.so)去打包so文件,开头和结尾必须分别为“lib”和“.so”,否则是不会打包到apk文件中的。

2、在android源码里,使用mm命令编译apk工程。

   在project根目录下创建一个Android.mk,文件内容如:

LOCAL_PATH:= $(call my-dir)   

include $(CLEAR_VARS)   

LOCAL_MODULE_TAGS := user  

LOCAL_SRC_FILES := $(call all-subdir-java-files)   

LOCAL_PACKAGE_NAME := jnisample   

LOCAL_JNI_SHARED_LIBRARIES := libtest   

include $(BUILD_PACKAGE)   

include $(LOCAL_PATH)/jni/Android.mk   

#include $(call all-makefiles-under,$(LOCAL_PATH)) 

LOCAL_JNI_SHARED_LIBRARIES := libtest 就是把so[b]文件放到apk文件里的libs/armeabi里[/b],而include $(LOCAL_PATH)/jni/Android.mk为了编译so文件

把工程文件放到SDK platform/packages/apps目录下,然后进入该目录,命令里输入mm,进行该工程的编译,这样编译出来的apk,在libs/armeabi文件夹里面包含so文件

分割线----------////////////////////////////////////////////////////////////////

说说我知道的两种:

1. 在jni目录下放置你的.c等源文件,在Android.mk文件中标注好源文件,生成的lib文件名称等,然后在NDK中 ndb-build编译so文件,这种适合大多数的单独的第三方应用,就是在应用市场发布的那种第三方应用,缺点是比较麻烦,需要手动生成so文件到libs/armeabi下(我发现最后其实在lib/armeabi-v7a/ 下,可以自行查阅armeabi&armeabi-v7a的区别),不过生成一次,后面无需再生成,也还算好;
http://blog.sina.com.cn/s/blog_9026a07a010179uj.html jni基础知识 http://www.cnblogs.com/yejiurui/archive/2012/11/21/2780959.html


NDK入门项目实战(转载)

http://www.cnblogs.com/yejiurui/archive/2012/11/21/2780940.html
http://blog.csdn.net/muyu114/article/details/7841283
2. 就是做rom开发的厂商,可以基于Android源码开发,比如在packages/app/Video 路径下,需要写一个jni文件,那么怎么操作呢?这种操作利用Android.mk文件,无需手动编译so文件,会使用脚本自动编译so文件,比较方便,但是只适合基于Android源码开发的厂商。

第一种方法,网上有很多文章,我下载也转载了一些,重点说第二种,因为我就是在第二种方式上遇到了麻烦。

如何把so文件打包到apk中呢?
这两篇文章帮助了我~感谢作者

.so 打包到apk中的方法: http://blog.chinaunix.net/uid-26524139-id-3161753.html
http://www.android100.org/html/201308/20/4008.html

http://www.360doc.com/content/11/0511/00/4154133_115845654.shtml
http://wangjianfei1016.blog.163.com/blog/static/20244096201212825258706/ http://blog.csdn.net/koko7958/article/details/7955046 (在这个网址里搜索:LOCAL_JNI_SHARED_LIBRARIES ,这个参数非常重要)

===》LOCAL_REQUIRED_MODULES

要注意一下这个参数:Android原生浏览器也有这行,后面的网址有解释。
LOCAL_REQUIRED_MODULES := SoundRecorder  
http://blog.chinaunix.net/uid-29535415-id-4144835.html
Gallery和Camera共用了一个Android.mk文件

LOCAL_SRC_FILES是指定java文件,LOCAL_RESOURCE_DIR是指定资源文件,然后通过include $(BUILD_PACKAGE)来编译apk,其中LOCAL_REQUIRED_MODULES指定了libjni_mosaic,所以会优先编译Camera的这个c库。

include $(call all-makefiles-under, jni)

是编译Gallery的jni

剩下的就是编译测试apk

34 # Use the following include to make gallery test apk.

35 include $(call all-makefiles-under, $(LOCAL_PATH))

36 

37 # Use the following include to make camera test apk.

38 include $(call all-makefiles-under, ../Camera)

别的没什么了,比较简单

===》

如何让jni下的文件自动编译生成so文件呢?开始总是不执行,我一直找不到原因,后来认真看了看其它项目的Android.mk文件,发现最后一行都有这句:

# additionally, build tests in sub-folders in a separate .apk

include $(call all-makefiles-under,$(LOCAL_PATH))
看这样就知道是把项目所有子文件下的Android.mk文件执行,加上这句果然OK了。

还有人建议说这样搞:就是在项目的Android.mk文件中引用jni的mk文件,让它去编译,应该是都可以的,只不过如果有很多mk文件,上面的办法更方便一点。

#include $(LOCAL_PATH)/jni/Android.mk

使用so文件就相当容易了,可以参照网上的文章即可~

 
-------------------------------------------------------------------------------------------------------

编译时候的信息:

  adding: lib/ (stored 0%)

  adding: lib/armeabi-v7a/ (stored 0%)

  adding: lib/armeabi-v7a/libpatcher_jni.so (deflated 47%)

最后在应用的 data/data/com.lices.book/lib 会有 libpatcher_jni.so 这个文件

 armeabi和armeabi-v7a区别

 armeabi和armeabi-v7a是表示cpu的类型,不同的cpu的特性不一样,armeabi就是针对普通的或旧的arm cpu,armeabi-v7a是针对有浮点运算或高级扩展功能的arm cpu。

问:编译时候生成多个.so文件,有时会在armeabi和armeabi-v7a中分别放置一份,为什么?

 

答:该设置一般能在jni下的Application.mk中或Android.mk中找到。这个需要看你的Native Code要做什么事情,armeabi是指的该so库用于Arm的通用CPU,而v7a的CPU支持硬件浮点运算。因此armeabi通用性强,但速度慢,而v7a能充分发挥v7a CPU的能力。具体v7a的优势可以参见http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344c/Cacciced.html

介绍
http://www.myexception.cn/android/1594360.html
 

--------------------------------------------------------------------------------------------------------

背景:对于Android工程 Eclipse里编译好的.so文件放到 libs\armeabi下以后, 这样.so文件就可以打包到apk文件里,在apk装到手机上以后 
在libs\armeabi下的.so文件应该就会解压到/data/data/这里是你project的包名(比如com.first.second)/lib下。

问题:写了个测试的android JNI app,.so已经放到了libs\armeabi下 而且从编译的apk里也能看到.so已经打包进去(解压apk文件,里边应该有lib文件夹) 但是在/data/data/com.test.test/lib下就是没有相应的.so文件

解决方法:原来是因为我的.so没有按照android标准命名,.so的名字必须有'lib'前缀 否则apk解压/安装到手机的时候不会把libs\armeabi下的.so拷贝到/data/data/com.test.test/lib下。原来我用的so名字是JNITest.so, 改为libJNITest.so后一切正常。

补充:

a) 用loadLibrary调用的时候需要去掉lib前缀 System.loadLibrary("JNITest"); 

b) 用load调用的时候需要写全路径名 且不能去掉lib前缀 因为这里是当成一个普通文件读取的 System.load("/data/data/com.test.test/libJNITest.so"); 其实这个是错误的,因为正规的使用根本不是引用在data/data/com.xxx/lib下的这个so文件,而是引用在libs/armeabi下的so文件,只用通过System.load("JNITest.so"); 调用即可。当然这样调用也是可以的~只不过不规范,不推荐

 

参考链接:

http://blog.csdn.net/leer168/article/details/7239641
http://bbs.51cto.com/thread-948244-1.html
Android.mk文件的介绍,挺好
http://blog.chinaunix.net/uid-25885064-id-3364408.html
http://blog.chinaunix.net/uid-25885064-id-3364407.html

http://blog.chinaunix.net/uid-25885064-id-3364408.html(这篇文章写的非常棒~)
http://blog.sina.com.cn/s/blog_602f8770010148ce.html

http://dengzhangtao.iteye.com/blog/1750782

http://www.linuxidc.com/Linux/2011-03/33354.htm








-----------------------------------------------------------------------------------------------------------------

2014-3-26号补充:

最近3天,在使用JNI的地方懂了很多,遇到一个有意思的问题,这里必须总结一下,肯定很多人没遇见过,或者不知道。

以下所有步骤都是基于Android原生代码开发:(下面的Test代表我的项目名称)

比如你的应用用到了JNI,那么你可以在packages/app/Test下这样建立目录:

Test 

     -> src

     -> res

     -> jni

     -> libs

     -> Android.mk

     -> AndroidManifest.xml

     -> README

第一种情况:

     你用的是第三方的so文件或者别人编译好的so文件,你可以直接放到 libs/armeabi 下,这样在标红色的Android.mk中只需要加上:
#patch libpatcher_jni into apk file: 

LOCAL_JNI_SHARED_LIBRARIES := libuserbookpatcher_jni
即可把so文件打包到apk中,然后你可以 adb install -r Test.apk,然后运行程序,即可使用so文件了。

补充:LOCAL_JNI_SHARED_LIBRARIES
:= libtest 就是把so文件放到apk文件里的libs/armeabi里, 但是我们已经把so文件放到libs/armeabi目录下了,所以加这句完全没用,我已经测试通过了。可以忽略标为绿色的文字。

第二种情况:

     你用的是第三方的so文件或者别人编译好的so文件,或者你自己写的源代码文件,你也可以不在libs下放任何东西,你需要在jni目录下放置源文件和负责编译的Android.mk,像下面这样:

     jni
          -> Android.mk
          -> call.c

          -> call.h
          -> ... ...

     你需要放入源文件和Android.mk文件,Android.mk文件是对源文件的一个配置,打包的时候按照这个规则打。你需要在标红色的Android.mk中只需要加上:

#patch libpatcher_jni into apk file

LOCAL_JNI_SHARED_LIBRARIES := libuserbookpatcher_jni
这样你可以把so文件打包到apk中,然后你可以 adb install -r Test.apk,然后运行程序,即可使用so文件了。

第三种情况:

你可以利用上面第二种情况编译了一个APK,利用这个命令:mm -B, 然后可以看到在编译的最后有这样的命令:

Install: out/target/product/xxx/system/app/xxxbook.apk

Install: out/target/product/xxx/system/lib/libxxxbook_jni.so
发现第二条命令把我们需要的so文件打到了out的system/lib下

如果你push apk /system/app/,就是说你的应用是系统应用,那么你在使用这个so文件的时候,会先去系统的 system/lib 下找,如果没找到,就会报下面的错误:

W/dalvikvm( 5177): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/miui/userbook/util/Patcher;

W/dalvikvm( 5177): threadid=12: thread exiting with uncaught exception (group=0x41566930)

E/AndroidRuntime( 5177): FATAL EXCEPTION: AsyncTask #3

E/AndroidRuntime( 5177): java.lang.RuntimeException: An error occured while executing doInBackground()

E/AndroidRuntime( 5177):     at android.os.AsyncTask$3.done(AsyncTask.java:299)

E/AndroidRuntime( 5177):     at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)

E/AndroidRuntime( 5177):     at java.util.concurrent.FutureTask.setException(FutureTask.java:219)

E/AndroidRuntime( 5177):     at java.util.concurrent.FutureTask.run(FutureTask.java:239)

E/AndroidRuntime( 5177):     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)

E/AndroidRuntime( 5177):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)

E/AndroidRuntime( 5177):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)

E/AndroidRuntime( 5177):     at java.lang.Thread.run(Thread.java:856)

E/AndroidRuntime( 5177): Caused by: java.lang.ExceptionInInitializerError

E/AndroidRuntime( 5177):     at com.xxx.util.UserbookUpdateManager.update(UserbookUpdateManager.java:394)

E/AndroidRuntime( 5177):     at com.xxx.util.UserbookUpdateManager.doUpgrade(UserbookUpdateManager.java:283)

E/AndroidRuntime( 5177):     at com.xxx.util.UserbookUpdate.update(UserbookUpdate.java:104)

E/AndroidRuntime( 5177):     at com.xxx.util.UserbookUpdate.access$000(UserbookUpdate.java:15)

E/AndroidRuntime( 5177):     at com.xxx.util.UserbookUpdate$1.doInBackground(UserbookUpdate.java:57)

E/AndroidRuntime( 5177):     at com.xxx.util.UserbookUpdate$1.doInBackground(UserbookUpdate.java:54)

E/AndroidRuntime( 5177):     at android.os.AsyncTask$2.call(AsyncTask.java:287)

E/AndroidRuntime( 5177):     at java.util.concurrent.FutureTask.run(FutureTask.java:234)

E/AndroidRuntime( 5177):     ... 4 more

E/AndroidRuntime( 5177): Caused by: java.lang.UnsatisfiedLinkError: Couldn't load xxxbook_jni from loader dalvik.system.PathClassLoader[dexPath=/system/app/xxxbook.apk,libraryPath=/data/app-lib/xxxbook]: findLibrary returned null

E/AndroidRuntime( 5177):     at java.lang.Runtime.loadLibrary(Runtime.java:365)

E/AndroidRuntime( 5177):     at java.lang.System.loadLibrary(System.java:535)

E/AndroidRuntime( 5177):     at com.xxx.util.Patcher.<clinit>(Patcher.java:32)

E/AndroidRuntime( 5177):     ... 12 more

W/ActivityManager( 1171):   Force finishing activity com.xxx/.ListActivity

V/PhoneStatusBar( 2411): setLightsOn(true)

I/RenderThread( 4536): RenderThread resumed

当然,首先需要确认你的语法及逻辑没有问题,如果都排除了,那么问题就处在so文件的位置上了, 也是我遇到的问题。

理一理思路:

1.如果你是adb install xxx.apk按装的,那么按装后,我们的so文件会被拷贝到:/data/data/com.xxx.book/lib

-rwxr-xr-x system   system      73916 2008-05-16 06:40 libuserbookpatcher_jni.so

用命令ll可以看到如下:

/data/data/com.xxx.book # ll

drwxrwx--x u0_a60   u0_a60            2014-03-26 09:38 app_cache

drwxrwx--x u0_a60   u0_a60            2014-03-26 09:38 app_database

drwxrwx--x u0_a60   u0_a60            2014-03-26 09:38 cache

drwxrwx--x u0_a60   u0_a60            2014-03-26 09:38 databases

lrwxrwxrwx install  install           2014-03-26 10:54 lib -> /data/app-lib/com.xxx.book-1

drwxrwx--x u0_a60   u0_a60            2014-03-26 09:38 shared_prefs

这样我们在使用so文件的时候,会默认到应用的data/data目录的lib文件夹下找,如果找不到就会报错误。当前前提是你的系统system/lib下没有同样的so文件

2.如果你是adb push xxx.apk /system/app/下,那么你的so文件就必须使用上面的第二种方法打包,然后so文件会打到system/lib 下,这样你刷机后,系统的system/lib下就有你需要的so文件了。

如果你不想刷机,你也可以通过adb push *.so \system\lib的方式,将*.so放到system\lib下,以供调用。

在你需要使用so文件的时候,会默认到system/lib 下找这个so文件,如果找不到,就会报错误,像下面这样:

Caused by: java.lang.UnsatisfiedLinkError: Couldn't load xxxbook_jni from loader dalvik.system.PathClassLoader[dexPath=/system/app/xxxbook.apk,libraryPath=/data/app-lib/xxxbook]:
findLibrary returned null
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jni apk so库