Android之NDK开发一第一个测试

前两天研究了一下NDK开发,然而没有成功。今天兴趣盎然,再试试,不知道会不会成功,我将记录我在学习过程中遇到的一些困难,以及成功后的效果。
我当前的状态是以及学习了一段时间Android开发,用的环境是Android Studio(2.1.1),所以jdk与Android studio 的配置就不描述了,从NDK开始。

第一步:安装NDK

这里写图片描述
选择NDK点击应用,开始下载。
这里写图片描述
解压,卡这儿半小时了,怎么办?
停止,重新下载,还是卡这儿,好吧,安装软件有时候也得看运气,把安装的删除了,然后重启电脑,再下载,睡觉,第二天醒来,装好了,继续出发。

第二步:修改gradle

修改APP的gradle

//ndk编译生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //输出指定三种abi体系结构下的so库。
       }

我这样修改,后面的不加,看会出现什么情况:

 defaultConfig {
        applicationId "com.example.hj.hellojni"
        minSdkVersion 18
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

    }
    ndk{
        moduleName="hello_jni"
    }//主要是这句话
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

出错了,出现了如下错误:

Error:(15, 0) Gradle DSL method not found: 'ndk()'
Possible causes:<ul><li>The project 'HelloJNI' may be using a version of Gradle that does not contain the method.
<a href="open.wrapper.file">Open Gradle wrapper file</a></li><li>The build file may be missing a Gradle plugin.
<a href="apply.gradle.plugin">Apply Gradle plugin</a></li>

好吧,网上搜了一下,好像应该这样写:

 defaultConfig {
        applicationId "com.example.hj.hellojni"
        minSdkVersion 18
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        ndk{
            moduleName="hello_jni"
        }//写在这个地方
    }

这次还是出错了,不过错误不一样:

Error:(13, 0) NDK integration is deprecated in the current plugin.
<a href="http://tools.android.com/tech-docs/new-build-system/gradle-experimental">Consider trying the new experimental plugin</a><br><a href="useDeprecatedNdk">Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration</a>

按照意思添加即可,在gradle.properties中添加android.useDeprecatedNdk=true
好了,不报错了,开i始编写代码:

第三步:添加代码

在MainActivity这样写:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("hello_jni");
    }
    //加载so库,这儿的名字要与gradle声明的相同
    private native String getJNIString();//声明本地方法
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textview =(TextView)findViewById(R.id.hellowjni);
        textview.setText(getJNIString());
        Toast.makeText(MainActivity.this, getJNIString(), Toast.LENGTH_SHORT).show();
    }
}

好了,activity的编写就完成了,接下来该编写C语言代码了。

第四步:编写C语言代码并运行

前面声明本地方法后,方法名会高亮,如下:
这里写图片描述
按alt+回车,会得到如下效果,会自动创建一个文件,就是以添加静态代码块时的字符串为名,我想应该AS会直接生成函数的,但是并没有,可能还没更新吧,或者我的版本有点低:
这里写图片描述
先不管,我们自己来写,再回到MainActivity中,将鼠标放在方法名上:如下
这里写图片描述
这就是我们的C语言函数名,我们需要在前面生成的c文件中写程序。
具体如下:

#include <jni.h>
jstring Java_com_example_hj_hellojni_MainActivity_getJNIString( JNIEnv* env,jobject thiz ) {
    return (*env)->NewStringUTF(env, "Hello JNI !");
}

好了,C语言的编写也完成了,我们开始运行吧,运行结果如下,成功:
这里写图片描述

思考

1、我们的so库到哪去了?

运行成功,在工程中搜索so,找到了so文件的目录:
这里写图片描述
前面是这样配置gradle的:

    ndk{
        moduleName="hello_jni"
    }//主要是这句话

默认生成所有CPU平台的so库,所以这儿生成的so库比较多。
现在我们这样配置:

//ndk编译生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //输出指定三种abi体系结构下的so库。
       }

运行后得到的so库是这样的:
这里写图片描述
这儿lib与obj下都是so库,这儿找到了这样一句话:

As part of the build process, the files in the libs folder have been stripped of symbols and debugging information. So you'll want to keep two copies of each of your .so files: One from the libs folder to install on the Android device, and one from the obj folder to install for GDB to get symbols from.

也就是说,libs目录下生成的库是剥离了符号表与调试信息的,而obj下的库是带有调试信息的,我们都可以使用。

2、其他人怎么使用我写的so库?

记得使用百度地图sdk的时候导入过so库,好像记得是导入到libs下面。
照着它的样子,导入自己写的so库。
这里写图片描述
新建的一个module,取名test,它的MainActivity的代码和上面一个的相同,其他什么都不用管,包名是package com.example.test;。
然后运行,程序崩溃,报错如下:

09-09 09:38:26.361 1950-1950/com.example.test E/art: No implementation found for java.lang.String com.example.test.MainActivity.getJNIString() (tried Java_com_example_test_MainActivity_getJNIString and Java_com_example_test_MainActivity_getJNIString__)

找不到本地方法的实现,看报错的原因,它在找本地方法时是根据新建的module的包名找方法的。
so库中的方法名是Java_com_example_hj_hellojni_MainActivity_getJNIString
而在现在的module中找的是
Java_com_example_test_MainActivity_getJNIString
好吧,想想造成这个的原因是,新module的中调用本地方法时,会根据自己的包名来找本地方法,与so库中的方法名不同,当然就找不到本地方法的实现了,先这样做一个测试。

