1. 程式人生 > >Free Pascal (Lazarus)版Android NDK的應用示例之:常用資料型別的轉換

Free Pascal (Lazarus)版Android NDK的應用示例之:常用資料型別的轉換

         lazarus為free pascal 提供良好的IDE(在我看來,Delphi已死),可編譯出各種平臺的目標程式,而且大前提是免費的!我想這是free pacal的魅力所在。 android 正如日中天,獨霸一方,lazarus也提供了編譯到android目標的方法,但於作者各種嘗試後感覺,其UI實現渣了點,顯然還處於起步階段。 但非UI的銜接相容的很好,用free pascal寫android NDK一點也不輸C。

         好了,題外話不多說,以下我提供一些最常用的資料型別轉換函式,用作android呼叫 free pascal進行資料運算絕對沒問題。
android_jni_2下有兩大目錄和三個pas檔案:android是android project的目錄,直接在ADT裡面匯入即可;native為 lazarus工程目錄,jni_demo3.lpr是工程檔案,簡單幾個演示function
示例如何呼叫這些轉換函式;三個pas檔案,分別是jni.pas,jniutil.pas,和log.pas,jni就是常規的jni不用多說,jniutil是我寫的若干個資料型別轉換函式,log是日誌輸出函式。

以下分別介紹jniutil的幾個函式及應用:
以下兩個String JString互相轉換的function是來自chenyuchih的,謝謝他的奉獻。
function JNI_JStringToString(Env: pJNIEnv; JStr: JString): string; cdecl;
  //Java String轉Pascal String
var
  pAnsiCharTMP: pAnsiChar;
  IsCopy: JBoolean;
  pIsCopy: pJBoolean;

begin
  IsCopy := JNI_TRUE;
  pIsCopy := @IsCopy;

  if (JStr = nil) then
  begin
    Result := '';
    Exit;
  end;

  pAnsiCharTMP := Env^^.GetStringUTFChars(Env, JStr, pIsCopy);
  if (pAnsiCharTMP = nil) then
  begin
    Result := '';
  end
  else
  begin
    Result := StrPas(pAnsiCharTMP); //將pAnsiChar轉為Pascal String後作為回傳值
    Env^^.ReleaseStringUTFChars(Env, JStr, pAnsiCharTMP);
    //輸入字串相關的轉換資源用完要釋放,否則會產生記憶體洩漏問題!
  end;
end;

function JNI_StringToJString(Env: pJNIEnv; Str: string): jstring; cdecl;
begin
  Result := Env^^.NewStringUTF(Env, @Str[1]); //依Pascal String內容建立新的Java String回傳
end;

以下是字串陣列的互相轉換:

function JNI_JStringArrayToStrings(Env: PJNIEnv; jStrings: jarray): TStringDynArray;cdecl;
var
  len, i: jsize;
  w_ret: TStringDynArray;
  obj: jobject;
begin
  len := Env^^.GetArrayLength(env, jStrings);
  SetLength(w_ret, len);
  for i := 0 to len - 1 do
  begin
    obj := env^^.GetObjectArrayElement(env, jStrings, i);
    w_ret[i] := JNI_JStringToString(Env, jString(obj));
  end;
  Result := w_ret;
end;

function JNI_StringsToJStringArray(Env: PJNIEnv; strings: TStringDynArray): jarray;cdecl;
var
  i, len: cardinal;
  w_ret: jarray;
begin
  len := length(strings);
  //開始忘了在java裡面String是Object,轉了很大彎
  w_ret := Env^^.NewObjectArray(Env, Len,Env^^.FindClass(Env,'java/lang/String'),JNI_StringToJString(Env,''));
  for i := 0 to len - 1 do
  begin
    Env^^.SetObjectArrayElement(Env, w_ret, i, JNI_StringToJString(env, strings[i]));
  end;
  Result := w_ret;
end;

以下是整形陣列的互相轉換:
function JNI_JIntArrayToIntegers(Env: PJNIEnv; jarr: jintArray): TIntegerDynArray;cdecl;
var
  w_ret: TIntegerDynArray;
  len: jint;
begin
  len := Env^^.GetArrayLength(Env, jarr);
  SetLength(w_ret, len);
  Env^^.GetIntArrayRegion(Env, jarr, 0, len, @w_ret[0]);
  Result := w_ret;
end;

function JNI_IntegersToJIntArray(Env: PJNIEnv; arr: TIntegerDynArray): JIntArray;cdecl;
var
  len: cardinal;
  intArray: JIntArray;
begin
  if (arr = nil) then
  begin
    Result := nil;
    Exit;
  end;

  len := High(arr) + 1;
  intArray := Env^^.NewIntArray(Env, Len);
  Env^^.SetIntArrayRegion(Env, intArray, 0, len, @arr[0]);
  Result := intArray;
end;


以下是位元組陣列的互相轉換:
{ Convert byte array to JByteArray }
function JNI_BytesToJByteArray(Env: PJNIEnv; Bytes: TByteDynArray): JByteArray;cdecl;
var
  len: cardinal;
  byteArray: JByteArray;
begin
  if (bytes = nil) then
  begin
    Result := nil;
    Exit;
  end;

  len := High(Bytes) + 1;
  byteArray := Env^^.NewByteArray(Env, Len);
  Env^^.SetByteArrayRegion(Env, byteArray, 0, len, @Bytes[0]);
  Result := byteArray;
end;

function JNI_JByteArrayToBytes(Env: PJNIEnv; JBytes: jbyteArray): TByteDynArray;cdecl;
var
  len: jsize;
  w_ret: TByteDynArray;
begin
  len := Env^^.GetArrayLength(env, JBytes);
  SetLength(w_ret,len);
  Env^^.GetByteArrayRegion(env, JBytes, 0, len, @w_ret[0]);
  Result := w_ret;
end;


還有一個丟擲異常的函式:
procedure JNI_ThrowException(Env: PJNIEnv; E: Exception);cdecl;
var
  jc: JObject;
begin
  jc := Env^^.FindClass(Env, 'java/lang/Exception');
  Env^^.ThrowNew(Env, jc, PChar(E.Message));
end;

===========

以下是Native程式碼:

library jni_demo3;

{$mode objfpc}{$H+}
{*******************************************************************************
    jni_demo3 by lrh ([email protected])

    非常感謝fpc和lazarus的各位大神無私奉獻,讓 pascal語言能同行各平臺(包括現在
    流行的android),謝謝貓工、delphicn的引路,也謝謝臺灣的chenyuchih給啟蒙。

    小弟化了一天半的時間作了如下最常用的資料型別轉換function,希望能幫助大家。
    free pascal雖然偏冷,但並不代表她不優秀,要發揚光大得靠大家努力^_^。

    main project source 只是簡單帶出這幾個function的一些應用示例,具體如何呼叫還要
    看android那邊的java程式碼。
 *******************************************************************************}
uses
  Classes, jniutil,types,sysutils,log,
  jni in '../jni.pas';
function JNI_OnLoad(vm: PJavaVM; reserved: pointer): jint; cdecl;
begin
  Result := JNI_VERSION_1_6;
end;

procedure JNI_OnUnload(vm:PJavaVM;reserved:pointer); cdecl;
begin
end;

function TestAddint(env: pJniEnv; this: jObject; a, b: jInt): jInt; cdecl;
begin
  LOGW(pchar('a='+inttostr(a)+',b='+inttostr(b)));
  result:=a+b;
end;
function testSubfloat(env: pJniEnv; this: jObject; a, b: jdouble): jdouble; cdecl;
begin
  LOGW(pchar('a='+FloatToStr(a)+',b='+FloatToStr(b)));
  result:=a-b;
end;
function testString(env: pJniEnv; this: jObject; str:jstring): jstring; cdecl;
begin
   result:=JNI_StringToJString(env,'hello,'+JNI_JStringToString(env,str)+'.\n你好,'+JNI_JStringToString(env,str));
end;
function testByteArray(env: pJniEnv; this: jObject; jbytes:jbyteArray): jbyteArray; cdecl;
var w_vals:TByteDynArray;
begin
   LOGW('entry.');
   w_vals:=JNI_JByteArrayToBytes(env,jbytes);
   LOGW(pchar('length(w_vals):'+inttostr(length(w_vals))));
   if length(w_vals)>=3 then begin
     w_vals[0]:=w_vals[0]+1;
     w_vals[1]:=w_vals[1]+1;
     w_vals[2]:=w_vals[2]+1;
   end;
   result:=JNI_BytesToJByteArray(env,w_vals);
end;
function testIntegerArray(env: pJniEnv; this: jObject; jIntegers:jintArray): jintArray; cdecl;
var w_vals:TIntegerDynArray;
begin
   w_vals:=JNI_JIntArrayToIntegers(env,jIntegers);
   if length(w_vals)>=3 then begin
     w_vals[0]:=w_vals[0]+1;
     w_vals[1]:=w_vals[1]+1;
     w_vals[2]:=w_vals[2]+1;
   end;
   result:=JNI_IntegersToJIntArray(env,w_vals);
end;
function testStringArray(env: pJniEnv; this: jObject; jstrs:jarray): jarray; cdecl;
var w_vals:jniutil.TStringDynArray;
begin
   w_vals:=JNI_JStringArrayToStrings(env,jstrs);
   if length(w_vals)>=3 then begin
     w_vals[0]:='第一行:'+w_vals[0];
     w_vals[1]:='第二行:'+w_vals[1];
     w_vals[2]:='第三行:'+w_vals[2];
   end;
   result:=JNI_StringsToJStringArray(env,w_vals);
end;
procedure testThrowException(env: pJniEnv; this: jObject; message:jstring); cdecl;
begin
  JNI_ThrowException(env,Exception.Create(JNI_JStringToString(env,message)));
end;

exports
  testAddint name 'Java_jnidemo_JniDemo_testAddint',
  testSubfloat name 'Java_jnidemo_JniDemo_testSubfloat',
  testString name 'Java_jnidemo_JniDemo_testString',
  testByteArray name 'Java_jnidemo_JniDemo_testByteArray',
  testIntegerArray name 'Java_jnidemo_JniDemo_testIntegerArray',
  testStringArray name 'Java_jnidemo_JniDemo_testStringArray',
  testThrowException name 'Java_jnidemo_JniDemo_testThrowException',
  JNI_OnLoad,
  JNI_OnUnLoad;
  //JNI函式命名原則:Java_<Java專案下的Package路徑,若當中有「.」則用「_」替換之,例如「com.example」則變成「com_example」>_<Class名稱>_<函式名稱>

begin
end.             


Android方面的程式碼:
package jnidemo;

public class JniDemo {
	static{
		System.loadLibrary("jni_demo3");
	}
	public static native int testAddint(int a,int b);
	public static native double testSubfloat(double a,double b);
	public static native String testString(String str);
	public static native byte[] testByteArray(byte[] vals);
	public static native int[] testIntegerArray(int[] vals);
	public static native String[] testStringArray(String[] vals);
	public static native void testThrowException(String message);
}



Activity的呼叫例子:
	public static class PlaceholderFragment extends Fragment {

		public PlaceholderFragment() {
		}

		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			View rootView = inflater.inflate(R.layout.fragment_main, container,
					false);
			 ((Button)rootView.findViewById(R.id.btn_int)).setOnClickListener(new View.OnClickListener() {
				
				@Override
				public void onClick(View v) {
					//整數傳遞
					int a=123;
					int b=234;
					int c=JniDemo.testAddint(a, b);
					Toast.makeText(_this, a+"+"+b+"="+c, Toast.LENGTH_SHORT).show();								
				}
			});
			 ((Button)rootView.findViewById(R.id.btn_float)).setOnClickListener(new View.OnClickListener() {					
				@Override
				public void onClick(View v) {
					double a=1.2345;
					double b=0.2045;					
					double c=JniDemo.testSubfloat(a, b);
					Toast.makeText(_this, a+"+"+b+"="+c, Toast.LENGTH_SHORT).show();								
				}
			});
			 ((Button)rootView.findViewById(R.id.btn_string)).setOnClickListener(new View.OnClickListener() {					
				@Override
				public void onClick(View v) {
					//string的互相傳遞,繁簡中文沒問題
					String w_ret=JniDemo.testString("rock(陳大文)");
					Toast.makeText(_this, w_ret, Toast.LENGTH_SHORT).show();								
				}
			});
			 ((Button)rootView.findViewById(R.id.btn_bytes)).setOnClickListener(new View.OnClickListener() {					
				@Override
				public void onClick(View v) {
					byte[] w_val=new byte[3];
					w_val[0]=1;
					w_val[1]=2;
					w_val[2]=3;
					w_val=JniDemo.testByteArray(w_val);
					Toast.makeText(_this, String.format("[0]=%d, [1]=%d,[2]=%d",w_val[0],w_val[1],w_val[2]), 
							Toast.LENGTH_SHORT).show();								
				}
			});
			 
			 ((Button)rootView.findViewById(R.id.btn_integers)).setOnClickListener(new View.OnClickListener() {					
				@Override
				public void onClick(View v) {
					int[] w_val=new int[3];
					w_val[0]=10;
					w_val[1]=20;
					w_val[2]=30;
					w_val=JniDemo.testIntegerArray(w_val);
					Toast.makeText(_this, String.format("[0]=%d, [1]=%d,[2]=%d",w_val[0],w_val[1],w_val[2]), 
							Toast.LENGTH_SHORT).show();								
				}
			});
			 ((Button)rootView.findViewById(R.id.btn_exception)).setOnClickListener(new View.OnClickListener() {					
				@Override
				public void onClick(View v) {
					try{
						JniDemo.testThrowException("這是一個異常!");
					}
					catch (Exception e){
						Toast.makeText(_this, e.getMessage(), Toast.LENGTH_SHORT).show();
					}
				}
			});

			 ((Button)rootView.findViewById(R.id.btn_strings)).setOnClickListener(new View.OnClickListener() {					
				@Override
				public void onClick(View v) {
					String[] w_val=new String[3];
					w_val[0]="first(第一)";
					w_val[1]="second(第二)";
					w_val[2]="third(第三)";
					w_val=JniDemo.testStringArray(w_val);
					Toast.makeText(_this, String.format("[0]=%s, [1]=%s,[2]=%s",w_val[0],w_val[1],w_val[2]), 
							Toast.LENGTH_SHORT).show();								
				}
			});
			 
			return rootView;
		}
	}

執行如下圖:


程式碼下載:

相關推薦

Free Pascal (Lazarus)Android NDK應用示例常用資料型別轉換

         lazarus為free pascal 提供良好的IDE(在我看來,Delphi已死),可編譯出各種平臺的目標程式,而且大前提是免費的!我想這是free pacal的魅力所在。 android 正如日中天,獨霸一方,lazarus也提供了編譯到androi

free pascal(lazarus)android JNI進階篇反向呼叫java的方法並傳遞複雜引數

        這篇文章雖然短暫,但耗用了筆者一天的時間搜尋各種資料,除錯了無數次,在這裡跟大家分享是想大家碰到類似問題時少走彎路,請各位轉載註明出處,http://blog.csdn.net/rocklee 。         查閱了無數C、C++、pascal、java

NDK學習筆記jni資料型別轉換

背景 隨著Android專案中c++程式碼部分功能複雜程度的增加,jni中需要傳遞的資料型別也越來越多,關於jni資料型別轉換網上有不少相關文章,但是在使用時發現這些例子中存在不少謬誤,遂在此重新總結相關內容,並附相關例程,以便日後參考。 下文我們將對以下幾

Android四大應用元件BroadCastReceiver

廣播接收器 定義: 一個應用可以在發生特定事件時傳送Broadcast, 系統中任何應用只要註冊了對應Receiver就會接收到此Broadcast,一個應用如果對某個廣播感興趣, 就可以註冊對應的Receiver來接收廣播 廣播事件機制是應用程式(程

Android四大應用元件ContentProvider初探

理解 首先談一談為什麼要有ContentProvider? 當一個應用想要訪問另一個應用的資料庫時,由於每個應用的資料庫檔案時應用私有的,不能直接訪問,這時,被訪問的應用就需要一個對外的資料庫內容提供者,也就是ContentProvider。 <p

Android NDK學習起航先買一艘船

      感覺Android寫了這麼久了, 也該學點裝逼的技能了。所以我把目光放在了NDK和C++上,只要玩轉了這兩個, 我就能去玩OpenCV和TensorFlow了,想想就美滋滋。今天先來基礎配置一個最簡單的NDK環境。基於AndroidStudio 3.2,使用Cma

神經網路(四)應用示例分類

一、 傳統分類模型的侷限 在之前的文章中(《神經網路(一)》、《神經網路(二)》和《神經網路(三)》),我們討論的重點是神經網路的理論知識。現在來看一個實際的例子,如何利用神經網路解決分類問題。(為了更好地展示神經網路的特點,我們在這個示例中並不劃分訓練集和測

Android逆向基礎筆記—Android NDK開發2Windows下的gcc手動編譯(交叉連編譯)和利Linux Ubuntu系統下的交叉工具鏈手動編譯

一、交叉工具鏈 這些工具都在NDK的路徑下:E:\Android\android-ndk-r13\toolchains\arm-linux-androideabi-4.9\prebuilt\windo

ArcGIS Android API 應用開發圖形繪製與長度面積量算

本文通過一個簡單的小Demo,向大家介紹如何使用ArcGIS Android API繪製點、線、面圖形,並且測量線的長度和多邊形的面積。 首先來看一下程式執行的效果圖: 實現上述功能,首先需要向地圖中新增一個GraphicsLayer,用來繪製Point、Poly

Android系統應用---SystemUI狀態列電池圖示的顯示和Android電池管理的探討

電池圖示顯示 電池圖示是SystemUI顯示中不可缺少的一部分,它顯示在SystemUI的電池和訊號組合區域。 從佈局來看,電池的顯示屬於status_bar.xml,包含了system_icons.xml佈局 <includelayout="@layout/

Android NDK編譯的程式依賴obj資料夾下so

so引用方式不用PREBUILT的方式,就可以不依賴obj/local/.... #LOCAL_SHARED_LIBRARIES := *** LOCAL_LDFLAGS   := -pie -fPIE #LOCAL_LDFLAGS  += -Wl,--allow-shl

Android手機應用開發(七) | 資料儲存(下)

實驗目的 學習SQLite資料庫的使用。 學習ContentProvider的使用。 複習Android介面程式設計。 這次大概是做一個有登入、註冊、評論、點贊等功能的小型APP 效果如下:(圖片比較大) 登入註冊頁面的切換 兩個按鈕用RadioB

Android逆向基礎筆記—Android NDK開發4Android studio NDK自動編譯

這部分就是最後的部分了,為什麼要寫Android studio呢。大家知道,eclipse 到了現在,已經不被Google支援了,所以現在最好的開發就是利用Android studio。雖然說,網上有

Android NDK開發篇(一)新版NDK環境搭建(免Cygwin,超級快)

  以前做Android的專案要用到NDK就必須要下載NDK,下載安裝Cygwin(模擬Linux環境用的),下載CDT(Eclipse C/C++開發外掛),還要配置編譯器,環境變數...   麻煩到不想說了,Shamoo在網上查了一下資料,發現了一個超級快配置NDK的

JAVA 程式設計題解與上機指導(第四) 第二章·識別符號、關鍵字和資料型別 2.12

/*2.12 設計實現一個MyGraphic類及子類,它們代表一些基本圖形,這些圖形包括矩形、三角形、圓、橢圓、菱形、梯形等。 試給能描述這些圖形所必需的屬性及必要的方法 **/ public class MyGraphic { String lineColor;//線條顏色 Strin

Android中JNI使用詳解(4)---Java與C之間資料型別轉換

Jni中基本型別轉換對應的表格 Java型別 本地型別 說明 boolean jboolean 無符號,8位 byte jbyte

Android------JNI 資料型別轉換

  ndk可以用程式碼1呼叫 , 但例子中是用程式碼2。程式碼2可能是c++中的標準語法,程式碼1是ndk的。引用時要注意 程式碼1:env->GetStringUTFChars(str) 程式碼2:(*env)->NewStringUTF(env, st

Android開發小工具Chrome Custom Tabs

參考文章 官方文件 官方原始碼 http://qq157755587.github.io/2016/08/12/custom-tabs-best-practices/ https://juejin.im/entry/586f089c61ff4b006d29f9c0 一

Android進階2Activity之間資料交流(onActivityResult的用法)

                主要功能:在一個主介面(主Activity)上能連線往許多不同子功能模組(子Activity上去),當子模組的事情做完之後就回到主介面,或許還同時返回一些子模組完成的資料交給主Activity處理。這樣的資料交流就要用到回撥函式onActivityResult。<1>

全國計算機等級考試二級教程--python語言程式設計(2018年)第六章組合資料型別

宣告:本篇文章只是個人知識盲區、知識弱點、重點部分的歸納總結,望各位大佬不喜勿噴。梳理順序是按照書籍的實際順序梳理,轉載請註明出處。 作者:sumjess   一、組合資料型別的基本概念:       Python語言中最常用的組合資料型別