目标:在Android Studio工程中加入C语言写的动态库so,实现App调用so
0. 首先需要下载Android NDK,解压以后,假设路径为/your/path/to/ndk/,里面有一个编译的脚本ndk-build,以及一些例子sample。
1. 创建一个C的动态库目录。假设路径为/your/path/to/c/
C程序的目录结构如下:
├── AndroidManifest.xml
├── default.properties
└── jni
├── Android.mk
├── Application.mk
└── cpuinfo.c
这个结构是参照ndk/sample/hello-jni。里面只有cpuinfo.c和cpuinfo.h是自己写的,其他文件都是从例子里面拷贝出来修改的。
2. 写一个C程序。这个程序功能比较简单,只是从Linux种把CPU的信息读出来然后发给app.
cpuinfo.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
static char *get_cpu_info(char *buf, int len)
{
char *p = buf;
int fd;
if (NULL == p)
return NULL;
fd = open("/proc/cpuinfo", O_RDONLY);
if (fd < 0)
return NULL;
memset(buf, 0, len);
len = read(fd, buf, len);
close(fd);
return p;
}
jstring Java_com_android_cpuinfo_MainActivity_getCpuinfo(JNIEnv* env, jobject this)
{
char buf[2048];
const char *p = get_cpu_info(buf, 2048);
if (p)
return (*env)->NewStringUTF(env, p);
strcpy(buf, "NOTHING");
return (*env)->NewStringUTF(env, p);
}
补充:这里JNI的接口是要符合一定的规则的,规则就是Java_PACKAGE_ACTIVITY_YOURAPI。我这里的package是com.android.cpuinfo,activity是在创建app工程时默认的MainActivity,我定的API名称叫做getCpuinfo,所以这里的接口名字就叫做Java_com_android_cpuino_MainActivity_getCpuinfo。
3. 修改编译配置文件
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cpuinfo"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="3" />
<application android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".Cpuinfo"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := cpuinfo
LOCAL_SRC_FILES := cpuinfo.c
include $(BUILD_SHARED_LIBRARY)
default.properties和Application.mk不用修改
4. 编译C动态库
cd /your/path/to/c/
/your/path/to/ndk/ndk-build
这个时候编译就完成了,目录下会出现libs,所有编译出来的动态库libcpuinfo.so都会放在这个目录下。
5. 创建一个App工程。我是完全不懂Android App的,所以就按照向导创建了一个最简单的HelloWorld,然后再修改。
创建工程以后,把刚才编译出来的libs/目录下的所有文件复制到app/libs/目录下
6. 修改编译配置文件app/build.gradle,需要增加下面的内容
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
app/build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.android.cpuinfo"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
}
7. 修改App java程序
MainActivity.java
package com.android.cpuinfo;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main); //mask it
//i add from here
TextView tv = new TextView(this);
// here, we dynamically load the library at runtime
// before calling the native method.
//
System.loadLibrary("cpuinfo");
tv.setText(getCpuinfo());
setContentView(tv);
//i add end
}
public native String getCpuinfo(); //add a jni API
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
补充:这里的Jni接口是要和C程序里面的接口名称相对应的,我的JNI接口名字叫做Java_com_android_cpuino_MainActivity_getCpuinfo,所以这里调用的是getCpuinfo。
8. 生成app
build->make project
build->generate signed apk
生成的app在app/app-release.apk,可以直接放到手机上面安装使用。这个app的功能比较简单,主要是把android app调用c程序的框架搭起来了。
一开始如果NDK不会用的话可以多看ndk/sample/目录下面的例子