so库测试一

首先,建一个module,取名testLib,这个module不是app,而是Android Library,建成后,
第一步修改gradle,和上面相同,添加:

//ndk编译生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //输出指定三种abi体系结构下的so库。
       }

第二步创建类:

package com.example.testlib;
public class InitLib {
    static {
        System.loadLibrary("hello_jni");
    }
    //加载so库,这儿的名字要与gradle声明的相同
    public native String getJNIString();//声明本地方法
}

第三步创建C语言代码:步骤、代码和前面的都相同,注意c语言中的函数名即可。
第四步修改前面的test,将其中的MainActivity修改,注意testLib的引包:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textview = (TextView) findViewById(R.id.hellowjni);

        String s = new InitLib().getJNIString();
        textview.setText(s);
        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
    }
}

运行,想要的结果又出来了。
这里写图片描述
来看看我们的testLib
这里写图片描述
这儿它又生成了so库, 调用时,就是调用的这儿的so库,别人想用so库,只要把library的module给他,让他引入,然后就像test的MainActivity那样调用就行了。
但是,还没完,记得使用百度地图时,百度给的是so库和jar包, 给的不是library,想想也是,不是开源的,就应该给so库和jar包,而不是library。
现在,我们再做个实验。

so库测试二

将testLib生成的so库拷贝到test的libs下,结果如下:
这里写图片描述
然后,删除testLib中gradle中添加的代码:

//ndk编译生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //输出指定三种abi体系结构下的so库。
       }

删除添加的jni以及C语言文件
再运行试试,报错了…:

09-09 10:25:49.040 15302-15302/com.example.test E/AndroidRuntime: FATAL EXCEPTION: main
                                                                  Process: com.example.test, PID: 15302
                                                                  java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader[DexPathList[[dex file "/data/data/com.example.test/files/instant-run/dex/slice-support-annotations-23.4.0_0f6553860408c70fcbf9af741b0e3a91ead8e096-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_9-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_8-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_7-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_6-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_5-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_4-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_3-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_2-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_1-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_0-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-internal_impl-23.4.0_d11d62ff0469a851c3144084b7282ebc60ff68c1-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-support-vector-drawable-23.4.0_286fb307ef2e5e61244113450b8053e8fd8b844e-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-support-v4-23.4.0_df88297ba1abd7fa68c5b2511b53ff29b15c41f3-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-appcompat-v7-23.4.0_7cb187c95178878a86f245c4ef9f5bd7bb029aa1-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-animated-vector-drawable-23.4.0_90f456ae3bbbade7285eb6faea927e851bb72c6c-classes.dex"],nativeLibraryDirectories=[/vendor/lib, /system/lib, /vendor/lib, /system/lib]]] couldn't find "libhello_jni.so"
                                                                      at java.lang.Runtime.loadLibrary(Runtime.java:366)
                                                                      at java.lang.System.loadLibrary(System.java:988)
                                                                      at com.example.testlib.InitLib.<clinit>(InitLib.java:10)
                                                                      at com.example.test.MainActivity.onCreate(MainActivity.java:22)

找不到so文件,怎么办,百度,找到这么一句话:

apk安装时,系统会把apk中libs目录下armeabi的SO拷贝到应用的私有目录下。所以libs里没有放入SO,运行时肯定找不到SO。

但是我的确放进去了啊,怎么回事?
回头看看百度地图是怎么使用so库的。

在src/main/目录下新建jniLibs目录,工程会自动加载src目录下的so动态库,放入libBaiduMapSDK_vX_X_X_X.so

哦,原来如此,要放到jniLibs下面啊,那我前面那样做可以吗?可以的,只是工程并不会自动加载libs下的so,需在gradle编译时,通过加入代码: jniLibs.srcDir ‘libs’ 来说明so的路径为该libs路径才可以。
先来试试jniLibs的方法吧,移动文件:
这里写图片描述
移动后,再次运行,又出错了:

09-09 11:41:08.962 7913-7913/com.example.test E/art: No implementation found for java.lang.String com.example.testlib.InitLib.getJNIString() (tried Java_com_example_testlib_InitLib_getJNIString and Java_com_example_testlib_InitLib_getJNIString__)

又出现这个错误了,so库是找到了,并且需要的函数名称和我们testLib中的相同,但是找不到函数,唉,可能是编写C语言时直接将jni拷过去,函数名没改,导致函数名错误,不能偷懒啊!重复前面的工作,生成一次so库,删除testLib下的gradle配置,jni,运行成功。

接下来的任务就是将我们的testLib打包成jar就OK了,别人使用时,导入so库和jar文件就可以了。

总结(纯属个人理解,欢迎指教)

NDK开发,就是用C/C++编写代码,使得运行效率更高。编写的代码将打包成so库的形式来供我们使用,加载so库:
static {
System.loadLibrary(“hello_jni”);
}
在Android中,使用之前需要使用native关键字修饰方法,而C语言中实现,它的函数名是由当前引用的包名+方法名,”.”将用”_”代替来命名。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值