图片 24

入门实例,运转NDK程序时踩过的那几个坑

一、基础概念

率先,笔者把Windows下NDK开编写JNI的流水生产线说一下,在方方面面流程中着至关心珍重要出里面包车型地铁一些坑,防止其余开荒者步作者后尘。
一、NDK遇到搭建
其一就不在这里说了,不是注重,大家自行google、百度。
2、JNI开发
1)创建Android项目。
二)查看项目local.properties中参与ndk和sdk的路径是还是不是科学

对于笔记三中的HelloWorld程序,编写翻译时借使输入javac
HelloWorld.java就能够生成类文件;再用java HelloWorld就能够运营。

  (一)面向对象的三大特色:      一.封装         二.一而再         
3.多态

     ndk.dir=/Users/userName/AndroidStudioProjects/ndk/android-ndk-r13b
      sdk.dir=/Users/userName/Library/Android/sdk

万壹源程序行使了包注解,编写翻译运转时要采用一些参数才干学有所成编写翻译或运维。上边用轻巧的事例说多美滋(Dumex)下。

  (贰)封装:隐藏达成细节,对外提供公共的访问格局(接口)。

3)配置项目下的gradle.properties文件,表示大家要选择NDK进行支付。

 

     封装的体现之1:将品质都私有化,对外提供相应的  setXXX   
getXXX  方法来走访。

android.useDeprecatedNdk=true

作者: 蝉蝉

     封装的裨益:

4)在moudle根目录下的的build.gradle中的defaultConfig标签内部里参预如下代码

请尊重作者劳动成果,转发请在标题注脚“转发”字样,并注解原著链接:

          一.抓牢安全性。不一样意直接待上访问细节。并因此公共的法子来走访,实现可控。

ndk{    
moduleName "secret"   //生成的so文件名字,跟System.loadLibrary("secret");  中的名字相对应  
abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种平台下的so库,
// 还可以添加 'x86_64', 'mips', 'mips64'
}

 

          二.加强了易用性。

5)编写jni代码

 

          三.升高了复用性。

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();
}

 有五个源文件,分别为StringCompareEmp.java和StringFunction.java,多少个源文件都有包表明语句”package
StringTest;”,即它们同属StringTest包,编写翻译生成的类公事要放权StringTest文件夹下。具体代码如下:

          四.割裂的变型。中期应用于分层设计。

6)生成.h头文件
直接使用Android Studio
背后部分的Terminal,暗许命令行窗口路线已经在日前项目,进入到app/src/main/java目录,输入以下命令(固定格式:javah
-jni 包名+类名),那几个一般的博客网址告诉我们的吩咐

 注:包名最棒小写…

二、实例代码

javah -jni com.david.ndktest.MainActivity

图片 1图片 2

  需求用私有化举行打包将Person的年华打字与印刷出来。

好了,这里存在多少个坑,首先第三个坑
1、Terminal提示 错误:找不到‘com.david.ndktest.MainActivity’类文件
原因:java未有蕴含 Android SDK中的 java 文件
化解办法:
找到Android
sdk目录,找到platforms文件夹,进入“android-20”(大概别的版本也行),然后找到
“android.jar” 文件,将其增多到Computer境况变量的 CLASSPATH
中。重启一下Terminal只怕AndroidStudio
再也实践“javah -jni
com.david.ndktest.MainActivity”,好了此间又出了一个小坑

二、Terminal提醒 错误:编码GBK的不可映射字符
原因:由于JDK是国际版的,在编写翻译的时候,借使大家尚无用-encoding参数钦命大家的JAVA源程序的编码格式,则javac.exe首先获得我们操作系统暗许使用的编码格式。
化解办法:应该接纳-encoding参数指明编码格局,如:javah -jni -encoding
UTF-八 com.david.ndktest.MainActivity

/*
 * compareTo()返回参与比较的两个字符串的ascii码差值
 * Object类....
 */
