Android Studio之NDK环境搭建,so文件打包以及使用

HeidiMcGuin 8年前
   <p>关于ndk,jni的介绍请自行谷歌、百度。推荐查看网友的ndk系列文章,写的较为详细,深入 Android NDK开发 (该系列教程开发环境为win + Eclipse)</p>    <h3>本篇入门教程开发环境为mac + Android Studio + gradle 2.14.1,主要包含以下3块内容:</h3>    <ul>     <li>NDK环境搭建</li>     <li>编写第一个JNI项目</li>     <li>打包出动态so文件,在其他项目中使用</li>    </ul>    <p>一、NDK环境搭建</p>    <ol>     <li>从Android Studio安装(自行F墙)<br> 打开AndroidStudio,选择顶部工具条,Tools->Android->SDK Manager->SDK Tools->NDK 点击install</li>     <li> <p>自行下载ndk包</p> <p>1)国内推荐通过 AndroidDevTools镜像 下载,或者Google的官方网站下载Android NDK的安装包 <a href="/misc/goto?guid=4959635917924585496" rel="nofollow,noindex">https://developer.android.com/ndk/downloads/index.html</a></p> <img src="https://simg.open-open.com/show/c90dcd91e54f36af6be0383f1ce977f6.png"> <p>NDK r13b</p> <p>2)下载ndk包后解析到某个路径,打开Project Structure->设置 NDK location</p> <img src="https://simg.open-open.com/show/ed57ec1b71c957f5fdca3333178f7183.png"> <p>AndroidDevTools 下载</p> </li>     <li>ndk环境变量配置,我们需要使用到ndk-build命令<br> 打开终端 -> 输入 :vim ~/.bash_profile -> 加入ndk 包的路径(mac中环境变量之间以封号隔开)</li>    </ol>    <p>自此,ndk开发环境我们已经可以进行jni开发了</p>    <p>二、JNI开发</p>    <ol>     <li>创建android项目</li>     <li>查看项目local.properties中加入ndk和sdk的路径是否正确 <pre>  <code class="language-java">ndk.dir=/Users/userName/AndroidStudioProjects/ndk/android-ndk-r13b  sdk.dir=/Users/userName/Library/Android/sdk</code></pre> </li>     <li>配置项目下的gradle.properties文件,表示我们要使用NDK进行开发。 <pre>  <code class="language-java">android.useDeprecatedNdk=true</code></pre> </li>     <li>在moudle根目录下的的build.gradle中的defaultConfig标签内部里加入如下代码 <pre>  <code class="language-java">ndk{      moduleName "hello"       //生成的so文件名字,调用C程序的代码中会用到该名字      abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种平台下的so库,  // 还可以添加 'x86_64', 'mips', 'mips64'  }</code></pre> </li>     <li> <p>编写jni代码</p> <pre>  <code class="language-java">package com.david.ndktest;  -  public class MainActivity extends AppCompatActivity {     //使用静态代码块,表示我们要加载的资源文件为libsecret.so   static {       System.loadLibrary("secret");   }     @Override   protected void onCreate(Bundle savedInstanceState) {       super.onCreate(savedInstanceState);       setContentView(R.layout.activity_main);       TextView tv_msg = (TextView) findViewById(R.id.tv_msg);       tv_msg.setText(stringFromat());     }   //声明一个本地方法,用native关键字修饰   public native String stringFromat();  }</code></pre> </li>     <li> <p>生成.h头文件</p> <p>直接使用Android Studio 底部的Terminal,默认命令行窗口路径已经在当前项目,进入到app/src/main/java目录,输入以下命令(固定格式:javah -jni 包名+类名)</p> <pre>  <code class="language-java">javah -jni com.david.ndktest.MainActivity</code></pre> <p>为在对应包的根目录下生成.h文件,熟悉该函数名后,日常开发中可以不用生成.h文件</p> <img src="https://simg.open-open.com/show/be3ad6de0b3d2b8365e1e1a963c2b816.png"> <p>生成头文件</p> </li>     <li> <p>执行第5部的时候,对应native会提示找不到对应方法,快捷键 alt+enter 会生成对应jni文件夹,包含libName.c文件,此处MainActivity中的native方法还是会显示红色,但是不影响编译</p> <img src="https://simg.open-open.com/show/8d140b6ca045f1943881dac8e15ef205.png"> <p>生成.c文件</p> </li>     <li> <p>编译项目后会发现app/build中已经生成so文件,并且已经对应的cpu包就是我们在gradle中已经配置的,并且已经调用成功</p> <img src="https://simg.open-open.com/show/0884bd57ca38002f4d24604b357fff8b.png"> <p>build中生成对应so文件</p> <img src="https://simg.open-open.com/show/4773327cc22568e407aedb7d2f421644.png"> <p>成功调用native方法</p> </li>    </ol>    <p>自此我们的第一个JNI项目已经编写完毕</p>    <p>三、打包出动态so文件,在其他项目中使用</p>    <p>有时候我们的需求是这样的,我们把一些比较重要的业务逻辑封装到ndk内部,对java层只暴露接口。我们就需要打包出so文件,并且可能需要在其他项目中使用,下面将介绍so(符合JNI标准)文件的打包,以及在其他项目中如何正确的调用</p>    <ul>     <li>编写Android.mk文件,放到jni文件夹根目录,与.c文件同级</li>    </ul>    <pre>  <code class="language-java">LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  LOCAL_MODULE := secret //lib 名  LOCAL_SRC_FILES := \      /Users/userName/AndroidStudioProjects/NdkTest/app/src/main/jni/secret.c \ //.c文件名    include $(BUILD_SHARED_LIBRARY)</code></pre>    <ul>     <li> <p>使用ndk-build命令(需要配置ndk环境变量,参照第一步第3点),生成so文件</p> <p>进入到main目录后在terminal中输入命令,ndk-build工具便会帮我们打包出所有cpu平台so文件(目前不知道如何设置需要打包cpu平台)</p> <pre>  <code class="language-java">ndk-build</code></pre> <img src="https://simg.open-open.com/show/152ffc0af3565739fdaa1145cddae5c4.png"> <p>building so文件</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/88af383ab052b534d95861bffe520f30.png"></p>    <p>main根目录下生成lib,obj目录</p>    <ul>     <li>其他项目中使用该so文件      <ul>       <li>拷贝so文件到项目的main/jniLibs目录</li>       <li>==新建package,包名与类名以及方法名必须与生成so文件的类保持一致!==</li>       <li>使用方法与第二部一致,需要声明loadLibrary与native方法 <p><img src="https://simg.open-open.com/show/a9d7f4049202f8a3f0a793f529f0f0b3.png"></p> </li>      </ul> </li>    </ul>    <ul>     <li> <p>调用native方法</p> <pre>  <code class="language-java">com.keywea.duolintest;  -  public class MainActivity extends AppCompatActivity {   @Override   protected void onCreate(Bundle savedInstanceState) {       super.onCreate(savedInstanceState);       setContentView(R.layout.activity_main);       TextView tv_msg = (TextView) findViewById(R.id.tv_msg);       tv_msg.setText(com.david.ndktest.MainActivity.stringFromat());     }  }</code></pre> </li>    </ul>    <p>自此我们已经能够接入符合JNI标准的so库,重点在于包名,类名,方法名需要与so库保持一致,因此我们在提供so库的时候一定要记录详细的交互文档</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/fb29ebbf8415</p>    <p> </p>