zoukankan      html  css  js  c++  java
  • 二、Android NDK开发---从Hello Word学起

    一 、NDK目录简单介绍  

            在进行NDK开发之前,我们有必须熟悉一下NDK目录下包含哪些东西,以及这些东西对开发来说有什么作用?那么现在打开NDK的解压目录,查看一下解压目录下的文件:

    1)samples目录。这个目录包含了Google为NDK开发撰写的一些小例子,包括本地JNI开发,图片处理,多个库文件开发等等,这些例子虽小但面面俱到,能看懂samples目录下的小例子程序,那么对于NDK开发来说,就很好应付了。

    2)docs目录。这个目录下存放的都是Google给开发者提供的文档,指导开发者怎样在Android环境下进行NDK开发,这个非常重要。

    3)sources目录。由于Android是开源操作系统,作为Android的一部分的NDK,同样也是开源的,这个目录下存放的是NDK源码。

    4)platforms目录。里面存放的是当前ndk版本所支持的所有android平台的版本,做NDK开发的C代码也是可以指定由某个特定版本平台下编译,该platforms目录下存放的是不同版本所包含的C的库文件和头文件,不同版本有些微小的变化。

    5)prebuilt目录。这是提供给在Windows下开发ndk程序的一些工具集。

    6)build目录。里面存放大量的Linux编程脚本和Windows下的批处理文件,用来完成ndk开发中的交叉编译。 

    二、具体开发

    1,NDK开发步骤

            首先,我先列出NDK开发的简单步骤,然后再以此为大纲,用一个Hello World的实例讲述一下NDK开发:

    (1)创建一个android工程

    (2)JAVA代码中写声明native 方法 public native String helloFromJNI();

    (3)创建jni目录,编写c代码,方法名字要对应在c代码中导入jni.h头文件

    (4)编写Android.mk文件

    (5)Ndk编译生成动态库

    (6)Java代码load 动态库.调用native代码

    2,NDK开发具体实践

         下面就按照上述的步骤建立一个HelloWorld小案例来一步一步实现NDK开发

    2.1,创建一个Android工程,并且在Java代码中声明一个native方法:
    package com.example.ndkdemo01;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
        static{
            System.loadLibrary("Hello");  //System.loadLibrary(String 文件名);是用来加载动态库的方法,其中参数类型是字符串,参数是Android.mk文件中LOCAL_MODULE定义的名称。
        }
    private Button bt_click;
    public native String javaFromJNI();    //声明native方法
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            bt_click = (Button) this.findViewById(R.id.bt_click);
            bt_click.setOnClickListener(new OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                     Toast.makeText(MainActivity.this, javaFromJNI(),  
                                Toast.LENGTH_SHORT).show();  
                }
            });
        }
    
    }
    2.2,创建jni目录,编写Hello.c文件。
       先来说一下JNI代码的简单格式:方法签名规则:返回值类型 Java_包名_类名_native方法名(JNIEnv* env, jobject obj)
     1) Java: 表示被Java调用。  包名:表示package的名称(com_example_ndkdemo01),其中的"."被下划线替代。      MainActivity:申明调用函数的类名。     javaFromJNI:申明的函数名。
     2)怪异的返回值,在NDK中,不管是从Java传过来的传递值,还是传回Java的返回值,都是专门的参数(比如java中的string对应ndk的jstring,int[]对应jintarray,具体可参见jni.h)。

     3)多出来的传递参数,JNIEnv* env和jobject thiz是底层函数,必须要带的参数。env开发者利用此参数做查询和传化数据类型(比如将jintarray转换成int数组,在以后的章节中会详细说明)。thiz表示这个调用这个函数的类对象,本文中就是MainActivity的对象。

    #include<stdio.h>
    #include<jni.h>
    
    
    jstring Java_com_example_ndkdemo01_MainActivity_javaFromJNI(JNIEnv* env, jobject obj) {
    
    
        return (*(*env)).NewStringUTF(env, "hello jni!");
    
    }
    3,编写Android.mk文件和Application.mk

    3.1 这个Android.mk文件怎么写呢?这时候我们得打开NDK的文档来看看了,位置E:/NDK/android-ndk-r10d/docs/Start_Here.html,找到

    好,我们就先在jni目录下创建一个Android.mk的文件,将上面的这段话复制粘贴进去,将LOCAL_MODULE和LOCAL_SRC_FILES修改成我们自己写成的C文件的名称:

     LOCAL_PATH := $(call my-dir)
    
        include $(CLEAR_VARS)
    
        LOCAL_MODULE    := Hello
        LOCAL_SRC_FILES := Hello.c
     
        include $(BUILD_SHARED_LIBRARY)

     3.2 Application.mk文件的目的是描述在你的应用程序中所需要的模块(即动态库或静态库)。

           Application.mk文件通常被放置在 $PROJECT/jni/Application.mk下,$PROJECT指的是您的项目。

           要将CC++代码编译为SO文件,光有Android.mk文件还不行,还需要一个Application.mk文件。

          

             

     3.3 NDK程序生成支持多种CPU架构的SO包

          在我们android NDK项目的根目录下面有一个libs文件夹,如下图所示,这个文件夹下面有下面七个文件夹:arm64-v8a,armeabi ,armeabi-v7a,   mips, mips64,x86 ,x86_64。我们的c代码编译成SO库就会放在这七个文件夹中的一个或多个中。那么这些文件夹中的SO文件有什么区别?  arm64-v8a,armeabi ,armeabi-v7a,   mips, mips64,x86 ,x86_64是表示七种不同的cpu的架构,我们知道一般的手机或平板都是用arm的cpu,不同的cpu的特性不一样,armeabi就是针对普通的或旧的arm v5 cpu,armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

    为每个支持的CPU架构提供对应的.so文件

    • 你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。你应该为每个ABI目录提供对应的.so文件。
    • 当一个应用安装在设备上,只有该设备支持的CPU架构对应的.so文件会被安装。在x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件(因为x86设备也支持armeabi-v7a和armeabi)。

           

          NDK开发中,如何编译生成arm64-v8a,armeabi ,armeabi-v7a, mips, mips64,x86 ,x86_64七个文件夹下面的.so文件,可以在Application.mk里可以配置以下宏指定ABI生成机器代码:

    TARGET_CPU_API := all
    APP_ABI := all
    或者是
    TARGET_CPU_API :=  armeabi armeabi-v7a x86 x86_64 arm64-v8a mips mips64  
    APP_ABI :
    = armeabi armeabi-v7a x86 x86_64 arm64-v8a mips mips64

          默认情况下,NDK的编译系统根据 "armeabi" ABI生成机器代码。可以使用APP_ABI 来选择一个不同的ABI。

           比如:为了在ARMv7的设备上支持硬件FPU指令。可以使用  APP_ABI := armeabi-v7a

           或者为了支持IA-32指令集,可以使用      APP_ABI := x86

          或者为了同时支持这三种,可以使用       APP_ABI := armeabi armeabi-v7a x86

          或者有时候可能只需要兼容几种cpu类型,则“TARGET_CPU_API :=” 和 “APP_ABI :=”随便指定上面六种中任意一种。  

          程序中可能会出现下面类似的错误,主要是由于没有生成对应支持CPU类型的SO文件

    java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/cn.com.chinatelecom.account-1/base.apk"],nativeLibraryDirectories=[/data/app/cn.com.chinatelecom.account-1/lib/arm64, /vendor/lib64, /system/lib64]]] couldn't find "libFeedbackUtils.so"

    4,ndk编译生成动态库

    在eclipse中点击项目,右击->>Android Tools->>Add Native Support... 。随后会有一个对话框弹出,在输入框里输入模块名称。可以看到编译通过了,下面刷新一下工程,就可以看到工程libs目录下多了个libHello.so的文件,这个就是Android认识的动态库了。

    5,Java代码load 动态库.调用native代码

    编译出来这个libHello.so文件后,就需要在Java代码中加载这个.so的库文件了,代码很简单,然后Toast一下看看效果:

    System.loadLibrary(String 文件名);是用来加载动态库的方法,其中参数类型是字符串,参数是Android.mk文件中LOCAL_MODULE定义的名称。

    参考博客 http://blog.csdn.net/allen315410/article/details/41805719 http://www.cnblogs.com/yaozhongxiao/archive/2012/03/06/2381586.htmlhttp://www.jianshu.com/p/cb05698a1968?from=timeline&isappinstalled=0

  • 相关阅读:
    beego学习笔记(4):开发文档阅读(1)
    go的匿名组合
    beego学习笔记(3)
    beego学习笔记(2)
    python发送post请求发送json数据时,报415的原因和处理方法。
    Kali Linux的安装
    linux下配置mysql的远程访问
    selenium学习笔记
    Fiddler使用方法简介
    使用webdriver打开本地浏览器--python版
  • 原文地址:https://www.cnblogs.com/fuyanan/p/4539034.html
Copyright © 2011-2022 走看看