package StringTest;
public class StringCompareEmp{
    public void printCompare() {
//    public static void main(String[] args) {
        int indexI = 3;
        String firststr = "This String";
        String secondstr = "this string";
        Object objstr = firststr;

        String str1 = "a";
        String str2 = "b";
        String str3 = "abc";
        String str4 = "ab";
        String str5 = "abcde";
        String str6 = "a";

        System.out.println( firststr + " 第" + (indexI+1) + "个字符是:" + firststr.charAt(indexI) );//char charAt(int)
        System.out.println( firststr + " 与  " + secondstr + " 按字典顺序比较,结果为:" + firststr.compareTo(secondstr) );//int compareTo(String),按字典顺序比较两个字符串
        System.out.println( firststr + " 与  " + secondstr + " 按字典顺序比较并且不考虑大小写,结果为:" + firststr.compareToIgnoreCase(secondstr) );//int compareToIgnoreCase(String),按字典顺序比较两个字符串,不考虑大小写
        System.out.println( firststr + " 与  " + objstr + " 比较,结果为:" + firststr.compareTo(objstr.toString()) );//int compareTo(Object),把字符串与另一个对象比较


        // 两个字符串首字母不同,则返回首字母的ascii差值
        System.out.println( "两个字符串首字母不同,则返回首字母的ascii差值:" );
        System.out.println( str1 + " 与 " + str2 + " 按字典顺序比较,结果为: " + str1.compareTo(str2) );
        System.out.println( str4 + " 与 " + str2 + " 按字典顺序比较,结果为: " + str4.compareTo(str2) );
        System.out.println( str5 + " 与 " + str2 + " 按字典顺序比较,结果为: " + str5.compareTo(str2) );

        //首字母相同,则比较下一个字符,直到有不同的为止或比较到字符串最后一个字符,返回该不同的字符的ascii差值;如果两字符串长度不同,可以参与比较的字符又完全一样,返回两个字符串的长度差值
        System.out.println( "首字母相同,则比较下一个字符,直到有不同的为止或比较到字符串最后一个字符,返回该不同的字符的ascii差值;如果两字符串长度不同,可以参与比较的字符又完全一样,返回两个字符串的长度差值:" );
        System.out.println( str4 + " 与 " + str1 + " 按字典顺序比较,结果为: " + str4.compareTo(str1) );
        System.out.println( str1 + " 与 " + str6 + " 按字典顺序比较,结果为: " + str1.compareTo(str6) );
        System.out.println( str3 + " 与 " + str1 + " 按字典顺序比较,结果为: " + str3.compareTo(str1) );
        System.out.println( str3 + " 与 " + str4 + " 按字典顺序比较,结果为: " + str3.compareTo(str4) );
        System.out.println( str5 + " 与 " + str1 + " 按字典顺序比较,结果为: " + str5.compareTo(str1) );
        System.out.println( str5 + " 与 " + str4 + " 按字典顺序比较,结果为: " + str5.compareTo(str4) );
        System.out.println( str5 + " 与 " + str3 + " 按字典顺序比较,结果为: " + str5.compareTo(str3) );
        System.out.println( str3 + " 与 " + str5 + " 按字典顺序比较,结果为: " + str3.compareTo(str5) );

    }
}
 1 class Person
 2 {
 3     private int age;
 4     //对私有的数据,可以通过方法的方式对其进行访问。
 5     public void setAge(int a)
 6     {
 7         //可以对数据进行控制。
 8         if(a<0 || a>130){
 9             throw new RuntimeException(a+"数值的错误的");    //抛出异常
10             }else{
11                 age = a;
12             }
13         }
14     
15     public int getAge()    {
16         return age;
17     }
18     void speak(){
19         System.out.println("age="+age);
20     }
21     
22 }
23     
24 class PersonDemo
25 {
26     public static void main (String[] args){
27         //创建Person的对象,调用Person的属性和行为。
28         Person p = new Person();
29         p.setAge(20);
30         p.speak();
31     }
32 }

好了,以上几个坑化解掉了,再度实施命令javah -jni -encoding UTF-8com.david.ndktest.MainActivity
这一次Terminal未有其余输出,表达我们曾经打响的在java文件夹下生成了com.david.ndktest.MainActivity.h头文件,如下:

View Code

3、代码运转

图片 3

图片 4图片 5

  如若对Java代码实行编写翻译时出现编码GBK的不可映射字符的难点时,应该利用-encoding参数指明编码格局:javac
-encoding UTF-8 XX.java

头文件.png

package StringTest;
public class StringFunction{
    public static void main(String[] args) {
//        StringCompareEmp strcmp = new StringCompareEmp();
//        SearchLastString secstr = new SearchLastString();

//        strcmp.printCompare();
//        secstr.printSearch();
        System.out.println("function");
    }
}

  1.输入指令:javac -encoding UTF-八 Person德姆o.java   
(编译Person德姆o文件中的代码)

大家把头文件名字改成secret.c(一定要改成.c文件技能编写翻译), Build
->Build Project
一下工程,不过难点来了,我们并不曾在等级次序的build\intermediates\目录下看到ndk文件夹,表达编写翻译之后并未有成形对应的so库,这是第七个坑
三、原因:secret.c文件在java文件夹下都以java源代码,AndroidStudio不能辨识在这之中的c文件,自然不会去编写翻译。
消除办法:将指令:javah -jni -encoding UTF-8com.david.ndktest.MainActivity 改成
javah -jni -d ../jni -encoding UTF-八 com.david.ndktest.MainActivity
那样就能够在java目录的上一流main目录下自动生成jin文件夹,并将转移的头文件放到里面。
好了,大家在举办一下发令,然后把jni文件夹下生成的公文的公文名改成secret.c,再Build
->Build Project
一下,在build\intermediates\ndk文件夹下看到了打包好的so库,好了,到这里终于松了一口气。
七)假设6)不大概举办,可以直接从此外地方复制头文件进来,恐怕本人依据格式写一个.c的文本,放到src/main/jni目录下;

View Code

  二.输入指令:java PersonDemo  (实行PersonDemo文件中的代码)

如遭受编写翻译错误,如下:

图片 6

图片.png

 

  图片 7

化解办法:

图片 8

图片.png

