Android jni 编程入门
本文将介绍如何使用eclipse和ndk-build来编写一个基于Android4.4版本的包含有.so动态库的安卓程序。
前提是已经安装和配置好了诸如SDK,NDK等编译环境。下面开始编程!
1 程序逻辑
我们要编写的程序包含两部分:java部分——负责界面和调用JNI native函数;JNI native 部分——负责native函数的具体实现(本文使用C语言)。
native 函数伪代码如下:
/* funtion: 传入两个整形变量,计算他们之和 return : 返回字符串“The result is ” +sum */ char* test(int fisrt, int second){ sum = first + scond; return “The result is ” +sum; }
2 程序实现
2.1 创建项目
如编写普通apk一样创建一个apk项目。然后在该项目的根目录添加一个文件夹 jni,然后在这个文件夹下面添加hello.c和Android.mk两个文件,完成效果如下图所示:
2.2 开始JNI编程
hello.c代码如下:
/*hello.c*/
1 #include <string.h> 2 #include <stdio.h> 3 #include <jni.h> 4 5 //一定不要忘了 JNIEXPORT关键字! 6 JNIEXPORT jstring Java_com_wan_firstjniprogram_MainActivity_test( JNIEnv* env, 7 jobject thiz , jint first, jint second) 8 { 9 #if defined(__arm__) 10 #if defined(__ARM_ARCH_7A__) 11 #if defined(__ARM_NEON__) 12 #define ABI "armeabi-v7a/NEON" 13 #else 14 #define ABI "armeabi-v7a" 15 #endif 16 #else 17 #define ABI "armeabi" 18 #endif 19 #elif defined(__i386__) 20 #define ABI "x86" 21 #elif defined(__mips__) 22 #define ABI "mips" 23 #else 24 #define ABI "unknown" 25 #endif 26 27 const char* format = "The result is %d\n"; 28 char *ret; 29 //add two values 30 jint sum = first + second; 31 //malloc room for the ret 32 ret = malloc(sizeof(format) + 20); 33 //standard sprintf 34 sprintf(ret, format, sum); 35 jstring stringRet = (*env)->NewStringUTF(env, ret); 36 free(ret); 37 return stringRet; 38 }
关于JNI函数名的编写,网上有很多资料,这里主要提醒两点:1、包名需要全小写,类名和函数名要大小写一致;2、一定要在函数名前加上关键字 JNIEXPORT 。
再来编写Android.mk,代码如下:
1 LOCAL_PATH := $(call my-dir) 2 3 include $(CLEAR_VARS) 4 5 LOCAL_MODULE := hello 6 LOCAL_SRC_FILES := hello.c 7 8 include $(BUILD_SHARED_LIBRARY)
如何编写Android.mk就不过多解释了,网上资料也是一大堆,这里直接给出代码。
现在,我们的JNI 编写代码部分算是结束了,就还剩下最后一步——编译。编译很简单:使用cmd命令行进入你的apk工程所在的文件夹的jni目录(我的目录是D:\androidWorkSpace\firstJniProgram\jni),然后输入ndk-build命令即可自动编译,结果如下图所示:
从上图可以看出,我们已经成功生成了libhello.so库。到这里,JNI编程部分已经完结。下面就是进入JAVA部分了。
2.2 java编写
如何编写界面我就不多说了,这里指出我所遇到的一个问题。
问题:由于Android4.4的apk项目会创建两个layout.xml布局文件,如下图所示:
且首先展示给developer的是fragment_main.xml。这同Android2.3.3是不一样的(默认只有一个布局文件——activity_main.xml)!所以如果要添加组件的话,最好添加到activity_main中,这样才不会发生各种activity错误~。
activity_main.xml代码如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:id="@+id/container" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 tools:context="com.wan.firstjniprogram.MainActivity" 7 tools:ignore="MergeRootFrame" > 8 9 <TextView 10 android:id="@+id/info" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:text="@string/hello_world" /> 14 <Button 15 android:id="@+id/button" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:text ="JNItest"/> 19 20 </LinearLayout>
下面展示MainActivity.java的代码:
1 package com.wan.firstjniprogram; 2 3 import junit.framework.Test; 4 import android.support.v7.app.ActionBarActivity; 5 import android.support.v7.app.ActionBar; 6 import android.support.v4.app.Fragment; 7 import android.R.string; 8 import android.os.Bundle; 9 import android.util.Log; 10 import android.view.LayoutInflater; 11 import android.view.Menu; 12 import android.view.MenuItem; 13 import android.view.View; 14 import android.view.View.OnClickListener; 15 import android.view.ViewGroup; 16 import android.widget.Button; 17 import android.widget.TextView; 18 19 public class MainActivity extends ActionBarActivity { 20 21 22 static{ 23 System.loadLibrary("hello"); 24 } 25 26 public native String test(int first, int second); 27 28 private TextView info = null; 29 private Button button = null; 30 31 @Override 32 protected void onCreate(Bundle savedInstanceState) { 33 super.onCreate(savedInstanceState); 34 setContentView(R.layout.activity_main); 35 36 if (savedInstanceState == null) { 37 getSupportFragmentManager().beginTransaction() 38 .add(R.id.container, new PlaceholderFragment()) 39 .commit(); 40 } 41 42 this.info = (TextView)super.findViewById(R.id.info); 43 this.button = (Button)super.findViewById(R.id.button); 44 45 this.button.setOnClickListener(new MyonclickListen()); 46 47 } 48 49 private class MyonclickListen implements OnClickListener{ 50 51 @Override 52 public void onClick(View arg0) { 53 // TODO Auto-generated method stub 54 //MainActivity.this.info.setText("123"/*test(1, 2)*/); 55 int first = 1, second = 2; 56 String ret = test(first, second); 57 MainActivity.this.info.setText(ret); 58 } 59 60 } 61 62 @Override 63 public boolean onCreateOptionsMenu(Menu menu) { 64 65 // Inflate the menu; this adds items to the action bar if it is present. 66 getMenuInflater().inflate(R.menu.main, menu); 67 return true; 68 } 69 70 @Override 71 public boolean onOptionsItemSelected(MenuItem item) { 72 // Handle action bar item clicks here. The action bar will 73 // automatically handle clicks on the Home/Up button, so long 74 // as you specify a parent activity in AndroidManifest.xml. 75 int id = item.getItemId(); 76 if (id == R.id.action_settings) { 77 return true; 78 } 79 return super.onOptionsItemSelected(item); 80 } 81 82 /** 83 * A placeholder fragment containing a simple view. 84 */ 85 public static class PlaceholderFragment extends Fragment { 86 87 public PlaceholderFragment() { 88 } 89 90 @Override 91 public View onCreateView(LayoutInflater inflater, ViewGroup container, 92 Bundle savedInstanceState) { 93 View rootView = inflater.inflate(R.layout.fragment_main, container, false); 94 return rootView; 95 } 96 } 97 98 }
OK!到此,整个程序的代码已经编写完毕,可以鼠标右击项目,run as->android application了。点击JNItest按钮后,效果如下图所示:
3 修改
如果需要修改hello.c文件,那么在修改完成后,同样的方式编译即可,无需其他操作。还有一点需要指出的是,程序在运行的时候,eclipse的DDMS的logcat会提示:
NO JNIOnLoad....,这是一个warmming,是因为我们使用的javah方式编写native代码,而非使用jni_onload方式动态注册native函数,初学JNI编程可以不用理会。