Важно (9.07.22)

Если картинки в постах не отображаются, зайдите в блог через прокси. РКН заблокировал поддомены blogger.com на которые загружались картинки.

четверг, 10 июля 2014 г.

Как создавать обёртки для JAVA-кода

В этой статье я постараюсь объяснить, как создавать обёртки, самым доступным языком и с примером. В блоге я уже выкладывал собственную обёртку для работы с Wi-Fi, вот её мы и рассмотрим в качестве примера. Почти всё, что написано ниже, справедливо для любых JAVA-классов. В скором времени я покажу, как работать с собственными JAVA-классами в Delphi.

Update (11.07.14) - Продолжение тут "Как подключить и использовать свой JAVA-класс"



Начнём.

Рассмотрим задачу на примере обёртки для Android API. Допустим, вы решили написать обёртку для package android.net.wifi.

Что нужно знать/понимать:

1)  Необязательно писать обёртку для всех классов и методов, можно писать только для необходимых вам классов/методов

2)  Все классы и методы можно найти в трёх местах
  • На сайте Android API
  • В файле «api-versions.xml», данный файл хранится в папке «C:\Users\Public\Documents\Embarcadero\Studio\14.0\PlatformSDKs\adt-bundle-windows-x86-20131030\sdk\platform-tools\api», в этом файле вы также найдёте сигнатуры
  • В вашем собственном исходнике JAVA-класса (об этом поговорим в след. статье)
3)  Для связи используются сигнатуры, записываются вот так (пример)
  [JavaSignature('android/net/wifi/WifiManager$MulticastLock')]

4)  Соответствие типов JAVA в Delphi
Java Type
Delphi Type
Type Name
Signature
byte
ShortInt
JByte
B
char
WideChar
JChar
C
double
Double
JDouble
D
float
Single
JFloat
F
int
Integer
JInt
I
long
Int64
JLong
J
short
SmallInt
JShort
S
void
N/A
N/A
V
boolean
Boolean
JBoolean
Z

Типы, в которых содержатся квадратные скобки, записываются как массивы. 

Пример:
  Имеем тип «public String[]», значит, запишем его как «TJavaObjectArray<JString>».

5)  Необходимо рандомно генерировать GUID – это можно сделать при помощи комбинации клавиш "Ctrl+Shift+G"

Пример:
  ['{1D32FC9E-D6EB-4F1F-9760-1E90D971D602}']

6)  Любой класс, расширяет какой-то другой класс.

Пример:
Класс «ScanResult», расширяет основной класс «java.lang.Object», значит, в обёртке мы также должны расширять «JavaObject».

Обёртка для «ScanResult» будет выглядеть вот так:
  JScanResultClass = interface(JObjectClass)
  …
  JScanResult = interface(JObject)

7)  Поля (Fields):
  • Если тип поля указан как «public String», т.е. имеет только слово «public», то это поле мы пишем в «interface(JObject)». Данное поле мы можем получать и изменять.
  • Если тип поля указан как «public static final String[]», то это поле мы пишем в «interface(JObjectClass)». Данное поле мы можем получить и не можем изменить.


8)  Константы (Constants):
  • Все константы всегда пишутся в «interface(JObjectClass)». Константы можно получать и нельзя изменять.

9)  Методы (Public Methods):
  • Если тип метода содержит слово «static», то такой метод записывается в «interface(JObjectClass)».
  • Если тип метода не содержит слово «static», то такой метод записывается в «interface(JObject)».

10)  Для нормальной работы обёртки, необходимо подключать минимум два модуля:
  • Androidapi.JNIBridge,
  • Androidapi.JNI.JavaTypes;

11)  В конце обёртки, желательно зарегистрировать типы:
procedure RegisterTypes;
begin
  TRegTypes.RegisterType('Название вашего модуля', TypeInfo(‘Название вашего модуля’.’Название интерфейса’)); 
end;

initialization

  RegisterTypes;

end.

На этом всё, теперь рассмотрим примеры:

Примечание: Кучу различных обёрток вы можете найти в исходниках студии, они лежат примерно тут «C:\Program Files\Embarcadero\Studio\14.0\source\rtl\android».


Пишем обёртку для классов «ScanResult» и «WifiInfo».