增进红线标识的代码即可,因为jni每一回运营都会在build\intermediates\ndk中机动编写翻译生成3个Android.mk文件,那样我们在此之前在jni中手动编写翻译的Android.mk就从未有过功效了,加上那句后就不会自行编写翻译了,用的是我们和好的mk文件。

八)在secret.c文件里福如东海stringFormat方法如下:

图片 9

图片.png

9)编写翻译C/C++文件,输出
.so文件(第陆步无法生成so文件条件下,选取如下方式)
壹、cd命令进入创立的jni文件夹下;
2、执行ndk-build命令

1,在windows系统下开拓命令行,进入到源文件所在目录

 

若出现如下图所示错误:

图片 10

图片.png

一,编写翻译StringCompareEmp. java,输入javac -d . StringCompareEmp.
java,这里运用参数”-d
.”,表示在当前目录下生成包文件夹,并把类公事放到该文件夹下
;不加-d参数的话,在当前目录下能生成类文件,但运营时会提示找不到或无法加载类文件,原因在于,JVM要到包文件夹下寻觅类公事,而此时只在当前目录下有类公事,那样就能出错。

化解办法:

在build\intermediates\ndk\debug目录下找到Android.mk文件,将其复制黏贴到src\main\jni文件加下,再度实施ndk-build就能够自动生成so文件,可在src\mian\libs目录下查看。

运营该工程,OK,native方法调用成功,效果如下:

图片 11

图片.png

注意:
一、build.gradle 里面包车型客车moduleName “xxx”
跟System.loadLibrary(“xxx”);的名字要对应起来,可是c文件的名字可以自定义,不断定要平等。
2、相关参照他事他说加以考查资料来自
http://blog.csdn.net/qq\_36788768/article/details/59487103
http://www.2cto.com/kf/201706/648968.html。

命令如图1所示:

图片 12

图1

 

出现“编码GBK的不可映射字符”的荒唐,原因是,系统暗许的编码格式与源文件编码格式分裂,编写翻译时不曾显式钦命源文件编码格式的话,JDK会把源文件从系统默许编码格式调换为Java暗中认可编码格式,那样就应时而生乱码了。

图片 13

图2

 

②,编译时丰裕参数“-encoding UTF-八”就能够准确编译了。

图片 14

图3

 

编译通过后,查看目录,开采当前目录下生成StringTest文件夹,StringCompareEmp.
class文件就置身该文件夹下。

图片 15

 图4

 

三,用同样的指令编写翻译StringFunction. java

图片 16

图5

 

翻看StringTest文件夹,三个类公事都存放在那之中。

图片 17

图6

 

肆,用命令java StringTest. StringFunction实践类公事,成功输出

图片 18

 图7

 

2,改变源程序StringFunction.
java,修改后的源文件如下:

图片 19图片 20

package StringTest;
public class StringFunction{
    public static void main(String[] args) {
        StringCompareEmp strcmp = new StringCompareEmp();
//        SearchLastString secstr = new SearchLastString();

        strcmp.printCompare();
//        secstr.printSearch();
        System.out.println("function");
    }
}

View Code

 

 1,把前面编写翻译生成的类公事删除,重新编写翻译那三个源文件。

先编写翻译StringFunction.
java,出现”找不到符号”的荒谬,原因在于,StringFunction.
java实例化了三个StringCompareEmp对象,而此刻StringCompareEmp还未编写翻译,调度编写翻译顺序,先编写翻译StringCompareEmp.
java,再编写翻译StringFunction. java就足以了。详见图⑧:

图片 21

 图8

 

2,输入命令java StringTest. StringFunction,成功施行,详见图九:

图片 22

 图9

 

三,上边的例子都把源文件放在当前目录下了,源文件位于别的地点呢?

一,要用到另贰个参数”-cp
源文件所在的路线”,表示要编写翻译的源文件在所示路线下
;此时”-d .”也改为”-d
源文件所在路线”,表示在源文件所在路线下生成包文件夹,并把类公事放在该文件夹下。详见图10:

图片 23

 图10

 

②,输入java -cp StringFunction\src StringTest.
StringFunction,成功实施,详见图1壹:

图片 24

 图11

 

总结:

壹,源文件中有包表明语句时,编写翻译时要动用“-d
路线”参数,表示编写翻译时自动生成与包同名的文书夹,并把类公事放到该文件夹下,指标是运营时让JVM能够在包文件夹下找到要加载的类公事。

贰,源文件中有汉语字符时,要编译时要用“-encoding
UTF-8”参数,不然编写翻译有乱码。

3,当要编写翻译的多少个源文件有引用关系时,先编写翻译不引用别的类的文本,后编译有引用的文本,不然编译会出现“找不到字符”的失实。

4,当源文件在非当前路径下时,编译或运维时要使用“-cp
源文件所在路线”参数,表示到所给路线下搜寻源文件。

 

待学习:

①,用javac *.java解决类之间互相引用时,编写翻译顺序难点

2,JVM怎么样加载类

三,变量,代码块等在内部存款和储蓄器如何存款和储蓄