android java和c混合编程_C/C++在Java、Android和Objective-C三大平台下实现混合编程
Android和iOS開發(fā)都支持C++開發(fā),可以一套代碼多平臺使用。同時C++難以反編譯的特性也可以為Android開發(fā)帶來代碼的保密,另一native特性也可以提高代碼的運(yùn)行效率。
一、為什么使用C/C++便于移植,用C/C++寫得庫可以方便在其他的平臺上再次使用。
代碼的保護(hù),由于java層代碼很容易被反編譯,而C/C++庫反匯難度較大。
提高程序的執(zhí)行效率,將要求高性能的應(yīng)用邏輯使用C/C++開發(fā),從而提高應(yīng)用程序的執(zhí)行效率。
訪問現(xiàn)有開源庫,需要訪問底層的API或引用一些只有C/C++的庫。
二、開發(fā)工具介紹
盡管Android Studio可以同時編寫C++和Java代碼,寫完就可以編譯運(yùn)行,但是對聯(lián)想和錯誤提示并不是非常友好,個人建議C++的整體代碼使用Visual Studio或Xcode編譯開發(fā),聯(lián)想功能非常友好,編譯速度很快,調(diào)試也非常方便。Visual Studio(PC)
Xcode(Mac)
Android Studio(多平臺)
eclipse(多平臺)
三、第一行代碼
1. 如何在Objective-C項目中使用C++;
在Objective-C使用C/C++非常簡單,僅僅需要把.m后綴的文件改成.mm即可使用C++,我們通常不會把.mm的文件寫到整個項目都有,而是設(shè)計一個接口,用來做兩個語言之間的橋梁,他們之間的交互僅僅在這個接口。
要點(diǎn):String類型轉(zhuǎn)換//?Objective-C(NSString)?->?C++(std::string)NSString?*?ocString?=?@"Hello?World,OC";std::string?cppString?=?[ocString?UTF8String];std::cout<?Objective-C(NSString)std::string??cppString2?=?"Hello?World,C++";
NSString?*ocString2=?[NSString?stringWithCString:cppString2.c_str()?encoding:[NSString?defaultCStringEncoding]];
NSLog(@"%@",ocString2);
記得要include相關(guān)的文件#include?#include?
2.在普通的JAVA項目中使用JNI編程
由于我是在MAC下辦公,所以這里就介紹如何在MAC下進(jìn)行JNI開發(fā),在Windows平臺下的Virtual Studio也很簡單。
第一步:在Xcode下創(chuàng)建一個普通的C++項目
第二步:關(guān)聯(lián)JavaVM的Framework
路徑:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaNativeFoundation.framework/
第三步:創(chuàng)建頭文件,用于和Java交互 cn_taoweiji_nativemodule_NativeDemo.h#include?#ifndef?_Included_cn_taoweiji_nativemodule_NativeDemo#define?_Included_cn_taoweiji_nativemodule_NativeDemo#ifdef?__cplusplusextern?"C"?{#endif
JNIEXPORT?jint?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_add
(JNIEnv?*,?jclass,?jint,?jint);????JNIEXPORT?void?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_say
(JNIEnv?*,?jclass,?jstring);????JNIEXPORT?jstring?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_getInfo
(JNIEnv?*,?jclass);????JNIEXPORT?void?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_nativeToJava
(JNIEnv?*,?jclass,?jobject);#ifdef?__cplusplus}#endif#endif
第四步:創(chuàng)建實(shí)現(xiàn) NativeDemo.cpp#include?"cn_taoweiji_nativemodule_NativeDemo.h"#include?JNIEXPORT?jint?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_add(JNIEnv?*,?jclass,?jint?param1,?jint?param2){
jint?result?=?param1?+?param2;????return?result;
}JNIEXPORT?void?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_say(JNIEnv?*env,?jclass,?jstring?param){????//?std::string?->?jstring
const?char?*param_char?=?env->GetStringUTFChars(param,?NULL);????std::string?str?=?param_char;
}JNIEXPORT?jstring?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_getInfo(JNIEnv?*env,?jclass){????//?jstring?->?std::string
std::string?str?=?"Hi,I?am?C++.";
jstring?result?=??env->NewStringUTF(str.c_str());????return?result;
}JNIEXPORT?void?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_nativeToJava(JNIEnv?*?env,?jclass,?jobject?obj){????//?調(diào)用Java方法
jclass?cls?=?env->FindClass("cn/taoweiji/nativemodule/NativeDemo");????//int?subtract(int?param1,?int?param2)?->?(II)I
jmethodID?mid?=?env->GetMethodID(cls,?"subtract",?"(II)I");????int?result?=?(int)?env->CallIntMethod(obj,?mid,?10,?2);//????std::cout<
//常見類型轉(zhuǎn)換例子
//String?getInfo();
//->?()Ljava/lang/String;
//PackageInfo?getPackageInfo(String?packageName,?int?flags);
//->?(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;;}
第五步:編譯生成JNI文件,按 +B (Product -> Build).
編譯后文件
根據(jù)自己的電腦環(huán)境,查找編譯后的文件,我的路徑是/Users/Wiki/Library/Developer/Xcode/DerivedData/DEMO_MAC_JNI-clxymnzifegyfaajsaattzgxqfbr/Build/Products/Debug/DEMO_MAC_JNI
第六步:編寫JNI接口package?cn.taoweiji.nativemodule;/**
*?包名和類名稱一定要和前面的C++頭文件對應(yīng)
*?cn_taoweiji_nativemodule_NativeDemo.h
*/public?class?NativeDemo?{????public?static?native?int?add(int?param1,?int?param2);????public?static?native?void?say(String?name);????public?static?native?String?getInfo();????public?static?native?void?nativeToJava(NativeDemo?nativeDemo);????public?int?subtract(int?param1,?int?param2)?{
System.out.println("NativeDemo:"?+?String.format("%s?-?%s?=?%s",?param1,?param2,?param1?-?param2));????????return?param1?-?param2;
}
}
第七步:調(diào)用C++public?class?Main?{????static?{
System.load("/Users/Wiki/Library/Developer/Xcode/DerivedData/DEMO_MAC_JNI-clxymnzifegyfaajsaattzgxqfbr/Build/Products/Debug/DEMO_MAC_JNI");
}????public?static?void?main(String[]?args)?{
System.out.println("Hello?World!");????????int?result?=?NativeDemo.add(1,?2);
System.out.println("1+2="?+?String.valueOf(result));
NativeDemo.say("Hello,I?am?Java.");
System.out.println("getInfo:"?+?NativeDemo.getInfo());
NativeDemo.nativeToJava(new?NativeDemo());
}
}
3. 在ANDROID項目中使用NDK
Android的JNI開發(fā),C++文件必須編寫在獨(dú)立的module里面,Java接口代碼可以編寫在app(module),也可以和C++放在同一個module,通過gradle關(guān)聯(lián)。詳細(xì)代碼請自行下載demo瀏覽gradle配置(NativeModule)
apply?plugin:?'com.android.library'android?{
compileSdkVersion?23
buildToolsVersion?"24.0.0?rc2"
defaultConfig?{
minSdkVersion?14
targetSdkVersion?23
versionCode?1
versionName?"1.0"
}
buildTypes?{
release?{
minifyEnabled?false
proguardFiles?getDefaultProguardFile('proguard-android.txt'),?'proguard-rules.pro'
ndk?{
moduleName?"joyrun"
stl?"stlport_static"
ldLibs?"log"//用于解決__android_log_print
abiFilters?"armeabi",?"armeabi-v7a",?"x86",?"x86_64",?"arm64-v8a"
//add?-fexceptions?to?allow?throw?error
//add?-w?to?"format?not?a?string?literal?and?no?format?arguments?[-Werror=format-security"
cFlags?"-w?-fexceptions"
}
}
}
}
dependencies?{
compile?fileTree(dir:?'libs',?include:?['*.jar'])
}
編寫JNI接口//?NativeDemo.javapackage?cn.taoweiji.nativemodule;public?class?NativeDemo?{????public?static?native?int?add(int?param1,?int?param2);
}
編寫C++接口代碼,JNI文件目錄默認(rèn)是module/src/main/jni,可以通過gradle配置改變//?cn_taoweiji_nativemodule_NativeDemo.h#include?#ifndef?_Included_cn_taoweiji_nativemodule_NativeDemo#define?_Included_cn_taoweiji_nativemodule_NativeDemo#ifdef?__cplusplusextern?"C"?{#endif/*
*?Class:?????cn_taoweiji_nativemodule_NativeDemo
*?Method:????add
*?Signature:?(II)I
*/JNIEXPORT?jint?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_add(JNIEnv?*,?jclass,?jint,?jint);#ifdef?__cplusplus}#endif#endif//?NativeDemo.cpp#include?"cn_taoweiji_nativemodule_NativeDemo.h"JNIEXPORT?jint?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_add(JNIEnv?*,?jclass,?jint?param1,?jint?param2){
jint?result?=?param1?+?param2;????return?result;
}
調(diào)用//?靜態(tài)加載static?{
System.loadLibrary("joyrun");
}//調(diào)用int?result?=?NativeDemo.add(1,2);
Log.i("1+2=",String.valueOf(result));
四、NDK開發(fā)要點(diǎn)
編譯文件分析
將NativeModule編譯后生成的aar文件后綴改成.zip解壓,可以發(fā)現(xiàn)里面有個jni文件,打開可以看到”armeabi”, “armeabi-v7a”, “x86”, “x86_64”, “arm64-v8a”等文件夾,再打開可以看到是以lib前綴的so格式文件,這就是編譯后的native層文件,我們平常引用的第三方庫(百度地圖)也是要添加這些文件到我們的libs文件夾,不同的名稱代表著不同平臺相關(guān)的編譯文件,市面上大多數(shù)的手機(jī)都是arm架構(gòu)CPU,x86架構(gòu)的手機(jī)幾乎沒人用(genymotion模擬器屬于x86平臺),所以我們通常發(fā)布APP不會考慮x86平臺,僅僅添加armeabi文件即可,但是在開發(fā)過程中建議也添加x86的so文件,以方便我們在模擬器上運(yùn)行。
運(yùn)行庫
Android平臺帶有一個微型的C運(yùn)行庫支持庫,成為系統(tǒng)運(yùn)行庫。該運(yùn)行庫不支持一下特性:C標(biāo)準(zhǔn)庫、異常支持、RTTI支持。NDK提供了用于補(bǔ)充系統(tǒng)運(yùn)行庫功能的一些額外的C++運(yùn)行庫。C++運(yùn)行庫C++異常支持C++RTTIC++標(biāo)準(zhǔn)庫系統(tǒng)庫NoNoNo
GAbi++NoYesNo
STLportNoYesYes
GNU STLYesYesYesSTLportSTLport是一個開源的、多平臺的C標(biāo)準(zhǔn)庫實(shí)現(xiàn)。它提供一個C標(biāo)準(zhǔn)庫頭文件的完整集合以及對RTTI的支持。
GNU STLGNU標(biāo)準(zhǔn)C庫,也叫l(wèi)ibstdc-v3,是Android NDK最全面的標(biāo)準(zhǔn)C運(yùn)行庫。它是一個正在開發(fā)的、以實(shí)現(xiàn)ISO標(biāo)準(zhǔn)C庫為目標(biāo)的開源項目。
gradle配置STL運(yùn)行庫引用
“armeabi”, “armeabi-v7a”, “x86”, “x86_64”, “arm64-v8a”等平臺配置
C++輸出logcat配置
一個編譯異常解決
異常捕獲//?生成so文件的名稱moduleName?"joyrun"//?引入STL標(biāo)準(zhǔn)庫stl?"stlport_static"//gnustl_static//用于解決__android_log_printldLibs?"log"abiFilters?"armeabi",?"armeabi-v7a",?"x86",?"x86_64",?"arm64-v8a"?//添加編譯的平臺//add?-fexceptions?to?allow?throw?error//add?-w?to?"format?not?a?string?literal?and?no?format?arguments?[-Werror=format-security"cFlags?"-w?-fexceptions"
LOGCAT輸出#include?#define?LOGI(...)??__android_log_print(ANDROID_LOG_INFO,?"tag_joyrun",?__VA_ARGS__)#define?LOGE(...)??__android_log_print(ANDROID_LOG_ERROR,?"tag_joyrun",?__VA_ARGS__)LOGE("Hello?Logcat");
類型轉(zhuǎn)換//?std::string?->?jstringstd::string?str?=?"Hello?World";
jstring?result?=??env->NewStringUTF(str.c_str());//?jstring?->?std::stringjstring?param;const?char?*param_char?=?env->GetStringUTFChars(param,?NULL);std::string?str?=?param_char;//?jboolean?兩個值?JNI_TRUE、JNI_FALSE
C++調(diào)用JAVA代碼
//Javapublic?static?native?void?nativeToJava(NativeDemo?nativeDemo);public?int?subtract(int?param1,?int?param2)?{
Log.e("NativeDemo",?String.format("%s?-?%s?=?%s",?param1,?param2,?param1?-?param2));return?param1?-?param2;
}
//C++JNIEXPORT?void?JNICALL?Java_cn_taoweiji_nativemodule_NativeDemo_nativeToJava(JNIEnv?*?env,?jclass,?jobject?obj){????//?調(diào)用Java方法
jclass?cls?=?env->FindClass("cn/taoweiji/nativemodule/NativeDemo");
jmethodID?mid?=?env->GetMethodID(cls,?"subtract",?"(II)I");????int?result?=?(int)?env->CallIntMethod(obj,?mid,?10,?2);????//常見類型轉(zhuǎn)換例子
//String?getInfo();
//->?()Ljava/lang/String;
//PackageInfo?getPackageInfo(String?packageName,?int?flags);
//->?(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;;}
一鍵生成從JAVA到C++接口代碼腳本
文件:autojavah.sh#!/bin/shexport?ProjectPath=$(cd?"../$(dirname?"$1")";?pwd)export?TargetClassName="co.runner.app.jni.NativeDemo"export?SourceFile="${ProjectPath}/app/src/main/java"export?TargetPath="${ProjectPath}/jni-joyrun/src/main/jni"cd?"${SourceFile}"javah?-d?${TargetPath}?-classpath?"${SourceFile}"?"${TargetClassName}"echo?-d?${TargetPath}?-classpath?"${SourceFile}"?"${TargetClassName}"
五、C++面向?qū)ο蠹皹?biāo)準(zhǔn)庫入門
C++類定義//?Demo.hpp#ifndef?Demo_hpp#define?Demo_hpp#include?#include?class?Demo{public:????std::string?name;????int?age?=?0;????void?say();????static?int?add(int?param1,int?param2)
{????????return?param1?+?param2;
}
};#endif?/*?Demo_hpp?*/
類方法的實(shí)現(xiàn)//?Demo.cpp#include?"Demo.hpp"#include?void?Demo::say()
{????std::cout<
}
對象創(chuàng)建及訪問對象的成員//對象創(chuàng)建Demo?d1;
Demo?*?d2?=?new?Demo;//?運(yùn)算符訪問d1.say();//?指針訪問d2->say();//靜態(tài)函數(shù)訪問int?result?=?Demo::add(1,2);std::cout<
LIST鏈表//include相關(guān)文件#include?#include?#include?#include?"Demo.hpp"//?鏈表定義std::list?*?demos?=?new?std::list;
Demo?*?demo?=?new?Demo;
demo->name?=?"Wiki";
demo->age?=?24;//?在后面插入數(shù)據(jù)demos->push_back(*demo);
demo?=?new?Demo;
demo->name?=?"Wiki2";
demo->age?=?25;//?在前面插入數(shù)據(jù)demos->push_front(*demo);//?順序鏈表遍歷for?(std::list::iterator?iter?=?demos->begin();?iter?!=?demos->end();?++iter)?{
iter->say();
}//?反順序鏈表遍歷for?(std::list::reverse_iterator?iter?=?demos->rbegin();?iter?!=?demos->rend();?++iter)?{
iter->say();
}//?獲取指定位置元素std::list::iterator?iter?=?demos->begin();
advance(iter,?1);
iter->say();
指針、引用和值
在C++中,函數(shù)可以用許多不同的方法傳遞參數(shù),比如通過指針、引用或是直接傳值。//?通過指針void?handle1(Demo?*p);//?通過引用void?handle1(Demo&?p);//?通過值void?handle1(Demo?*demo);
堆和棧的理解棧(stack):由操作系統(tǒng)自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧;
堆(heap): 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時可能由OS回收,分配方式倒是類似于鏈表。Demo?d1;//棧Demo?*?d2?=?new?Demo;//堆char?c;?//棧上分配char?*p?=?new?char[3];?//堆上分配,將地址賦給了p;
垃圾回收
new和C++ delete運(yùn)算符用于動態(tài)分配和撤銷內(nèi)存的運(yùn)算符,自己new出來的對象就要通過delete釋放內(nèi)存。delete?demos;
六、開發(fā)中常用的方法及注意事項(坑)時間戳獲取方法。long?time?=?time(0);注意so文件防盜用問題。
盡量避免調(diào)用與平臺有關(guān)的方法,避免移植帶來問題。
JNI接口的包名不能帶有下劃線‘_’。
相關(guān)源代碼:
總結(jié)
以上是生活随笔為你收集整理的android java和c混合编程_C/C++在Java、Android和Objective-C三大平台下实现混合编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java字母反过来_java之字母反转~
- 下一篇: java序列化异常_Java|序列化异常