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编程可以不用理会。

  

  

posted @ 2014-05-31 14:50  WanChouchou  阅读(744)  评论(0编辑  收藏  举报