目标:在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>
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := cpuinfo LOCAL_SRC_FILES := cpuinfo.c include $(BUILD_SHARED_LIBRARY)
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/目录下面的例子