Запускаем студию, создаём Unit, назовём его «Androidapi.JNI.Net.Wifi». 
Сразу выложу код:
unit Androidapi.JNI.Net.Wifi;

interface

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;

type
  {Class forward declarations}
  JScanResult = interface; //android.net.wifi.ScanResult
  JWifiInfo = interface; //android.net.wifi.WifiInfo

JScanResultClass = interface(JObjectClass)
['{5AEBB13C-C013-47CB-B120-F3D1FCFF9BE8}']
end;

[JavaSignature('android/net/wifi/ScanResult')]
JScanResult = interface(JObject)
['{1D32FC9E-D6EB-4F1F-9760-1E90D971D602}']
  {Property Methods}
  function _GetBSSID: JString;
  procedure _SetBSSID(Value: JString);
  function _GetSSID: JString;
  procedure _SetSSID(Value: JString);
  function _Getcapabilities: JString;
  procedure _Setcapabilities(Value: JString);
  function _Getfrequency: Integer;
  procedure _Setfrequency(Value: Integer);
  function _Getlevel: Integer;
  procedure _Setlevel(Value: Integer);
  {Methods}
  function toString: JString; cdecl;
  {Properties}
  property BSSID: JString read _GetBSSID write _SetBSSID;
  property SSID: JString read _GetSSID write _SetSSID;
  property capabilities: JString read _Getcapabilities write _Setcapabilities;
  property frequency: Integer read _Getfrequency write _Setfrequency;
  property level: Integer read _Getlevel write _Setlevel;
end;
TJScanResult = class(TJavaGenericImport<JScanResultClass, JScanResult>) end;

JWifiInfoClass = interface(JObjectClass)
['{E34882C5-CD5D-469C-9020-513FC1C4E48A}']
  {Property Methods}
  function _GetLINK_SPEED_UNITS: JString;
  {Methods}
  //getDetailedStateOf(SupplicantState suppState): NetworkInfo.DetailedState; cdecl;
  {Properties}
  property LINK_SPEED_UNITS: JString read _GetLINK_SPEED_UNITS;
end;

[JavaSignature('android/net/wifi/WifiInfo')]
JWifiInfo = interface(JObject)
['{6E31D165-FE5E-49EF-BE9D-61A93C7A8EAB}']
  {Methods}
  function getBSSID: JString; cdecl;
  function getHiddenSSID: Boolean; cdecl;
  function getIpAddress: Integer; cdecl;
  function getLinkSpeed: Integer; cdecl;
  function getMacAddress: JString; cdecl;
  function getNetworkId: Integer; cdecl;
  function getRssi: Integer; cdecl;
  function getSSID: JString; cdecl;
  function getSupplicantState: JSupplicantState; cdecl;
  function toString: JString; cdecl;
end;
TJWifiInfo = class(TJavaGenericImport<JWifiInfoClass, JWifiInfo>) end;

implementation

procedure RegisterTypes;
begin
  TRegTypes.RegisterType('Androidapi.JNI.Net.Wifi.JScanResult', TypeInfo(Androidapi.JNI.Net.Wifi.JScanResult));
  TRegTypes.RegisterType('Androidapi.JNI.Net.Wifi.JWifiInfo', TypeInfo(Androidapi.JNI.Net.Wifi.JWifiInfo));
end;

initialization
  RegisterTypes;
end.

Как видите, получилось всего 80 строчек кода, ни чего сложного в них нет. Я внезапно понял, что и описывать-то в коде нечего уже, т.к. я всё изложил выше в 11 пунктах. Единственное, что ещё раз повторю, необязательно описывать все классы или методы, можно описать только необходимое, даже лучше так делать, т.к. чем больше всего описано, тем больше весит ваше приложение.

Дополнение: Как использовать полученную обёртку. Здесь приведена только часть обёртки и чтобы не захламлять статью, пример использования можно посмотреть тут.



Чуть не забыл сказать, обёртки для всего Android API выложены тут Full Android SDK Interface Files In Object Pascal For Firemonkey, но не забывайте проверять их, т.к. они сгенерированы автоматически и имеют некоторое несоответствие пунктам выше.

Спасибо за внимание.

p.s. В следующей статье вы узнаете, как подключать к проекту любой JAVA-код, как создавать файл classes.dex и т.д.