Android系统的底层库由C/C++编写,上层Android应用程序通过Java虚拟机调用底层接口。衔接底层C/C++库与Java应用程序间的接口正是JNI(Java Native Interface)。本文讲解如何在Mac下打包so库,并安装到APK中。
1、环境的安装
Android的环境安装ADT、NDK、SDK、JDK、ANT等一些环境的搭建,http://www.androiddevtools.cn/
在控制台下输入:vim ~/.bash_profile,点击i,输入环境变量。如果之前没有配置过Android的环境,./bash_profile文件应该不存在。下面我把我的配置文件粘上来。
# Add environment variable COCOS_CONSOLE_ROOT for cocos2d-x
#export COCOS_CONSOLE_ROOT=/Users/chen/Desktop/cocos2d-x-3.0/tools/cocos2d- console/bin
#export PATH=$COCOS_CONSOLE_ROOT:$PATH
# Add environment variable NDK_ROOT for cocos2d-x
#export NDK_ROOT=/Users/chen/work_space/Cocos/android-ndk-r9b
#export PATH=$NDK_ROOT:$PATH //以上#后面的都是注释,读者可忽略上面的内容
export NDK_ROOT=/Users/chen/work_space/Cocos/android-ndk-r9d //NDK
export ANDROID_SDK_ROOT=/Users/chen/work_space/Cocos/adt-bundle-mac-x86_64-20130917/sdk//SDK
export ANDROID_NDK_ROOT=/Users/chen/work_space/Cocos/android-ndk-r9d //NDK
export ANT_ROOT=/Users/chen/work_space/Cocos/apache-ant-1.9.3/bin//ANT
export PATH=$ANT_ROOT/bin:$PATH//添加环境变量
export PATH=$PATH:$ANT_ROOT
export PATH=$PATH:$NDK_ROOT
export PATH=$PATH:$ANDROID_SDK_ROOT
export PATH=$PATH:$ANDROID_NDK_ROOT
# Add environment variable COCOS_CONSOLE_ROOT for cocos2d-x
export COCOS_CONSOLE_ROOT=/Users/chen/Cocos/cocos2d-x-3.3rc0/tools/cocos2d-console/bin //Cocos2d-x环境
export PATH=$COCOS_CONSOLE_ROOT:$PATH
export PATH=${PATH}:/Users/chen/work_space/Cocos/adt-bundle-mac-x86_64-20130917/sdk/platform-tools//红米手机链接Mac需要此步
# add by quick-cocos2d-x setup, DATE: 2014-11-15 TIME: 14:11:22
export QUICK_V3_ROOT=`cat ~/.QUICK_V3_ROOT` //Quick环境
完成上面的配置,点电脑键盘esc键,在命令行输入":wq"即保存退出。注意环境路径不要有空格。
控制台下执行source ~/.bash_profile,保存环境变量,关闭Mac的bash终端。重新打开,如果不报错,就环境变量添加成功。
2、验证安装
1)java -version:检测JDK是否安装成功
2)ndk-build:验证NDK是否安装成功,在编译动态库时会用到该命令
3)ant -version:验证ANT是否安装成功
3、新建Android工程
打开Eclipse或ADT新建Android工程,此时的Android工程并没有jni这个文件夹。我们需要创建这个文件夹。在项目的根目录下添加一个jni文件夹。在文件夹下先添加3个文件,分别是Android.mk、Application.mk、hello-jni.c。下面分别介绍下这三个文件。
1)Android.mk是android编译时的makefile文件,需要将头文件、源文件、和库添加其中。我们打包的库文件即在该文件中添加。
下面是我的mk文件内容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
注意:":="是赋值的意思,"+="是追加的意思,"$"表示引用某变量的值。
1)LOCAL_PATH:当前文件的路径,即android.mk文件的路径。第一句话的意思就是将当前文件的路径返回给LOCAL_PATH
2)LOCAL_MODEL:表示模块的名字,唯一,且不含空格。必须在包含任一的$(BUILD_XXXX)的脚本之前定义它。模块的名字决定了生成库文件的名字。
3)LOCAL_SRC_FILE:表示将要编译的源代码。源码文件都是相对于当前文件路径的
4)LOCAL_STATIC_LIBRARIES:表示该模块需要使用哪些静态库,一边在编译时进行连接。
5)LOCAL_SHARED_LIBRARIES:表示模块在运行时依赖的动态库。
6)LOCAL_JNI_SHARED_LIBRARIES := libxxx这样在编译时NDK自动会把libxxx打包进apk,放在lib目录下
#编译静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE = libhellos
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = hellos.c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_COPY_HEADERS_TO := libhellos
LOCAL_COPY_HEADERS := hellos.h
include $(BUILD_STATIC_LIBRARY)
#编译动态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE = libhellod
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = hellod.c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_COPY_HEADERS_TO := libhellod
LOCAL_COPY_HEADERS := hellod.h
include $(BUILD_SHARED_LIBRARY)
#使用静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hellos
LOCAL_STATIC_LIBRARIES := libhellos
LOCAL_SHARED_LIBRARIES :=
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := mains.c
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
#使用动态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hellod
LOCAL_MODULE_TAGS := debug
LOCAL_SHARED_LIBRARIES := libc libcutils libhellod
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := maind.c
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
4、我的代码
1)修改src文件
package com.example.myhello;
import com.example.myhello.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.textView1);
// tv.setText("Test");
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
public native void printString(String info);
static{
System.loadLibrary("hello-jni");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
2)添加jni文件夹
文件夹下添加Android.mk、Application.mk和hello-jni.c
(1)hello-jni.c
#include <string.h>
#include <jni.h>
#include <android/log.h>
JNIEXPORT jstring JNICALL
Java_com_example_myhello_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
JNIEXPORT void JNICALL
Java_com_example_myhello_MainActivity_printString( JNIEnv* env,jobject thiz,jstring info)
{
const jchar* strDest;
strDest = (*env)->GetStringUTFChars(env,info,NULL);
if(strDest == NULL)
{
return NULL;
}
__android_log_print(ANDROID_LOG_INFO, "printString", strDest);
(*env)->ReleaseStringUTFChars(env,info,strDest);
}
(2)Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
(3)Application.mk
由于我的ADT版本与NDK版本不符,所有会有一些警告当成了错误,所以,我的Application需添加如下代码
APP_CFLAGS += -Wno-error=format-security
5、编译动态库
1)cd到工程目录
2)ndk-build,编译生成so库
3)在src的文件中调用,即
MainActivity调用库中的文件,注意,一定要先加载库文件