您的位置:首页 > 移动开发 > Android开发

Android.mk语法详解

2016-07-28 11:29 375 查看
在前面的文章中,我们已经完成了环境的搭建。其实在我们的开发Android应用程序时,已经安装了ADT,在ADT中已经包含了CDT,所以如果安装了ADT就不需要安装CDT了。在[第一个JNI例子](http://blog.csdn.net/mr_dsw/article/details/52048102)中我们弄了一个小demo测试是否环境能正常使用。在这篇文章中,我们引入一个小例子,然后介绍下.mk文件的语法以及作用。

一、HelloJni的实现
(1)、我们创建一个HelloJni的工程。然后创建一个HelloJni.java的类。
public class HelloJni {
public native String getHelloJni();
}
在该类中,我们定义一个getHelloJni()方法,该方法返回一个字符串。

(2)、我们通过javah工具生成.h头文件,然后创建对应的.c文件,实现我们定义的本地方法。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include "com_dsw_hellojni_HelloJni.h"

/*
* Class:     com_dsw_hellojni_HelloJni
* Method:    getHelloJni
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_dsw_hellojni_HelloJni_getHelloJni
(JNIEnv *env, jobject obj){
return (*env)->NewStringUTF(env,"HelloJni");
}
在这个方法的实现时候,我直接在函数里return "HelloJni";编译不会出现问题,但是在Activity中进行使用的时候,出现

07-28 09:38:59.790: A/libc(20749): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 20749 (om.dsw.hellojni)

错误。最后改成上面的形式就可以完成了。这里建议新建一个C的share Libarary工程,用于我们的.c文件的编写、编译检查。
(3)、最后我们jni文件中创建Android.mk文件,通过ndk工具进行编译jni,生成so库。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := com_dsw_hellojni_HelloJni.c
LOCAL_MODULE := libHello
include $(BUILD_SHARED_LIBRARY)

(4)在Activity中进行使用。使用时,我们首先必须要先加载生成的so库,然后才能调用里面的方法。
public class MainActivity extends Activity {
private TextView tv_text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_text = (TextView) findViewById(R.id.tv_hello);
findViewById(R.id.change_native).setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
tv_text.setText(getStringNative());
}
});
}

static{//加载so包
System.loadLibrary("Hello");
}

private String getStringNative(){//通过jni方法获取字符串
HelloJni jni = new HelloJni();
return jni.getHelloJni();
}
}




二、Android.mk文件解析
Android.mk是Android提供的一种makefile文件,用来执行相关的编译信息,包含有生成so库名、引用的头文件目录、需要编译的.c文件、静态库等。

一个android子项目中会存在一个或多个Android.mk文件:

(1)、单一的Android.mk文件 
比如我们上面的工程中,只在jni中创建一个Android.mk文件。

(2)、多个Android.mk文件 
如果需要编译的模块比较多,我们可能会将对应的模块放置在相应的目录中, 
这样,我们可以在每个目录中定义对应的Android.mk文件(类似于上面的写法), 
最后,在根目录放置一个Android.mk文件,内容如下: 
include $(call all-subdir-makefiles) 
只需要这一行就可以了,它的作用就是包含所有子目录中的Android.mk文件 

(3)、多个模块共用一个Android.mk 
这个文件允许你将源文件组织成模块,这个模块中含有: 

   -静态库(.a文件) 

  -动态库(.so文件) 
只有动态库才能被安装/复制到您的应用软件(APK)包中,静态库则可以被链接入动态库。
include $(BUILD_STATIC_LIBRARY),编译出的是静态库 
include $(BUILD_SHARED_LIBRARY),编译出的是动态库 

1、NDK系统的系统变量。
GNU Make变量在我们自定义的Android.mk文件解析之前就已经定义好了。常见的变量有:

(1)、CLEAR_VARS:指向一个编译脚本,几乎所有未定义的 LOCAL_XXX 变量都在"Module-description"节中列出。必须在开始一个新模块之前包含这个脚本:include$(CLEAR_VARS),用于重置除LOCAL_PATH变量外的,所有LOCAL_XXX系列变量。 

(2)、BUILD_SHARED_LIBRARY:指向编译脚本,根据所有的在 LOCAL_XXX 变量把列出的源代码文件编译成一个共享库。 注意,必须至少在包含这个文件之前定义 LOCAL_MODULE 和 LOCAL_SRC_FILES。

(3)、BUILD_STATIC_LIBRARY:  一个 BUILD_SHARED_LIBRARY 变量用于编译一个静态库。静态库不会复制到的APK包中,但是能够用于编译共享库。 

       示例:include $(BUILD_STATIC_LIBRARY) 

       注意,这将会生成一个名为 lib$(LOCAL_MODULE).a 的文件 

(4)、TARGET_ARCH: 目标 CPU平台的名字 

(5)、TARGET_PLATFORM: Android.mk 解析的时候,目标 Android 平台的名字.详情可考/development/ndk/docs/stable- apis.txt. 

       android-3 -> Official Android 1.5 system images 

       android-4 -> Official Android 1.6 system images 

       android-5 -> Official Android 2.0 system images 

(6)、TARGET_ARCH_ABI:  暂时只支持两个 value,armeabi 和 armeabi-v7a。。 

(7)、TARGET_ABI: 目标平台和 ABI 的组合。

(8)、PREBUILT_SHARED_LIBRARY

把这个共享库声明为 “一个” 独立的模块。 

指向一个build 脚本,用来指定一个预先编译好多动态库。 与BUILD_SHARED_LIBRARY and BUILD_STATIC_LIBRARY不同,

此时模块的LOCAL_SRC_FILES应该被指定为一个预先编译好的动态库,而非source file.  LOCAL_PATH := $(call my-dir)

    

include $(CLEAR_VARS)

LOCAL_MODULE := foo-prebuilt     # 模块名

LOCAL_SRC_FILES := libfoo.so     # 模块的文件路径(相对于 LOCAL_PATH)  

include $(PREBUILT_SHARED_LIBRARY) # 注意这里不是 BUILD_SHARED_LIBRARY

这个共享库将被拷贝到 $PROJECT/obj/local 和 $PROJECT/libs/<abi> (stripped)  主要是用在将已经编译好的第三方库

使用在本Android Project中。为什么不直接将其COPY到libs/armabi目录呢?因为这样做缺陷很多。下一节再详细说明。

(9)PREBUILT_STATIC_LIBRARY: 预先编译的静态库。 同上。

2、NDK提供的功能宏:
GNU Make 提供的功能宏,只有通过类似: $(call function)   的方式来得到其值,它将返回文本化的信息。

(1)、my-dir: $(call my-dir): 返回最近一次include的Makefile的路径。通常返回Android.mk所在的路径。它用来作为Android.mk的开头来定义LOCAL_PATH。

LOCAL_PATH := $(call my-dir) 

请注意:返回的是最近一次include的Makefile的路径。所以在Include其它Makefile后,再调用$(call my-dir)会返回其它Android.mk 所在路径。   例如:  

LOCAL_PATH := $(call my-dir)   ... declare one module   include $(LOCAL_PATH)/foo/Android.mk   LOCAL_PATH := $(call my-dir)   ... declare another module

则第二次返回的LOCAL_PATH 为:$PATH/foo。 而非$PATH.   

(2)、all-subdir-makefiles:

返回一个列表,包含'my-dir'中所有子目录中的Android.mk。   

例如: 结构如下:  

sources/foo/Android.mk   

sources/foo/lib1/Android.mk   

sources/foo/lib2/Android.mk 

在If sources/foo/Android.mk 中, 

include $(call all-subdir-makefiles)  那则自动include 了sources/foo/lib1/Android.mk and sources/foo/lib2/Android.mk。

(3)、this-makefile:  返回当前Makefile 的路径(即这个函数调用的地方) 

(4)、parent-makefile:  返回调用树中父 Makefile 路径。即包含当前Makefile的Makefile 路径。 

(5)、grand-parent-makefile:返回调用树中父Makefile的父Makefile的路径 

3、模块描述变量
此类变量用来给Build System描述模块信息。在'include $(CLEAR_VARS)' 和 'include $(BUILD_XXXXX)'之间。必须定义此类变量。 

include $(CLEAR_VARS) script用来清空这些变量。   

include $(BUILD_XXXXX)收集和使用这些变量。    

(1)、LOCAL_PATH:  这个变量用于给出当前文件的路径。 

       必须在 Android.mk 的开头定义,可以这样使用:LOCAL_PATH := $(call my-dir) 

       如当前目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径 

       这个变量不会被$(CLEAR_VARS)清除,因此每个 Android.mk 只需要定义一次(即使在一个文件中定义了几个模块的情况下)。 

  (2)LOCAL_MODULE: 这是模块的名字,它必须是唯一的,而且不能包含空格。 

       必须在包含任一的$(BUILD_XXXX)脚本之前定义它。模块的名字决定了生成文件的名字。 

  (3)LOCAL_SRC_FILES:  这是要编译的源代码文件列表。 

       只要列出要传递给编译器的文件,因为编译系统自动计算依赖。注意源代码文件名称都是相对于 LOCAL_PATH的,你可以使用路径部分,例如: 

        LOCAL_SRC_FILES := foo.c toto/bar.c\ 

        Hello.c 

       文件之间可以用空格或Tab键进行分割,换行请用"\" 

       如果是追加源代码文件的话,请用LOCAL_SRC_FILES += 

       注意:可以LOCAL_SRC_FILES := $(call all-subdir-java-files)这种形式来包含local_path目录下的所有java文件。 

  (4)LOCAL_C_INCLUDES:  可选变量,表示头文件的搜索路径。 

        默认的头文件的搜索路径是LOCAL_PATH目录。 

  (5)LOCAL_STATIC_LIBRARIES: 表示该模块需要使用哪些静态库,以便在编译时进行链接。 

  (6)LOCAL_SHARED_LIBRARIES:  表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。 

       注意:它不会附加列出的模块到编译图,也就是仍然需要在Application.mk 中把它们添加到程序要求的模块中。 

  (7)LOCAL_LDLIBS:  编译模块时要使用的附加的链接器选项。这对于使用‘-l’前缀传递指定库的名字是有用的。 

       例如,LOCAL_LDLIBS := -lz表示告诉链接器生成的模块要在加载时刻链接到/system/lib/libz.so 

       可查看 docs/STABLE-APIS.TXT 获取使用 NDK发行版能链接到的开放的系统库列表。 

   (8)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH 

       在 Android.mk 文件中, 还可以用LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH指定最后的目标安装路径. 

       不同的文件系统路径用以下的宏进行选择: 

       TARGET_ROOT_OUT:表示根文件系统。 

       TARGET_OUT:表示 system文件系统。 

       TARGET_OUT_DATA:表示 data文件系统。 

       用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 

       至于LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的区别,暂时还不清楚。 

   (9)LOCAL_JNI_SHARED_LIBRARIES:定义了要包含的so库文件的名字,如果程序没有采用jni,不需要 

        LOCAL_JNI_SHARED_LIBRARIES := libxxx 这样在编译的时候,NDK自动会把这个libxxx打包进apk; 放在youapk/lib/目录下 。

参考文章:
http://www.cnblogs.com/wainiwann/p/3837936.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: