SWIG與JAVA 交互最全開發(fā)指南一

項目背景

最近開始研究做移動端項目,但是本人基本是做了五六年的c++的底層研發(fā),對C++的研發(fā)可以說是駕輕就熟了,但是對于android還是屬于剛?cè)腴T階段,雖然斷斷續(xù)續(xù)做移動端也做了一年,但是沒有一個系統(tǒng)的去研究過android,怎么才能結(jié)合兩者的優(yōu)勢呢。我決定用C++寫底層。

  • ANDROID是運行JAVA虛擬機層,編譯的是字節(jié)碼,效率不會太高,特別是密集型CPU計算。
  • 希望用更快速,更簡單的方法調(diào)用已有的技術(shù),而不用重新來一套。
  • 防止核心代碼被別人反編譯破解。
    所以我想到了使用SWIG封裝技術(shù)。

使用SWIG介紹

SWIG是個幫助使用C或者C++編寫的軟件能與其它各種高級編程語言進行嵌入聯(lián)接的開發(fā)工具。SWIG能應(yīng)用于各種不同類型的語言包括常用腳本編譯語言例如Perl, PHP, Python, Tcl, Ruby and PHP。支持語言列表中也包括非腳本編譯語言,例如C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, OCAML以及R,甚至是編譯器或者匯編的計劃應(yīng)用(Guile, MzScheme, Chicken)。SWIG普遍應(yīng)用于創(chuàng)建高級語言解析或匯編程序環(huán)境,用戶接口,作為一種用來測試C/C++或進行原型設(shè)計的工具。SWIG還能夠?qū)С鯴ML或Lisp s-expressions格式的解析樹。SWIG可以被自由使用,發(fā)布,修改用于商業(yè)或非商業(yè)中。

SWIG從C++到Android開發(fā)的五個步驟

SWIG是一個開源的C++到其他語言包裝技術(shù),你可以下載它的源碼自己編譯,也可以直接下載編譯好的二進制包,如果沒有啥特殊的需求,使用編譯好的EXE就可以了。不用自己花心思去處理。從網(wǎng)上下載SWIG并下載一份官方文檔,解壓并將其目錄加到PATH的環(huán)境目錄中。

編寫C++代碼

與平常寫C++代碼沒有區(qū)別,但是為了方便,一般情況下我們會寫一個C++的接口文件,之后寫一個實現(xiàn)類來集成那個接口

  1. c++的接口文件(ishapeprovider.h)
//這是一個讀寫SHAPE數(shù)據(jù)的接口類;文件名小寫測
class IShapeProvider
{
public:
    //打開一個文件或者一個數(shù)據(jù)庫
    virtual void openFile(const std::string& file_name) = 0;
    //添加一個記錄到文件中,其中IFeature是一個自定義接口類;
    virtual void addFeatrue(IFeature* feature) = 0;
    //設(shè)置一個屬性;
    virtual void setProperty(const std::string& key, const Variant& variant) = 0;
    //打開一個圖層,ITable也是一個自定義接口類
#ifndef SWIG  //這個宏是SWIG的特有的宏,告訴SWIG在處理的時候不要將此函數(shù)暴露給JAVA,對C++代碼不受影響
    virtual ITable* table(const std::string& table_name) = 0;
#endif
    //獲取一個圖層的外包范圍
    #ifdef SWIG   //對于傳指針的處理方式
    %apply void &OUTPUT{ (int* x,int* y,int*with,int*height};
#endif
    virtual void extent(int* x,int* y,int*with,int*height)=0;

//獲取類的信息,由于toString方法在Java中的方法沖突,所以我們需要給該函數(shù)改一個名字
#ifdef SWIG
%rename (toJavaString)  toString;//()里面是修改之后的函數(shù)名字,toString是在C++的函數(shù)名字;
#endif
    virtual std::string toString()=0;

};
  1. C++的實現(xiàn)文件(shapeProvider.h)
class ShapeProvider:public IShapeProvider
{
public:
    //打開一個文件或者一個數(shù)據(jù)庫
    virtual void openFile(const std::string& file_name)
    {
        //實現(xiàn)省略
    }
    //添加一個記錄到文件中,其中IFeature是一個自定義接口類;
    virtual void addFeatrue(IFeature* feature)
    {
        //實現(xiàn)省略
    }
    //設(shè)置一個屬性;
    virtual void setProperty(const std::string& key, const Variant& variant)
    {
        //實現(xiàn)省略
    }
    //打開一個圖層,ITable也是一個自定義接口類
    virtual ITable* table(const std::string& table_name)
    {
        //實現(xiàn)省略
    }
       //其他實現(xiàn)....
private:
     void initTableInformaction() {}
private:
    QFile _file;//QT類
    OGRDriver* _driver;//GDAL類
    
};
  1. 類實例化封裝類(classfactory.h)
class ClassFactory
{
public:
    //打開一個文件或者一個數(shù)據(jù)庫
    virtual IShapeProvider* newInstance(const std::string& class_name) 
    {
//實現(xiàn)在CPP文件中
        if (class_name == "IShapeProvider")
        {
            return new ShapeProvider();
        }
    };
    virtual void releaseClass(IShapeProvider* shape_provider)
    {
        delete shape_provider;
    }

};

對于一個比較龐大的項目,按上面的方式去實現(xiàn),有以下幾個原因

  • 為了隱藏實現(xiàn)類的實現(xiàn)結(jié)構(gòu),暴露類的結(jié)構(gòu)容易造成各種依賴問題;
  • swig技術(shù)以最簡單的方式包裝出去,特別是引用第三方庫的時候,如果直接用一個實現(xiàn)類的頭文件,那么就有可能需要依賴第三方庫的頭文件,在包裝的時候就要考慮這個第三方頭文件的問題。
  • 做框架接口與實現(xiàn)分離, 對內(nèi)部實現(xiàn)的以及變量的改動不影響接口的修改,不用重新編譯所有模塊

寫SWIG的i文件

%module(directors="1") dandelion_swig   //指定模塊名 directors="1" 代表可以對C++的類在JAVA中繼承
%{
    #include "ishapeprovider.h"   //在后面生成的dandelion_swig.cxx代碼中包含的頭文件
   #include "classfactory.h"
}%
  %include "ishapeprovider.h"   //將該頭文件所有的類、宏定義、以及全局變量包裝到JAVA中,生成對應(yīng)的類
  %include "classfactory.h"  
//添加系統(tǒng)的一些文件,處理一些常用的基本類型,具體可以參考SWIG的幫助文檔
%include stdint.i
%include carrays.i
%include windows.i
%include typemaps.i
#ifdef SWIGJAVA   //swig到j(luò)ava的swig預(yù)定義宏
%include <enums.swg>
%rename (toDString) toString;
#endif
//對于這種結(jié)構(gòu)的定義,可以理解為改名,不改名在JAVA中是識別不了的,如智能指針
%template(JavaList) std::list<long>;
%template(IShapeProviderPtr) std::shared_ptr<IShapeProvider>;

i文件書寫簡要說明

STL/C++庫的轉(zhuǎn)化 swig提供了很多基本類型庫的轉(zhuǎn)化。如std::map 可以轉(zhuǎn)成java的map,std::string 轉(zhuǎn)成String
可以在C++寫接口在JAVA中實現(xiàn),如要對IShapeProvider接口用java 實現(xiàn),需要調(diào)用
%feature("director") IShapeProvider ;
一般在c++文件中,可通過宏定義加入swig的代碼,這樣方便管理,當(dāng)接口文件多了的時候要增加或者刪除某些功能可一起刪除。

使用SWIG將C++包裝成JAVA類

SWIG生成的文件一個是CXX的C文件,以及一些JAVA的類文件,還有就是模塊內(nèi)的全局變量文件

  • swig 腳本編寫
swig.exe -c++ -java -package com.simple.dandelion -outdir ./java_simple_wrapper/src/com/simple/dandelion -o ./java_simple_swig/src/java_dandelion_swig.cxx -Iswig_dandelion.i 

說明:

  • -c++ 指定當(dāng)前語言是C++還是C,默認是C,只有這兩種,沒有其他的
  • -java 生成的包裝語言,可以使其他任何一種支持的語言 如-python -csharp
  • -package 生成的java包名
  • -outdir java文件的輸出目錄
  • -o 輸出的CXX文件的路徑文件名
  • -I i文件路徑(文件名必須緊跟參數(shù))
  • swig 命令執(zhí)行完成之后,將在當(dāng)前路徑下面生成以下幾個文件
  • dandelion_swig_wrap.cxx c++文件,包裝文件,將C++的類的方法轉(zhuǎn)成C語言的函數(shù)
  • dandelion_swig.java 與SWIG定義中module名字同名的類
  • dandelion_swigJni.java c++類中的方法在此文件中轉(zhuǎn)為java的靜態(tài)方法,就是JAVA中的native方法
  • 其他的JAVA類為C++中對應(yīng)的類

編譯SO庫

使用android stuido 新建一個包含C++支持的工程,使用CMAKE將dandelion_swig_wrap.cxx 編譯為SO文件。以下是參考鏈接

將JAVA文件編譯為JAR包

方法很簡單,在android studio 中建立一個庫項目,將SWIG生成的所有的JAVA文件按照包名建立路徑,拷貝到所在包的路徑下面,直接用android studio 編譯就可以了。

android 調(diào)用so庫使用

//MainActivity類
public class MainActivity extends QtActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        JavaNatives.init(this);
        String text=JavaNatives.stringFromJNI();
        TextView tv = (TextView) findViewById(R.id.simple_text);
        tv.setText(text);
    }
}
import  android.content.Context;
import  android.util.Log;
import  com.simple.dandelion.*;


public class JavaNatives {

    public static void init(Context context) {
        System.loadLibrary("simple");//本來模塊調(diào)用的simple庫
        System.loadLibrary("dandelion_swig");//swig包裝的CXX文件生成的SO,加載順序必須先加載其他庫。
        ClassFactory class_factory=new ClassFactory(); //c++中的類
        IShapeProvider shape_provider=class_factory.newInstance("ShapeProvider");
        shape_provider.openFile("/sdcard/simple.shp");

    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容