Android 开发 DNK开发将.c文件打包成os
<h1>前言</h1><p> 不废话太多,Java与C之间联系的JNI的概念,这个要了解可以参考下面这个博客:</p>
<p> https://www.jianshu.com/p/87ce6f565d37</p>
<p> </p>
<p> 此博客只说明如何将.C文件通过NDK打包成so库并且使用的一个简单demo.</p>
<p> </p>
<h1>第一步 创建用于引用本地os库的Java工具类</h1>
<h3>目录</h3>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601142531690-1195243862.png" alt=""></p>
<h3>工具类代码</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> zq.ndkdemo;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> NDKTools {
</span><span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> {
System.loadLibrary(</span>"ndkdemomk-jni");<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里的"ndkdemomk-jni"是下面.mk文件里局部模块的值,这个到后面我会解释</span>
<span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">native</span> String getNDKcontent();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">您在Java里调用so库的静态方法</span>
}</pre>
</div>
<p> </p>
<h1>第二步 将Java工具类打包成.h文件</h1>
<h3>打开Android studio的Terminal准备用javah打包.h文件, cd进入到app/src/目录下 然后输入 javah -o ndkdemoHFile.h -jni -classpath ./main/java/ zq.ndkdemo.NDKTools 命令.(下面会解释这条命令)</h3>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601143935547-2044239236.png" alt=""></p>
<h3>解释一下这行命令</h3>
<div class="cnblogs_code">
<pre>javah -o ndkdemoHFile.h -jni -classpath ./main/java/<span style="color: rgba(0, 0, 0, 1)">zq.ndkdemo.NDKTools
javah </span>-o 你要打包的.h文件名加后缀 -jni -classpath 中间的路径app包名+工具类名</pre>
</div>
<h3>输入这条命令后,如果没报错,你就会在src目录下获得您的.h文件,如下图:</h3>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601144515834-2004068826.png" alt=""></p>
<p>打开.h可以看到:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> DO NOT EDIT THIS FILE - it is machine generated </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
#include </span><jni.h>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Header for class zq_ndkdemo_NDKTools </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
#ifndef _Included_zq_ndkdemo_NDKTools
</span><span style="color: rgba(0, 0, 255, 1)">#define</span> _Included_zq_ndkdemo_NDKTools<span style="color: rgba(0, 0, 0, 1)">
#ifdef __cplusplus
</span><span style="color: rgba(0, 0, 255, 1)">extern</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">#endif</span>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Class: zq_ndkdemo_NDKTools
* Method: getNDKcontent
* Signature: ()Ljava/lang/String;
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
JNIEXPORT jstring JNICALL Java_zq_ndkdemo_NDKTools_getNDKcontent
(JNIEnv </span>*<span style="color: rgba(0, 0, 0, 1)">, jclass);
#ifdef __cplusplus
}
</span><span style="color: rgba(0, 0, 255, 1)">#endif</span>
<span style="color: rgba(0, 0, 255, 1)">#endif</span></pre>
</div>
<p> </p>
<h1>第三步 创建jni文件夹并且将.h文件移入</h1>
<h3>在main目录下创建一个jni文件夹</h3>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601145151619-416074833.png" alt=""></p>
<p> </p>
<h3>将我们打包好的.h文件移动到jni文件夹里</h3>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601145324230-425585304.png" alt=""></p>
<h1>第四步 创建c语言函数文件</h1>
<p>进入到jni文件夹里,点击创建任意名称加.c后缀的文件</p>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601145633538-599021190.png" alt=""></p>
<p>打开写入代码如下:</p>
<div class="cnblogs_code">
<pre>#include <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ndkdemoHFile.h</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
JNIEXPORT jstring JNICALL Java_zq_ndkdemo_NDKTools_getNDKcontent
(JNIEnv </span>*<span style="color: rgba(0, 0, 0, 1)">env, jobject obj){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (*env)->NewStringUTF(env,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Hellow World,这是NDK的第一行代码</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p> 注意!代码里最好别写注释特别是中文注释.</p>
<h4>#include <span style="color: rgba(128, 0, 0, 1)">"<span style="color: rgba(128, 0, 0, 1)">ndkdemoHFile.h<span style="color: rgba(128, 0, 0, 1)">"</span></span></span> </h4>
<p>这行代码引用的就是.h文件名称</p>
<h4> <span style="color: rgba(0, 0, 0, 1)">JNIEXPORT jstring JNICALL Java_zq_ndkdemo_NDKTools_getNDKcontent </span></h4>
<p><span style="color: rgba(0, 0, 0, 1)">这行代码中 <span style="color: rgba(0, 0, 0, 1)">jstring</span> 为返回值</span></p>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"> Java_zq_ndkdemo_NDKTools_getNDKcontent 为 Java + 路径 + 类名 + 方法名称</span><br></span></p>
<p> </p>
<h1>第五步 创建.mk文件 </h1>
<h3> 在jni目录下创建一个叫Android.mk的文件</h3>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601154405078-706115241.png" alt=""></p>
<h3>内容如下:</h3>
<div class="cnblogs_code">
<pre>LOCAL_PATH := $(call my-<span style="color: rgba(0, 0, 0, 1)">dir)
include $(CLEAR_VARS)
LOCAL_MODULE :</span>= ndkdemomk-<span style="color: rgba(0, 0, 0, 1)">jni
LOCAL_SRC_FILES :</span>=<span style="color: rgba(0, 0, 0, 1)"> ndkdemoCFile.c
include $(BUILD_SHARED_LIBRARY)</span></pre>
</div>
<h3><code>LOCAL_PATH := $(call my-dir)</code>:</h3>
<p>每个Android.mk文件必须以定义开始。它用于在开发tree中查找源文件。宏<code>my-dir</code>则由Build System 提供。返回包含Android.mk目录路径。</p>
<div>
<h3><code>include $(CLEAR_VARS)</code> :</h3>
<div><code>CLEAR_VARS</code>变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx。例如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等等。但不是清理LOCAL_PATH。这个清理是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能便面相互影响。</div>
<div>
<h3><code>LOCAL_MODULE := ndkdemomk-jni</code>:</h3>
<div>LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。Build System 会自动添加适当的前缀和后缀。例如,demo,要生成动态库,则生成libdemo.so。但请注意:如果模块名字被定义为libabd,则生成libabc.so。不再添加前缀。<strong>另外我们一开始写的Java工具类里的System.</strong><span style="font-style: italic"><strong>loadLibrary(</strong><span style="color: rgba(0, 128, 0, 1); font-weight: bold">"ndkdemomk-jni"<span style="color: rgba(0, 0, 0, 1)"><strong>);</strong> 就是这个值</span></span></span></div>
</div>
</div>
<h3><code>OCAL_SRC_FILES := ndkdemCFile.c</code>:</h3>
<div>这行代码表示将要打包的C/C++源码。不必列出头文件,build System 会自动帮我们找出依赖文件。缺省的C++ 源码的扩展名为.cpp。</div>
<div>
<div>
<h3><code>include $(BUILD_SHARED_LIBRARY)</code>:</h3>
<div><code>BUILD_SHARED_LIBRARY</code>是Build System提供的一个变量,指向一个GUN Makefile Script。它负责收集自从上次调用<code>include $(CLEAR_VARS)</code>后的所有LOCAL_xxxxinx。并决定编译什么类型
<ul>
<li><code>BUILD_STATIC_LIBRARY</code>:编译为静态库</li>
<li><code>BUILD_SHARED_LIBRARY</code>:编译为动态库</li>
<li><code>BUILD_EXECUTABLE</code>:编译为Native C 可执行程序</li>
<li><code>BUILD_PREBUILT</code>:该模块已经预先编译</li>
</ul>
</div>
</div>
</div>
<h1>第六步 在build.gradle文件里添加部分代码</h1>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">android {
compileSdkVersion </span>28<span style="color: rgba(0, 0, 0, 1)">
defaultConfig {
applicationId </span>"zq.ndkdemo"<span style="color: rgba(0, 0, 0, 1)">
minSdkVersion </span>27<span style="color: rgba(0, 0, 0, 1)">
targetSdkVersion </span>28<span style="color: rgba(0, 0, 0, 1)">
versionCode </span>1<span style="color: rgba(0, 0, 0, 1)">
versionName </span>"1.0"<span style="color: rgba(0, 0, 0, 1)">
testInstrumentationRunner </span>"android.support.test.runner.AndroidJUnitRunner"
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要添加的部分</span>
<span style="color: rgba(0, 0, 0, 1)"> ndk{
moduleName </span>"ndkdemo-jni"<span style="color: rgba(0, 0, 0, 1)">
abiFilters </span>"armeabi-v7a", "x86"<span style="color: rgba(0, 0, 0, 1)">
}
}
buildTypes {
release {
minifyEnabled </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
proguardFiles getDefaultProguardFile(</span>'proguard-android-optimize.txt'), 'proguard-rules.pro'<span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要添加的部分</span>
<span style="color: rgba(0, 0, 0, 1)"> externalNativeBuild {
ndkBuild {
path </span>'src/main/jni/Android.mk'<span style="color: rgba(0, 0, 0, 1)">
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要添加的部分</span>
<span style="color: rgba(0, 0, 0, 1)"> sourceSets.main {
jni.srcDirs </span>=<span style="color: rgba(0, 0, 0, 1)"> []
jniLibs.srcDirs </span>= ['src/main/jniLibs'<span style="color: rgba(0, 0, 0, 1)">]
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要添加的部分</span>
<span style="color: rgba(0, 0, 0, 1)"> sourceSets{
main {
jni.srcDirs </span>=<span style="color: rgba(0, 0, 0, 1)"> []
}
}
}</span></pre>
</div>
<h1>第七步 检查Android studio是否已经下载配置过ndk</h1>
<p>点击local.properties打开</p>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601155347927-1092813497.png" alt=""></p>
</div>
<div> </div>
<div>查看是否有ndk</div>
<div>
<div class="cnblogs_code">
<pre>ndk.dir=/media/E/tools/SDK/androidsdklinux/ndk-<span style="color: rgba(0, 0, 0, 1)">bundle
sdk.dir</span>=/media/E/tools/SDK/androidsdklinux</pre>
</div>
<p>如果没有就需要进入到File >> Settings 里下载ndk</p>
</div>
<div><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601155539737-542453782.png" alt=""></div>
<div> </div>
<h1>第八步 编译SO文件</h1>
<p>在Android studio的工具栏里,点击Build >> clean Project 先清理一下之前的编译</p>
<p>在点击Build >> Rebuild Project 重新创建编译文件</p>
<p>然后可以打开下图所示路径,就可以看到我们的so文件了</p>
<p><img src="https://img2018.cnblogs.com/blog/1497956/201906/1497956-20190601160944036-71427501.png" alt=""></p>
<h1>第九步 调用工具类方法,run APP</h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> MainActivity <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> AppCompatActivity {
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> TextView mDemoText;
@Override
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onCreate(Bundle savedInstanceState) {
</span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDemoText </span>=<span style="color: rgba(0, 0, 0, 1)"> findViewById(R.id.demo_text);
String content </span>=<span style="color: rgba(0, 0, 0, 1)"> NDKTools.getNDKcontent();
mDemoText.setText(content);
}
}</span></pre>
</div>
<p>运行APP 即可.</p>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
<div style="text-align: center">
<p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/10959567.html </p>
<div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/10959567.html
頁:
[1]