1. 程式人生 > >利用Ubuntu將c++檔案生成.so庫

利用Ubuntu將c++檔案生成.so庫

記錄生成.so庫的步驟,以防自己每次忘記。用的是eclipse。

一、先在eclipse中生成一個android工程,然後在android工程下的src新建一個package,例如名字為com.android.aa,在其下新建一個java檔案,這個檔案相當於與.so庫的一個對接介面,其形式為:

 package  com.android.aa;

public class NativeAA{

         static{

                   System.loadLibrary("aa");          //載入aa.so庫

                   }

                  public native byte[] sent(String input);     //呼叫so庫中的sent函式

}

儲存一下,就可以在./bin/classes/com/android/aa檔案下生成NativeAA.class檔案。

二、利用javah生成jni的標頭檔案

首先利用cmd進入目錄中:例如工程放在了F盤,工程為test:

f:       //這是進入了f盤

cd   ./test/bin/classes目錄下,執行下一命令:

javah com.android.aa.NativeAA

就會在classes生成了一個com_android_aa_NativeAA.h的標頭檔案

裡面就是一個函式的宣告:

JNIEXPORT jbyteArray JNICALL Java_com_android_aa_NativeAA_sent

      (JNIEnv*,jobject ,  jstring);(其他的省略,並且java中有幾個函式宣告,.h就對應生成幾個,我這裡是一個)

三、生成與.h對應的.cpp檔案

在Ubuntu中建立一個資料夾,例如:我在project的檔案下建立了一個aa的檔案下,然後建立一個jni檔案下,將com_android_aa_NativeAA.h方進去,再新建一個對應的.cpp

,在命令視窗輸入gedit com_android_aa_NativeAA.cpp,就建立以個.cpp檔案。再建立實現sent函式的.cpp和.h這裡命名為sent.cpp和sent.h。

sent.cpp的中的函式sent實現的是輸入為char*,輸出的引數為char*型別,其宣告為:

int sent(char* sentInput,char **sentInput);

char*sentInput是輸入,char**sentInput是輸出(實際需要的是char*,返回二級指標下面會說明),返回int是標誌位,這個程式執行下來返回0表示成功;其他值表示失敗。

com_android_aa_NativeAA.cpp的形式:

#include "com_android_aa_NativeAA.h"

#define TAG "so-jni"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)       //

//功能:實現將jstring轉換為char*

char*jstringTostring(JNIEnv*env,jstring jstr);

JNIEXPORT jbyteArray JNICALL Java_com_android_aa_NativeAA_sent

      (JNIEnv*env,jobject object,  jstring jinput)  //jstring jinput對應的是java檔案中的String input

{

     //首先將jstring轉成char型別,因為我的例程中sent.cpp中的函式的輸入引數是char*;

     char *inputChar=jstringTostring(env,jinput);

     if(inputChar==NULL)

     {

               LOGD("jstring轉換char失敗!")

               return NULL;

     }

    else

    {

          char *outData=new char[255];

          memset(outData,0,255);

          if(outData==NULL)

                return NULL;

          int ret=sentInput(InputChar,&outData);           //outData是返回值,&outData這樣做是返回一組陣列。    

          if(ret==0)

          {

               //這是將c++中的char*轉為為java中的byte[];

                 jbyte *joutData=(jbyte*)outData;

                 jbyteArray jarrOutData=env->NewByteArray(255);

                 env->SetByteArrayRegion(jarrOutData,0,255,joutData);

                  delete[ ] outData;

                  return jarrOutData; 

          }

          else

           {

                  delete[] outData;

                   return NULL;

           }

    }  

     delete[ ] inputData;

}

//將Java中的String轉換為c++中的char*

char*jstringTostring(JNIEnv* env,jstring jstr)

{

        char*ttn=NULL;

        jclass clsstring=env->env->FindClass("java/lang/string");

        jstring strInput=env->NewStringUTF("utf-8");

        jmethodID mid=env->GetMethodID(clsstring,"getBytes","(Ljava/lang/String;)[B");

         jbyteArray barr=(jbyteArray)env->CallObjectMethod(jstr,mid,strencode);

          jsize alen=env->GetArrayLength(barr);

          jbyte*ba=env->GetByteArrayElements(barr,JNI_FALSE);

          if(alen>0)

           {

                   rtn=(char*)malloc(alen+1);

                   memcpy(rtn,ba,alen);

                   rtn[alen]=0;

           }

           env->ReleaseByteArrayElements(barr,ba,0);

           return rtn;

}

四、兩個makefile檔案

(1)Application.mk:用於描述APP需要的native model

其內容為:

APP_PLATFORM :=android-19

APP_STL :=gnustl_static             #要使用到的標準c++庫

APP_CPPFLAGS :=-frtti  -fexceptions

APP_ABI :=armeabin

分析:

Application.mk常用的有四個:

a.     APP_PLATFORM :=android-19

表示使用ndk庫函式版本號。一般和SDK的版本相對應,各個版本在NDK目錄下的platforms資料夾中

b.     APP_STL :=stlport_static

定義NDK的編譯系統的執行時庫,有如下幾種:

system:預設最小的c++執行庫,生成的應用體積小,記憶體佔用少,但部分功能將無法支援

stIport_static:使用STLport作為靜態庫(推薦使用)

stlport_shared:STLport作為動態庫(不推薦,可能產生相容性和部分低版本的Android韌體)

gnustl_static:使用GNU libstdc++作為靜態庫

c.  APP_ABI:=armeabi

表示要編譯成對應指令集cpu的動態庫,armeabi:ARMv7指令集,armeabi-v7a:ARMv7+硬體FPU指令集,x86:IA-32指令集,all支援常用的指令集

d.   APP_CPPFLAGS:該變數列出了一些編譯器標誌,在便以任何模組的c++程式碼時這些標誌都會被傳給編譯器;

e.       APP_OPTIM:=debug(我自己的專案沒用這一項)

設定編譯型別,debug:除錯版本,so動態庫中帶除錯資訊(可以使用gdb-server盡心動態段斷點低除錯),release:釋出版本,so庫不帶除錯資訊

(2)Android.mk:用於描述構建系統的原始檔以及shareed libraries。

其內容:

LOCAL_PATH :=&(call my-dir)

APP_STL  :=gnustl_static       #要用到標準c++庫,如果不用可以註釋掉

include $(CLEAR_VARS)

LOCAL_LDLIBS:=-llog

LOCAL_MODULE   :=aa          #aa為行程so庫的庫名

LOCAL_SRC_FILES :=sent.cpp \

com_android_usbkey_NativeUsbBase.cpp  \

LOCAL_CFLAGS :=-arm -mfloat-abi=softfp  -mfpu=vfp  -Wmultichar

include $(BUILD_SHARED_LIBRARY)

總結:

要行程.so庫的流程就這些(可根據自己的情況新增.cpp),需注意的是:ubuntu上必須安裝ndk上述檔案才可以形成.so庫。

在命令視窗需輸入的:

1.首先進入jni檔案所在的目錄(刪除的檔案都放在jni檔案裡);

2.輸入命令:ndk-build即可生成so庫(注意:清楚so庫的命令為:ndk-build clean)