jni

c知識html

stdlib 頭文件即standard library標準庫頭文件 經常使用系統函數,跟系統調用相關的,好比內存申請malloc和釋放free

stdio是標準io函數,好比printf和scanf函數 

windows和linux文件區別
windows     .exe     .dll    .batjava

linux          .elf      .so     .shlinux

 

 

x86對 jni兼容性能不好android

因爲ndk一開始是作給linux下用的,全部wind下用ndk會有不少問題。
因此還要裝個軟件 cygwin
只要裝2個功能 devel shellsshell

 

ndk環境變量設置windows

 windows下   1.直接解壓縮ndk,而後搭建環境變量. 在path目錄下面C:\android-ndk-r7b .  這是直接在cmd命令行下運行   ndk-buildapp

 Cygwin Terminal下   這個配置環境變量在  cygwin/etc/profile的32行ide

/cygdrive/c/android-ndk-r7b   把這個添加   不一樣ndk的安裝路徑不同函數

 每一個環境變量用:分隔.工具

 在cygwin下配置了環境便利ndk可是在別的目錄 運行ndk-build一直找不到目錄 .

執行./ndk-build -C samples/hello-jni 這個代碼可解決   博客:http://blog.sina.com.cn/s/blog_4c73bcc80101177e.html

 Jni.h 目錄:C:\android-ndk-r7b\platforms\android-14\arch-arm\usr\include

log.h目錄:C:\android-ndk-r7b\platforms\android-14\arch-arm\usr\include\android

 

實現步驟

1 定義一個c方法的接口   至關於在java代碼中定義了一個接口 接口的實現方法是C語言實現的

2 步 實現C代碼

3步驟 建立android.mk  告訴編譯器 如何把c代碼打包成函數庫

4步 把c代碼 打包成函數庫  用到了安裝的環境   經過cygwin terminal

5 步在java代碼中 引入庫函數 

static{
System.loadLibrary("hello");// 注意事項 去掉前面的lib 後面的.so
}
6 使用方法

 

 

 

Anroid.mk 文件
LOCAL_PATH := $(call my-dir) // 返回當前c代碼目錄
include $(CLEAR_VARS) // 清楚了全部 已local 開頭的配置文件 惟獨不清楚LOCAL_PATH

LOCAL_MODULE := hello // 庫函數的名字 嚴格遵照makefile 格式 lib .so 若是前面加lib 不會自動生成了
LOCAL_SRC_FILES := Hello.c
include $(BUILD_SHARED_LIBRARY) // 加入庫函數

 

 

 

jni 常見的錯誤
1錯誤1 忘記方法的參數
2 錯誤2 203-28 03:41:56.758: E/AndroidRuntime(821): java.lang.UnsatisfiedLinkError: Native method not found: com.example.error.DemoActivity.helloWorld:()Ljava/lang/String;   方法名錯誤
3 錯誤3 通常沒有日誌打印 直接報錯工程中止 通常c代碼有運行錯誤
4 錯誤4 在交叉編譯的工具鏈上報錯 c代碼有編譯錯誤 好比 一些函數沒有聲明 一些類型沒有聲明 少符號
5 錯誤5 沒有Android.mk 文件
6 錯誤6 Android.mk 文件有錯 
7 錯誤7 引用別人.so 函數庫 須要你本身native方法對應類的包名 和以前打包成.so函數庫的包名一致

 

 

 

使用javah時 有時一直報錯:找不到類文件   要添加環境變量classpath

 把安卓adt    adt\adt\adt-bundle-windows-x86_64-20140702\sdk\platforms\android-10

 這個目錄下的android.jar 解壓出來.而後把這個目錄放在classpath裏面加入

 例:   classpath:.;D:\Program Files\Android\android-sdk\platforms\android-8\android 

 而後到src目錄下   javah 包名.類名.  若是報錯找不到類文件就到  bin/classes下

 

 獲取方法簽名:

 使用javap -s 獲取內部類型簽名  這個在反射方法時候要用到

 在bin/classes 下執行:javap -s 包名.類名字

 

