本帖最后由 欧雨鹏 于 2020-10-23 22:06 编辑
(各位大哥大姐,咳 咳 ,看在我写的这么辛苦的份上。能不能看完整篇文章后,辛苦一下动动手指头,点一下评分。。谢谢了!)
如果想看如何用JNI_onLoad注册方法,请移架下面的贴子
折腾折腾用android studio去编写so--动态注册 https://www.52pojie.cn/thread-1289375-1-1.html
序言
个人只学过c/c++和c#语法,用的相关工具比较多。此前还没碰过android studio工具,JAVA代码也算大概看得懂吧。
为了写一个so。对着android studio这工具死命折腾。project创建了一个又一次。同样的事情不知道做了多少次。总算有一两次成功了。
现在只能做一做笔记,以免忘记。
由于刚开始折腾时,有些心躁。看着前辈们曾经发的贴子,跟着一遍一遍做。问题总是出现。接着就是修改host,添加sdk版本,更新Tools,有什么搞什么。。结果不知道哪脑子卡了。点了update..好了,直接update了。。图标变白了,界面变白了。一看版本,直接把版本给更新了。现在是4.1版本
a1
v2
最后,还是没看到前辈们贴子里提到的LLDB这个玩意。
v3
但这不影响。起码最后操作成功。
android studio版本 4 .1SDK TOOLS;默认添加了cmake,NDK没有LLDB这个东西。主要用到了NDK和CMAKE
创建工程用到的android 5.1版本
先来一遍操作过程再说。
1.把工程创建起来
v4
2.在java层的com.xxxx包里面添加一个类,这个类要用做什么?加载so,和声明native。简单的写法,写太复杂怕搞不定。
选中com.hello.myapplication4,右键--new---java class
v6
弹出这么一个对话框,没有确定按钮,好吧。这肯定是一个回车就搞定的玩意,上面输入名称就可以了:jniText
v5
然后又弹出这么一个对话框。虽然不是很想理它。但是还是搜了一下。
v7
(1).IMPORT_BLOCK - 以换行符分隔的列表,其中包含支持任何父类或接口所需的 Java import 语句,或为空字符串 ("")。例如,如果您仅实现 Runnable 接口而不扩展任何内容,则此变量将为 “import java.lang.Runnable;\n”。如果您实现 Runnable 接口并扩展 Activity 类,则此变量将为 “import android.app.Activity;\nimportjava.lang.Runnable;\n”。
(2).VISIBILITY - 相应类是否具有公开访问权限。其值可以为 PUBLIC 或 PACKAGE_PRIVATE。
(3).SUPERCLASS - 单个类名称,或为空。如果存在,则新类名称后面将有一个 extends ${SUPERCLASS} 子句。
(4).INTERFACES - 以英文逗号分隔的接口列表,或为空。如果存在,则父类后面将有一个 implements ${INTERFACES} 子句;如果没有父类,则类名称后面将有一个该子句。对于接口和注释类型,接口具有 extends 关键字。
(5).ABSTRACT - 相应类是否应为抽象类。其值可以为 TRUE 或 FALSE。
(6).FINAL - 相应类是否应为最终类。其值可以为 TRUE 或 FALSE。
注:上面的解释内容来自:https://blog.csdn.net/weixin_37077736/article/details/107460139
啥都不填,直接回车。
这样就生成了一个jniText类
v8
添加代码,代码如下:
[C] 纯文本查看 复制代码 package com.hello.myapplication4;
class jniText {
static{
System.loadLibrary("loadNewSo");//loadNewSo这个名称自己设置
}
public static native String getStringMyName();//声明native方法,实现是在c/c++中实现
}
3.jniText这个类创建好了,代码也写好了。下面就要先把它生成.class文件。不要问为什么要生成.class文件。问就是回答。
.class结尾的文件是java的字节码文件,里面存放的是我们对java源码编译后产生的二进制代码.
操作方法:Build---Rebuild Project
ReBuild完成后,在路径build\intermediates\javac\debug\class\com\hello\myapplication4里查看之前创建的类是不是生成了.class文件。
v9
4.上面的jni文件对native层的声明已经处理完毕。接下来是不是要实现它了。所以要创建c/c++的实现文件。这里选择用c文件。
(1)先生成一个与native相关的h文件。
就是刚刚创建的jniText类的java层。
在main层创建一个Jni文件夹:New---Folder---JNI Folder
v10
alt+f12调出terminal,如果有就不用了。cd到java层。因为要生成h文件的是com.hello.myapplication4.jniText这个类。所以要进入java层
v12
执行javah命令,生成h文件到jni文件夹里。(注:这里操作的时候,提示:编码GBK的不可映射字符。是因为注释是中文的。去掉了就正常了。)
执行命令如下:
[C] 纯文本查看 复制代码 javah -d E:\DownLoad\MyApplication4\app\src\main\jni com.hello.myapplication4.jniText
或者
[C] 纯文本查看 复制代码 javah -d ../jni com.hello.myapplication4.jniText
注:javah -d 生成路径 包名.类名
生成完成后可以在jni文件夹里看到生成的h文件。
v13
双击打开查看生成了相关代码
v14
(2).后面就可以继续在jni文件夹里创建c文件了。当然c++文件也行。
操作:New--C/C++ Source File,文件名合法随意取(后面设置时用到),格式有.c和.cpp两种格式选择
v15
添加实现代码:
[C] 纯文本查看 复制代码 #include "com_hello_myapplication4_jniText.h"
JNIEXPORT jstring JNICALL Java_com_hello_myapplication4_jniText_getStringMyName
(JNIEnv *env, jclass obj)
{
return (*env)->NewStringUTF(env,"pady");
}
上面的c文件就完成了。下面就可以着手开始配置然后生成so文件。
对,Activity层还得调用它。先给TextView设置个id先
v16
v17
5.配置文件属性并生成so
gradle.properties添加module
[C] 纯文本查看 复制代码 ndk{
moduleName "loadNewSo"
}
gradle.properties这个配置文件有什么作用?刚好找到一个解释:
在项目编译过程中,gradle.properties配置的值会被编译解析,其作为配置文件使用是很有必要的
添加好了,点一下sync now检查一下。
v18
然后是配置build.gradle添加节点。注意,android studio有两个build.gradle。需要操作的是app层里面的build.gradle,不是外面那个build.gradle。
不是外面的。不是外面的,不是外面的。
在defaultConfig节点添加以下内容:
[C] 纯文本查看 复制代码 externalNativeBuild{
cmake{
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'//生成多个版本的so文件
}
}
v19
在android节点添加以下内容(这个是在android节点里面,在defaultConfig节点外面):
[C] 纯文本查看 复制代码 externalNativeBuild{
cmake{
path "CMakeLists.txt"//设置所要编写的C源码位置,以及编译后so文件的名字
}
}
v20
完整内容如下:
[C] 纯文本查看 复制代码
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.hello.myapplication4"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild{
cmake{
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'//生成多个版本的so文件
}
}
}
externalNativeBuild{
cmake{
path "CMakeLists.txt"//设置所要编写的C源码位置,以及编译后so文件的名字
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
到上完的配置完成后,还差一个东西就可以开始生成so文件了。就是CMakeLists.txt.现在需要创建一个CMakeLists.txt。它用来做什么。做一些生成配置。
在app层里创建CMakeLists.txt.
操作如下:选中app,New---File,输入CmakeLists.txt,回车。这样就生成了一个txt文档
点击CMakeLists.txt,填写以下内容
[C] 纯文本查看 复制代码 # For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
#CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
# 设置so文件名称.
loadNewSo
# Sets the library as a shared library.
SHARED
# 设置这个so文件为共享.
# Provides a relative path to your source file(s).
# 设置这个so文件为共享.
src/main/jni/getName.c)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
# 制定目标库.
loadNewSo
# Links the target library to the log library
# included in the NDK.
${log-lib} )
上面就是所有内容了。需要修改的只有几部地方
第一处是add_library这里。括号里,第一个是loadLibrary的模块名称,即生成的名称会是这个。第二个是设置共享否,第三个是c源文件路径,之前创建的是getName.c,所以填写src/main/jni/getName.c
最后一处target_link_libraries,第一个也是loadLibrary的模块名称loadNewSo。这里要与jniText里面LoadLibrary的名称一致。
最后一步:Build--ReBuild Project
生成成功了。在路径build\intermediates\cmakes\debug\obj处,查看有没有生成各版本的so文件。如下图:
v21
生成的debug.apk在build-outputs-apk--debug
右键show in explorer。打开文件夹,安装apk。
运行效果如下:
v22
总结:其实整个过程并不复杂,就是java层创建jni类,生成class文件。再把java层的jni类生成c/c++的.h文件,再创建一个.c或.cpp源文件实现它。最后就是配置生成so.
===============================================================================================================
出现的问题:
在生成.class文件的操作过程中,除了rebuild外,还有一个javac手动命令。但是这个操作生成的class文件存放的路径可能导致出现下一步操作错误。
在java层里添加一个新类,不用rebuild生成class。手动javac生成。
在terminal里cd到E:\DownLoad\MyApplication5\app\src\main\java\com\hello\myapplication5
然后执行命令:javac myJNIutils.java
生成了myJNIutils.class
f1
它生成后,是和同名的java文件放在一起。
接着要生成h文件。
cd到java层。
执行下面的命令:
[C] 纯文本查看 复制代码 E:\DownLoad\MyApplication5\app\src\main\java>javah -jni com.hello.myapplication5.myJNIutils
包名是:com.hello.myapplication5
类名是:myJNIutils
结果出现找不到类的提示了。反复确认,包名.类名。没错啊。但就是不成功。
f2
为了证明是不是因为同名造成的冲突,把生成的.class文件去掉。改用rebuild生成class。
f3
再执行命令:
[C] 纯文本查看 复制代码 E:\DownLoad\MyApplication5\app\src\main\java>javah -jni com.hello.myapplication5.myJNIutils
结果成功生成h文件。
f4
原因是什么搞不清楚了,应该是这个4.1版本的android studio,出现同名不同格式的文件,执行javah时,造成冲突?
==================================================================================================================
上面是一系列的手动操作。有助于对过程的理解。下面是android studio另一个简洁操作。只需要改改名称就可以完成了。
1.新建工程的时候,选择Native C++
n1
点Next,再选择C++标准。这里选择C++11
n2
点finish后,就可以看到很多已经创建好了,包括CMakeLists.txt,cpp文件都自动创建好了。
n3
只需要修改几个地方的名称就可以直接点ReBuild了。
Activity里,可以修改用于生成,加载的名称
n4
然后就是修改CMakeLists.txt里面的内容。cpp的名称可以右键--Refactor---Rename,然后输入要修改的名称,确定。
在CMakeLists.txt里面,需要修改的就是上面改过的名称,还有cpp名称。
n5
n6
简单的总共就修改3个地方,然后直接Build---Rebuild project。
n7
非常简单的操作。如果需要修改native,可以直接在activity里修改或者添加,在cpp里直接实现。方法只需要修改这两处就可以了。
总结:只需修改三个地方,activity--修改生成模块名称,修改cpp名称,修改CMakeLists.txt,把前面修改过的,在这里面都要修改成相同的名称。
参考文档:
https://www.52pojie.cn/thread-706568-1-1.html
https://blog.csdn.net/zhangmiaoping23/article/details/74390713
https://www.jianshu.com/p/997ae19a5fae
https://www.cnblogs.com/dubo-/p/7724059.html
最后不得不想说的是,写代码五分钟,写贴子五小时。实在太累人了。
|