
正文
安卓java代码写到so java编写安卓
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
android打包时怎么修改添加自定义java类和so库,以及Androi
问题描述安卓java代码写到so:Android如何调用第三方SO库; 已知条件:SO库为Android版本连接库(*.so文件)安卓java代码写到so,并提供了详细的接口说明; 已了解解决方案: 1.将SO文件直接放到libs/armeabi下,然后代码中System.loadLibrary("xxx");再public native static int xxx_xxx_xxx();接下来就可以直接调用xxx_xxx_xxx()方法; 2.第二种方案,创建自己的SO文件,在自己的SO文件里调用第三方SO,再在程序中调用自己的SO,这种比较复杂,需要建java类文件,生成.h文件,编写C源文件include之前生成的.h文件并实现相应方法,最后用android NDK开发包中的ndk-build脚本生成对应的.so共享库; 求解: 1.上面两种方案是否可行安卓java代码写到so?不可行的话存在什么问题? 2.两种方案有什么区别?为什么网上大部都是用的第二种方案? 3.只有一个*.so文件,并提供了详细的接口说明,是否可在ANDROID中使用它?首先要看这个SO是不是JNI规范的SO,比如有没有返回JNI不直接支持的类型。也就是说这个SO是不是可以直接当作JNI来调用。如果答案是否定的,你只能选第二个方案。如果答案是肯定的,还要看你是不是希望这个SO的库直接暴露给JAVA层,如果答案是否定的,你只能选第二个方案,比如你本身也是一个库的提供者。一般如果你只有SO,就说明这个是别人提供给你的,你可以要求对方给你提供配套的JAVA调用文件。1、这个要看这个SO是不是符合JNI调用的规范。还要看你自己的意愿。 2、因为第二种方法最灵活,各种情况都可以实现。 3、可以看能不能直接从JAVA调用的最简单的方法就是看SO里的函数名是不是Java_XXX_XXX_XXX格式的 是就可以,你可以自己写一个配套的JAVA文件,注意一下SO函数名和JAVA函数名的转换规则,或者向SO提供方索要; 不是的话就选第二种方案吧。1、检查所需文件是否齐全 使用第三方动态库,应该至少有2个文件,一个是动态库(.so),另一个是包含 动态库API声明的头文件(.h) 2、封装原动态库 原动态库文件不包含jni接口需要的信息,所以我们需要对其进行封装,所以我 们的需求是:将libadd.so 里面的API封装成带jni接口的动态 3、编写库的封装函数libaddjni.c 根据前面生成的com_android_libjni_LibJavaHeader.h 文件,编写libaddjni.c,用 来生成libaddjni.soAndroid中集成第三方软件包(.jar, .so)Android中可能会用到第三方的软件包,这包括Java包.jar和Native包.so。jar包既可通过Eclipse开发环境集成,也可通过编译源码集成,看你的工作环境。假定自己开发的程序为MyMaps,需要用到BaiduMaps的库,包括baidumapapi.jar和libBMapApiEngine_v1_3_1.so。一、Eclipse中集成第三方jar包及.so动态库MyMaps工程下创建目录libs以及libs/armeabi,把baidumapapi.jar放在的libs/目录下,把libBMapApiEngine_v1_3_1.so放在libs/armeabi/下。Eclipse中把第三方jar包baidumapapi.jar打包到MyMaps的步骤:1. 右击工程,选择Properties; 2. Java Build Path,选择Libraries; 3. Libraries页面点击右面按钮“Add Library…”; 4. 选择“User Library”,点击“Next”; 5. 点击“User Libraries”按钮; 6. 在弹出界面中,点击“New…”; 7. 输入“User library name”,点击“OK”确认; 8. 返回之后,选择刚刚创建的User library,右面点击“AddJARs”; 9. 选择MyMaps/libs/下的baidumapapi.jar; 10. 确认,返回。这样,编译之后,该jar包就会被打进MyMaps.apk中,libBMapApiEngine_v1_3_1.so也被打包在lib/armeabi/中。 程序运行过程中,libBMapApiEngine_v1_3_1.so被放在/data/data/yourAppPackage/lib/下,加载动态库时系统会从程序的该lib/目录下查找.so库。二、源码中集成第三方集成jar包及.so动态库Android源码中MyMaps放在packages/apps下。MyMaps下创建目录libs以及libs/armeabi,并把baidumapapi.jar放在libs/,把libBMapApiEngine_v1_3_1.so放在libs/armeabi。2.1 修改Android.mk文件Android.mk文件如下:[plain] view plaincopy LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_STATIC_JAVA_LIBRARIES := libbaidumapapiLOCAL_SRC_FILES := $(call all-subdir-java-files)LOCAL_PACKAGE_NAME := MyMapsinclude $(BUILD_PACKAGE)################################################## include $(CLEAR_VARS)LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=libbaidumapapi:libs/baidumapapi.jar LOCAL_PREBUILT_LIBS :=libBMapApiEngine_v1_3_1:libs/armeabi/libBMapApiEngine_v1_3_1.so LOCAL_MODULE_TAGS := optional include $(BUILD_MULTI_PREBUILT)# Use the following include to make our testapk. include $(callall-makefiles-under,$(LOCAL_PATH))1 集成jar包 LOCAL_STATIC_JAVA_LIBRARIES取jar库的别名,可以任意取值; LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES指定prebuiltjar库的规则,格式:别名:jar文件路径。注意:别名一定要与LOCAL_STATIC_JAVA_LIBRARIES里所取的别名一致,且不含.jar;jar文件路径一定要是真实的存放第三方jar包的路径。 编译用BUILD_MULTI_PREBUILT。 2 集成.so动态库 LOCAL_PREBUILT_LIBS指定prebuilt so的规则,格式:别名:so文件路径。注意:别名一般不可改变,特别是第三方jar包使用.so库的情况,且不含.so;so文件路径一定要是真实的存放第三方so文件的路径。 编译拷贝用BUILD_MULTI_PREBUILT。2.2 加入到GRANDFATHERED_USER_MODULES在文件user_tags.mk中,把libBMapApiEngine_v1_3_1加入到GRANDFATHERED_USER_MODULES中[plain] view plaincopy GRANDFATHERED_USER_MODULES += \ … \ libBMapApiEngine_v1_3_1user_tags.mk可以是build/core下的,也可以是$(TARGET_DEVICE_DIR)下的,推荐修改$(TARGET_DEVICE_DIR)下的。2.3 编译结果MyMaps.apk编译生成在out/target/product/YourProduct/system/app/下; libBMapApiEngine_v1_3_1.so放在out/target/product/YourProduct/system/lib/下,这也是系统加载动态库时搜索的路径。
相关问答
Q1: 关于Android开发中Java对于.so库的调用
android掉用c的so包就是通过jni
,
应该给你的jar包就是用来调用so包的
。不会让你直接掉so包的放心。so包一般都是为了保证核心代码不被反编译
,另外就是效率高才会用,或者就是开发游戏
。
他们说够用应该是jar包已经和so包的jni调用接口都调好了
,
你用jar包就行了
Q2: 安卓JVM加载so库流程
好久没有写点东西发了,工作中的事情有点杂,也找不到整块东西可以写的。
最近调查了一个问题,稍微追了一下流程,这里记录一下。
由于我们支持的设备相对比竞品,zygote进程多占用了好几倍的内存空间。通过dump meminfo后发现,我们的设备在so库,ttf,和unkonwn mmap的内存空间相比竞品一共大了20多M,其中so库多了15M左右。
通过查看zygote进程的smaps,确定了占用空间最大的几个so库确实是我们自己的。虽然确定了内存占用大的原因,还是得把这些so库是加载在zygote进程中的时机确定了才行。
我的第一反应就是在zygote启动时,加载sharedLibrary()时,把这些库加载了,于是去看了这部分源码,并没有。
通过反复调试以及追踪源码,最后发现是在JVM启动的过程中加载了这些so库,这些so库的配置在“system/etc/public.libraries.txt”下。
这个文件里配置的都是public的so库,能够被普通app访问的。类似的配置文件还有“vendor/etc/”下面的,还有一些其他的配置地方,我没有深入去看,想要看的盆友可以自己去看源码或者注释。
下面我就带大家一起看看虚拟机加载这些so库的流程。
调用栈:
frameworks/base/cmds/app_process/app_main.cpp
----runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
frameworks/base/core/jni/AndroidRuntime.cpp
----AndroidRuntime::startVm
---JNI_CreateJavaVM(pJavaVM, pEnv, initArgs)
art/runtime/jni/java_vm_ext.cc
---android::InitializeNativeLoader();
system/core/libnativeloader/native_loader.cpp
----Initialize()
----ReadConfig(public_native_libraries_system_config, sonames, always_true, error_msg)
其中,public_native_libraries_system_config 为 system/etc/public.libraries.txt
这部分流程是在安卓设备开机过程中的,在执行ZygoteInit.main()之前会先启动java虚拟机的,这样fork其他java进程的时候,java环境就已经有了,不用再创建虚拟机了。
最后贴一下Initialize()函数:
Q3: java 调用so文件
用JNI实现
实例:
创建HelloWorld.java
class HelloWorld
{
private native void print();
public staticvoid main(String[] args)
{
new HelloWorld().print();
}
static
{
System.loadLibrary("HelloWorld");
}
}
注意print方法的声明,关键字native表明该方法是一个原生代码实现的。另外注意static代码段的System.loadLibrary调用,这段代码表示在程序加载的时候,自动加载libHelloWorld.so库。
编译HelloWorld.java
在命令行中运行如下命令:
javac HelloWorld.java
在当前文件夹编译生成HelloWorld.class。
生成HelloWorld.h
在命令行中运行如下命令:
javah -jni HelloWorld
在当前文件夹中会生成HelloWorld.h。打开HelloWorld.h将会发现如下代码:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include jni.h
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
该文件中包含了一个函数Java_HelloWorld_print的声明。这里面包含两个参数,非常重要,后面讲实现的时候会讲到。
实现HelloWorld.c
创建HelloWorld.c文件输入如下的代码:
#include jni.h
#include stdio.h
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
}
注意必须要包含jni.h头文件,该文件中定义了JNI用到的各种类型,宏定义等。
另外需要注意Java_HelloWorld_print的两个参数,本例比较简单,不需要用到这两个参数。但是这两个参数在JNI中非常重要。
env代表java虚拟机环境,Java传过来的参数和c有很大的不同,需要调用JVM提供的接口来转换成C类型的,就是通过调用env方法来完成转换的。
obj代表调用的对象,相当于c++的this。当c函数需要改变调用对象成员变量时,可以通过操作这个对象来完成。
编译生成libHelloWorld.so
在Linux下执行如下命令来完成编译工作:
cc -I/usr/lib/jvm/java-6-sun/include/linux/
-I/usr/lib/jvm/java-6-sun/include/
-fPIC -shared -o libHelloWorld.so HelloWorld.c
在当前目录生成libHelloWorld.so。注意一定需要包含Java的include目录(请根据自己系统环境设定),因为Helloworld.c中包含了jni.h。
另外一个值得注意的是在HelloWorld.java中我们LoadLibrary方法加载的是
“HelloWorld”,可我们生成的Library却是libHelloWorld。这是Linux的链接规定的,一个库的必须要是:lib+库
名+.so。链接的时候只需要提供库名就可以了。
运行Java程序HelloWorld
大功告成最后一步,验证前面的成果的时刻到了:
java HelloWorld
如果你这步发生问题,如果这步你收到java.lang.UnsatisfiedLinkError异常,可以通过如下方式指明共享库的路径:
java -Djava.library.path='.' HelloWorld
当然还有其他的方式可以指明路径请参考《在Linux平台下使用JNI》。
我们可以看到久违的“Hello world!”输出了。
Q4: android studio怎么生成.so文件
1、编译环境的搭建
正所谓:“工欲善其事必先利其器”嘛,所以先把改准备工具准备一下,因为是在studio下编译的,所以啊,什么studio、什么sdk、什么jdk,这些都自己去弄吧,这里说一下ndk这个编译C文件工具怎么下载安装;
下载完了之后直接是一个zip的压缩包,解压即可;
然后你会在local.properties文件中看到:
不要急,还没有完,ndk环境搭建还有最后一步,在gradle.properties的文件末尾加上android.useDeprecatedNdk=true这段代码:
好了,到此处环境就搭建完毕了。
2、java代码和C代码的编写步骤及过程
首先新建一个java类JNIUtils.java
public class JNIUtils {
static {
System.loadLibrary("huazict");
}
//java调C中的方法都需要用native声明且方法名必须和c的方法名一样
public native String getString();
}
然后如下图所示重新Make Project一下工程:
会在工程目录E:\work\MyApplication\stujni\build\intermediates\classes\debug\com\huazi\stujni\jni中看到自己编译后的class文件JNIUtils.class
其次就是生成.h文件了
在studio打开Terminal命令行工具,打开步骤是View-Tool Windows-Terminal
(或直接按Alt+F12
然后在命令行中先进入到工程的main目录下
输入命令:javah -d jni -classpath 自己编译后的class文件的绝对路径
例如:javah -d jni -classpath
E:\work\MyApplication\stujni\build\intermediates\classes\debug
com.huazi.stujni.jni.JNIUtils(注意debug后的空格)
看到上图,图中命令行中是直接进入到了工程的main目录下(在哪个目录下运行就会在哪个目录下自动生成jni文件夹),按回车之后就会在main目录下生成jni文件夹,同时生成.h文件,
这个文件.h文件不需要做任何修改,默认即可。
现在我们来写一个test的C文件huazict.c同.h文件一样放到jni文件夹下,代码如下:
#include "com_huazi_stujni_jni_JNIUtils.h"
/**
* 上边的引用标签一定是.h的文件名家后缀,方法名一定要和.h文件中的方法名称一样
*/
JNIEXPORT jstring JNICALL Java_com_huazi_stujni_jni_JNIUtils_getString
(JNIEnv *env, jobject obj) {
return (*env)-NewStringUTF(env, "这是我测试的jni");
}
最后在构建文件中的默认配置中加上:
//ndk编译生成.so文件
ndk {
moduleName "huazict" //生成的so名字
abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库。
}
到这里,通过jni调C就完成了,现在我们来测试一下,写个TextView显示一下调用的C:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.tv);
tv.setText(new JNIUtils().getString());
}
}
ok,没问题,可以调用,其实也没有想象中的那么难,是吧!
三、调用编译过的.so库
上边编译完成了,有人会问:我要的是编译后的.so库,别人用的时候直接拿来用就可以了,那编译后的.so库在哪呢?
根据这个路径就可以找到指定输出的三种体系结构下的.so库文件,然后把.so文件复制出来,如下图所示的放到相应的文件夹下就ok了:
再次运行,结果还是一样的,跟上边贴的那张图的显示效果是是一样的,同样能调用到,代码我就不上传了,都在上边贴上去了,而且也上传不了,公司的所有文件都是加密的,想上传都上传不了。
到这里,整个jni的调用过程就结束了,包括调用jni需要的环境以及调用的过程,最后.so文件的导出,都在上边了,如果上边的jni调用过程有什么问题,欢迎留言,谢谢。
Q5: 如何把JAVA工程 编译成.SO文件
1,写一个java文件,声明一个native 方法,例如public native void test(); 2,把它编译成class文件 3,使用javah 这个class来生成cpp的头文件 4,实现头文件里面那些函数 4,用ndk编译生成动态库 其中1,想生成几个就写几个native方法。
关于安卓java代码写到so和java编写安卓的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。







