欧雨鹏 发表于 2020-10-21 17:11

折腾折腾用android studio去编写so

本帖最后由 欧雨鹏 于 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版本

最后,还是没看到前辈们贴子里提到的LLDB这个玩意。

但这不影响。起码最后操作成功。

android studio版本 4 .1SDK TOOLS;默认添加了cmake,NDK没有LLDB这个东西。主要用到了NDK和CMAKE
创建工程用到的android 5.1版本

先来一遍操作过程再说。

1.把工程创建起来


2.在java层的com.xxxx包里面添加一个类,这个类要用做什么?加载so,和声明native。简单的写法,写太复杂怕搞不定。
选中com.hello.myapplication4,右键--new---java class

弹出这么一个对话框,没有确定按钮,好吧。这肯定是一个回车就搞定的玩意,上面输入名称就可以了:jniText

然后又弹出这么一个对话框。虽然不是很想理它。但是还是搜了一下。

(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类


添加代码,代码如下:
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文件。



4.上面的jni文件对native层的声明已经处理完毕。接下来是不是要实现它了。所以要创建c/c++的实现文件。这里选择用c文件。
(1)先生成一个与native相关的h文件。
就是刚刚创建的jniText类的java层。
在main层创建一个Jni文件夹:New---Folder---JNI Folder


alt+f12调出terminal,如果有就不用了。cd到java层。因为要生成h文件的是com.hello.myapplication4.jniText这个类。所以要进入java层

执行javah命令,生成h文件到jni文件夹里。(注:这里操作的时候,提示:编码GBK的不可映射字符。是因为注释是中文的。去掉了就正常了。)
执行命令如下:
javah -d E:\DownLoad\MyApplication4\app\src\main\jni com.hello.myapplication4.jniText
或者
javah -d ../jni com.hello.myapplication4.jniText
注:javah -d 生成路径 包名.类名
生成完成后可以在jni文件夹里看到生成的h文件。

双击打开查看生成了相关代码

(2).后面就可以继续在jni文件夹里创建c文件了。当然c++文件也行。
操作:New--C/C++ Source File,文件名合法随意取(后面设置时用到),格式有.c和.cpp两种格式选择


添加实现代码:
#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先



5.配置文件属性并生成so

gradle.properties添加module
ndk{
    moduleName "loadNewSo"
}
gradle.properties这个配置文件有什么作用?刚好找到一个解释:
在项目编译过程中,gradle.properties配置的值会被编译解析,其作为配置文件使用是很有必要的
添加好了,点一下sync now检查一下。


然后是配置build.gradle添加节点。注意,android studio有两个build.gradle。需要操作的是app层里面的build.gradle,不是外面那个build.gradle。
不是外面的。不是外面的,不是外面的。
在defaultConfig节点添加以下内容:
externalNativeBuild{
      cmake{
            
            abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'//生成多个版本的so文件
      }
    }



在android节点添加以下内容(这个是在android节点里面,在defaultConfig节点外面):
externalNativeBuild{
      cmake{
            path "CMakeLists.txt"//设置所要编写的C源码位置,以及编译后so文件的名字
      }
    }



完整内容如下:

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,填写以下内容
# 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文件。如下图:


生成的debug.apk在build-outputs-apk--debug
右键show in explorer。打开文件夹,安装apk。
运行效果如下:


总结:其实整个过程并不复杂,就是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

它生成后,是和同名的java文件放在一起。
接着要生成h文件。
cd到java层。
执行下面的命令:
E:\DownLoad\MyApplication5\app\src\main\java>javah -jni com.hello.myapplication5.myJNIutils
包名是:com.hello.myapplication5
类名是:myJNIutils
结果出现找不到类的提示了。反复确认,包名.类名。没错啊。但就是不成功。

为了证明是不是因为同名造成的冲突,把生成的.class文件去掉。改用rebuild生成class。

再执行命令:
E:\DownLoad\MyApplication5\app\src\main\java>javah -jni com.hello.myapplication5.myJNIutils

结果成功生成h文件。


原因是什么搞不清楚了,应该是这个4.1版本的android studio,出现同名不同格式的文件,执行javah时,造成冲突?

==================================================================================================================

上面是一系列的手动操作。有助于对过程的理解。下面是android studio另一个简洁操作。只需要改改名称就可以完成了。
1.新建工程的时候,选择Native C++


点Next,再选择C++标准。这里选择C++11


点finish后,就可以看到很多已经创建好了,包括CMakeLists.txt,cpp文件都自动创建好了。


只需要修改几个地方的名称就可以直接点ReBuild了。
Activity里,可以修改用于生成,加载的名称


然后就是修改CMakeLists.txt里面的内容。cpp的名称可以右键--Refactor---Rename,然后输入要修改的名称,确定。
在CMakeLists.txt里面,需要修改的就是上面改过的名称,还有cpp名称。



简单的总共就修改3个地方,然后直接Build---Rebuild project。


非常简单的操作。如果需要修改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

最后不得不想说的是,写代码五分钟,写贴子五小时。实在太累人了。

欧雨鹏 发表于 2020-10-21 18:00

carltian 发表于 2020-10-21 17:38
c++感觉比java复杂多了,还是java顺手

是不是看过多这篇文章觉得调用C/C++太复杂?其实那是早期的写法。IDE更新很快的。像VS也是更新很快。现在用VS,QT,这些开发都不那么痛苦。很多都会封装好。只需要简单的使用就可以了。就像android studio现在这个新建的native C++,也是帮我们处理好了,不需要像以前那样配置这个配置那个繁锁过程。IDE更新到现在,操作越来越简洁和方便了。只要熟悉它就不会有很大问题。

欧雨鹏 发表于 2020-10-21 23:18

战忽局滑稽 发表于 2020-10-21 20:21
楼主,你那android studio 什么版本的,我装的这个一直报这个,网上查了很久都解决不了

你这是东西还没下完全吧。中间那句话已经写得很明白了,项目没有成功同步。一般刚刚安装完android studio时,刚创建项目,会自动download一些东西的。
你可以试着设置一下SDK版本,TOOLS。
关闭android studio,再重新打开项目。网络保持顺畅 ,会有进条度,左下角会显示需要需要download的东西。
或者直接update版本。

jeanbood 发表于 2020-10-21 17:17

是个好东西

Luckyu920 发表于 2020-10-21 17:29

技术贴,受教了

wscb 发表于 2020-10-21 17:36

技术贴,受教

pisczx 发表于 2020-10-21 17:37

大神们的世界。

Jabez 发表于 2020-10-21 17:38

wscb 发表于 2020-10-21 17:36
技术贴,受教

正好用上感谢楼主

carltian 发表于 2020-10-21 17:38

c++感觉比java复杂多了,还是java顺手

tititui 发表于 2020-10-21 17:38

好的 学习一下

start01983 发表于 2020-10-21 17:38

谢谢楼主!!

Jabez 发表于 2020-10-21 17:38

顶 ~~~~ 正好用上感谢楼主   
页: [1] 2 3 4 5 6
查看完整版本: 折腾折腾用android studio去编写so