1
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__ )<br>解釋:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可變參數宏 ...和__VA_ARGS_ _
__VA_ARGS__ 是一個可變參數的宏,不多人知道這個宏,這個可變參數的宏是新的C99規範中新增的,目前彷佛只有gcc支持(VC6.0的編譯器不支持)。
實現思想就是宏定義中參數列表的最後一個參數爲省略號(也就是三個點)。這樣預約義宏_ _VA_ARGS_ _就能夠被用在替換部分中,替換省略號所表明的字符串。好比:
#define PR(...) printf(__VA_ARGS__)
int  main()
{
     int  wt=1,sp=2;
     PR( "hello\n" );
     PR( "weight = %d, shipping = %d" ,wt,sp);
     return  0;
}
輸出結果:
hello
weight = 1, shipping = 2
省略號只能代替最後面的宏參數。
#define W(x,...,y)錯誤!

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
把java裏的string轉成c裏面的 char *
char *   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
{
      char *   rtn   =   NULL;
      jclass   clsstring   =   (*env)->FindClass(env, "java/lang/String" );
      jstring   strencode   =   (*env)->NewStringUTF(env, "GB2312" );
      jmethodID   mid   =   (*env)->GetMethodID(env,clsstring,   "getBytes" ,   "(Ljava/lang/String;)[B" );
      jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
      jsize   alen   =   (*env)->GetArrayLength(env,barr);
      jbyte*   ba   =   (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
      if (alen   >   0)
      {
       rtn   =   ( char *) malloc (alen+1);         //"\0"
       memcpy (rtn,ba,alen);
       rtn[alen]=0;
      }
      (*env)->ReleaseByteArrayElements(env,barr,ba,0);  //
      return  rtn;
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
經過反射 調用java代碼   原理同樣
     
java裏面的反射
     Class<?> forName = Class.forName( "com.example.ndkcallback.DataProvider" );
         Method declaredMethod = forName.getDeclaredMethod( "helloFromJava" , new  Class[]{});
         declaredMethod.invoke(forName.newInstance(), new  Object[]{});
         
c裏面反射java
     ///jclass      (*FindClass)(JNIEnv*, const char*);
     jclass clazz=(*env)->FindClass(env, "com/example/ndkcallback/DataProvider" );
     //  jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
     // 方法簽名  參數和返回值
     jmethodID methodId=(*env)->GetMethodID(env,clazz, "helloFromJava" , "()V" );
     // void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
     (*env)->CallVoidMethod(env,jobject,methodId);

  

 

第一個:helloworld

建立jni目標.

建立Hello.c

 

#include<stdio.h>
#include<jni.h>

jstring Java_com_example_myhello_MainActivity_helloworldFromc(JNIEnv* env,jobject obj){
    return (*env)->NewStringUTF(env,"onehello");
}

 

建立Android.mk

複製代碼
LOCAL_PATH := $(call my-dir)

   include $(CLEAR_VARS)

   LOCAL_MODULE    := hello
   LOCAL_SRC_FILES := Hello.c

   include $(BUILD_SHARED_LIBRARY)
複製代碼

而後activity裏面加載library

複製代碼
package com.example.myhello;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

    public native String helloworldFromc();
    
    static{
        System.loadLibrary("hello");
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void click(View view) {
        // TODO Auto-generated method stub
        Toast.makeText(getApplicationContext(), helloworldFromc(), 0).show();
    }
}
複製代碼

 

c代碼調用java代碼事例

要調用的方法

1
2
3
4
5
6
7
package  com.example.threehello;
 
public  class  DataProvider {
     public  native  int  add( int  x, int  y);
     public  native  String sayHello(String s);
     public  native  int [] intMethod( int  []iNum);
}

在c裏面的實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <stdio.h>
#include <com_example_threehello_DataProvider.h>
#include <android/log.h>  //調用java代碼log的時候導入這個頭文件
#include <string.h>
 
#define LOG_TAG "clog"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__ )
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__ )
 
char * Jstring2CStr(JNIEnv*   env,   jstring   jstr) //java裏的string轉 c的char*
{
      char *   rtn   =   NULL;
      jclass   clsstring   =   (*env)->FindClass(env, "java/lang/String" );
      jstring   strencode   =   (*env)->NewStringUTF(env, "GB2312" );
      jmethodID   mid   =   (*env)->GetMethodID(env,clsstring,   "getBytes" ,   "(Ljava/lang/String;)[B" );
      jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
      jsize   alen   =   (*env)->GetArrayLength(env,barr);
      jbyte*   ba   =   (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
      if (alen   >   0)
      {
       rtn   =   ( char *) malloc (alen+1);         //"\0"
       memcpy (rtn,ba,alen);
       rtn[alen]=0;
      }
      (*env)->ReleaseByteArrayElements(env,barr,ba,0);  //
      return  rtn;
}
 
 
 
JNIEXPORT jint JNICALL Java_com_example_threehello_DataProvider_add
   (JNIEnv * env, jobject jobject, jint x, jint y){
     LOGD( "x=%d" ,x);
     LOGI( "y=%d" ,y);
     return  x+y;
}
 
JNIEXPORT jstring JNICALL Java_com_example_threehello_DataProvider_sayHello
   (JNIEnv * env, jobject jobject, jstring str){
     //jstring NewStringUTF(const char* bytes)
     char  *c= "hello" ;
     char  *strs = Jstring2CStr(env,str);
     strcat (strs,c);
     LOGD( "%s" ,strs);
     return  (*env)->NewStringUTF(env,strs);
 
}
 
JNIEXPORT jintArray JNICALL Java_com_example_threehello_DataProvider_intMethod
   (JNIEnv * env, jobject jobject, jintArray jarray){
     //    jsize       (*GetArrayLength)(JNIEnv*, jarray);
     //  jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
 
     int  length = (*env)->GetArrayLength(env,jarray);
     int  *array = (*env)->GetIntArrayElements(env,jarray,0);
     int  i=0;
     for (;i<length;i++){
         *(array+i)+=5;
     }
 
     return  jarray;
}

c代碼調用java代碼  log

先導入  頭文件 :    #include <android/log.h>

在  Android.mk 裏面加入  LOCAL_LDLIBS += -llog  這個動態連接庫在C:\android-ndk-r7b\platforms\android-14\arch-arm\usr\lib 目錄下

 

c代碼回調java代碼

事例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package  com.example.fourhello;
 
public  class  DataProvider {
     
     public  void  helloFromJava(){
         System.out.println( "haha我被調用了" );
     }
     
     public  int  Add( int  x, int  y){
         int  result = x+y;
         System.out.println(result);
         return  result;
     }
     
     public  void  printString(String s){
         System.out.println(s);
     }
 
     public  native  void  callMethod1();
     public  native  void  callMethod2();
     public  native  void  callMethod3();
}

  

 至關於java的反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.h>
#include<string.h>
#include<com_example_fourhello_DataProvider.h>
 
JNIEXPORT void  JNICALL Java_com_example_fourhello_DataProvider_callMethod1
   (JNIEnv * env, jobject jobject){
     //jclass      (*FindClass)(JNIEnv*, const char*);
     //    jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
     //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
 
     jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" );
     jmethodID methodID = (*env)->GetMethodID(env,clazz, "helloFromJava" , "()V" );
     (*env)->CallVoidMethod(env,jobject,methodID);
}
 
JNIEXPORT void  JNICALL Java_com_example_fourhello_DataProvider_callMethod2
   (JNIEnv * env, jobject jobject){
     jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" );
     jmethodID methodID = (*env)->GetMethodID(env,clazz, "Add" , "(II)I" );
     //    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
     (*env)->CallIntMethod(env,jobject,methodID,3,5);
}
 
JNIEXPORT void  JNICALL Java_com_example_fourhello_DataProvider_callMethod3
   (JNIEnv * env, jobject jobject){
     jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" );
     jmethodID methodID = (*env)->GetMethodID(env,clazz, "printString" , "(Ljava/lang/String;)V" );
     //    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
     //jstring     (*NewStringUTF)(JNIEnv*, const char*);
     jstring str = (*env)->NewStringUTF(env, "i do call back" );
     (*env)->CallVoidMethod(env,jobject,methodID,str);
}

  

c代碼調用java 其餘類裏的方法

1
2
3
4
5
6
7
public  class  MainActivity extends  Activity {
 
     public  void  hellocall(){
         System.out.println( "main activity callback" );
     }
     
}

  

1
2
3
4
5
6
7
8
JNIEXPORT void  JNICALL Java_com_example_fourhello_DataProvider_callMethod4
   (JNIEnv * env, jobject j){
     jclass clazz = (*env)->FindClass(env, "com/example/fourhello/MainActivity" );
     jmethodID methodID = (*env)->GetMethodID(env,clazz, "hellocall" , "()V" );
     //jobject     (*AllocObject)(JNIEnv*, jclass);  獲取jobject對象
     jobject jj = (*env)->AllocObject(env,clazz);
     (*env)->CallVoidMethod(env,jj,methodID);
}

   

 c代碼回調java的靜態方法

 

1
2
3
public  static  void  mystatic(){
     System.out.println( "呵呵我是靜態方法" );
}

  

1
2
3
4
5
6
7
8
JNIEXPORT void  JNICALL Java_com_example_fourhello_DataProvider_callMethod5
   (JNIEnv * env, jobject jobject){
     jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" );
//  jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
     jmethodID methodID = (*env)->GetStaticMethodID(env,clazz, "mystatic" , "()V" );
//    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
     (*env)->CallStaticVoidMethod(env,clazz,methodID);
}

  

jni在項目開發的用途  經過jni直接調用c方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package  com.example.fivehello;
 
import  android.app.Activity;
import  android.os.Bundle;
import  android.view.Menu;
import  android.view.MenuItem;
import  android.view.View;
import  android.widget.Toast;
 
public  class  MainActivity extends  Activity {
 
     static {
         System.loadLibrary( "hello" );
     }
 
     public  native  int  login(String password);
     
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
     }
     
     public  void  onclick(View view){
         int  login = login( "123" );
         Toast.makeText(getApplicationContext(), login+ "" , 0 ).show();
     }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<stdio.h>
#include<com_example_fivehello_MainActivity.h>
#include<string.h>
 
int  login( char * psw){
     char * rightword = "123" ;
     int  i = strcmp (rightword,psw);
     if (i==0){
         return  200;
     } else {
         return  302;
     }
}
 
char *   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
{
      char *   rtn   =   NULL;
      jclass   clsstring   =   (*env)->FindClass(env, "java/lang/String" );
      jstring   strencode   =   (*env)->NewStringUTF(env, "GB2312" );
      jmethodID   mid   =   (*env)->GetMethodID(env,clsstring,   "getBytes" ,   "(Ljava/lang/String;)[B" );
      jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
      jsize   alen   =   (*env)->GetArrayLength(env,barr);
      jbyte*   ba   =   (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
      if (alen   >   0)
      {
       rtn   =   ( char *) malloc (alen+1);         //"\0"
       memcpy (rtn,ba,alen);
       rtn[alen]=0;
      }
      (*env)->ReleaseByteArrayElements(env,barr,ba,0);  //
      return  rtn;
}
 
 
JNIEXPORT jint JNICALL Java_com_example_fivehello_MainActivity_login
   (JNIEnv * env, jobject jobject, jstring str){
     char  *c = Jstring2CStr(env,str);
     return  login(c);
}
相關文章
相關標籤/搜索