пятница, 11 июля 2014 г.

Как подключить и использовать свой JAVA-класс

В этой статье вы узнаете, как подключить свой JAVA-класс, как создаётся файл «classes.dex», как пишутся обёртки (об этом уже писал) и как потом использовать подключенный JAVA-код. Советую читать и изучать материал внимательно, чтобы все ваши вопросы решились сами собой.












Начнём!

Составим план того, что нам необходимо:
  1. Иметь сторонний JAVA-класс или написать самостоятельно
  2. Написать обёртку для выбранного JAVA-класса
  3. Создать файл "classes.dex" и склеить со стандартным файлом
  4. Написать приложение, в котором используется наш класс
Ну вот, план готов, давайте приступать.

1) Иметь сторонний JAVA-класс или написать самостоятельно

В данной статье, для примера, я написал собственный простейший Java-класс. Вы же можете попробовать подключить любой другой Java-класс, в интернете полно готового кода. Писать их можно где угодно, я делал это в среде «Eclipse» (Скачать можно тут Get the Android SDK). 

О том, как писать код на языке Java читайте на просторах интернета.

Класс, который я написал, имеет два метода:
Один статический, второй обычный. Это сделано для того, чтобы потом (в последнем шаге) показать, как использовать их в коде.

Мой класс принадлежит к пакету «TestClassHello» и имеет имя «HelloWorld», хранится по такому пути «C:\Users\Infocean\workspace\TestClass\src\TestClassHello\HelloWorld.java».

Код класса:
package TestClassHello;

public class HelloWorld {

 /**
  * @param args
  */

 public static String getStrStatic(String str) {

  String strAnswer;
  String strHelloDelphi = "hello from delphi!";

  if (str.toLowerCase().equals(strHelloDelphi))
   strAnswer = "Hello World!\nHello Delphi Community! ;)\n(JAVA! Static method)";
  else
   strAnswer = str + "\n(JAVA! Static method)";

  return strAnswer;
 }

 public String getStrNoStatic(String str) {
  return str + "\n(Java! Not a static method)";
 }

}

Что умеет класс.

Класс при использовании любого из двух методов, возвращает текст. Также в методы можно передавать любой текст, и он будет возвращён с добавлениями. А при определённой фразе, поздоровается со всем Delphi сообществом (хоть какое-то разнообразие :).

Теперь создадим папку (например, на рабочем столе) для нашего будущего приложения, пускай называется «testJAVA», в ней создадим папку «java» и скопируем в эту папку всё из «C:\Users\Infocean\workspace\TestClass\». 

Получим вот такое дерево папок:

В дальнейшем мы будем работать только с этой «testJAVA» папкой, поэтому можно забыть про всё остальное :)

Ну вот, первый пункт успешно выполнили. Быстренько переходим ко второму пункту, долго не задерживаемся.

2) Написать обёртку для выбранного JAVA-класса

Если вы посещаете/читаете мой блог, то скорее всего уже видели (и надеюсь читали) статью о том как создавать обёртки. Почти всё, что написано в той статье также справедливо и для обёрток сторонних Java-классов. Разница только в том, что список методов мы берём не с сайта, а из нашего JAVA-класса.

В классе всего два метода:
  • статический «getStrStatic» – значит, пишем его в interface(JObjectClass)
  • обычный «getStrNoStatic» – пишем в interface(JObject)
Оба метода принимают и возвращают строку.
Файл я так и назвал «TestClassHello».
Код обёртки для JAVA-класса:
unit TestClassHello;

interface

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;

type
  JHelloWorld = interface;

  JHelloWorldClass = interface(JObjectClass)
  ['{77C14F6E-4D89-419B-A721-366D48D6A671}']
    function getStrStatic(str: JString): JString; cdecl;
  end;

  [JavaSignature('TestClassHello/HelloWorld')]
  JHelloWorld = interface(JObject)
  ['{11121974-C65D-48B0-B9E2-B16E4F93B275}']
    {Property Methods}
    {Methods}
    function getStrNoStatic(str: JString): JString; cdecl;
    {Properties}
  end;
  TJHelloWorld = class(TJavaGenericImport<JHelloWorldClass, JHelloWorld>) end;


implementation

  procedure RegisterTypes;
  begin
    TRegTypes.RegisterType('TestClassHello', TypeInfo(TestClassHello.JHelloWorld));
  end;

initialization
  RegisterTypes;
end.

Обёртка готова, кладём её в корень папки «testJAVA». Половину пути прошли, теперь уже ни как нельзя бросать всё из-за лени, например)) Продолжаем…

3) Создать файл classes.dex и склеить со стандартным файлом
Файл с java реализацией дополнительного функционала FireMonkey.

Шаги создания:
  1. Компилируем наш класс
  2. Создаём jar, содержащий наш класс
  3. Конвертируем jar в dex
  4. Склеиваем стандартный classes.dex с нашим classes.dex

Для того чтобы создавать всё автоматически, уважаемый Brian Long написал bat-файл. Этим файлом мы и воспользуемся, изменив его под себя. Этот bat-файл, необходимо создать и сохранить в папке «\testJAVA\java\».

У всех этот файл выглядит по-разному, но выполняет одинаковые функции, вот вам мой, ниже будет небольшое описание.

Код файла «build.bat»:
@echo off

setlocal

if x%ANDROID% == x set ANDROID=C:\Users\Public\Documents\Embarcadero\Studio\14.0\PlatformSDKs\adt-bundle-windows-x86-20131030\sdk
set ANDROID_PLATFORM=%ANDROID%\platforms\android-19
set DX_LIB=%ANDROID%\build-tools\android-4.4\lib
set EMBO_DEX="C:\Program Files\Embarcadero\Studio\14.0\lib\android\debug\classes.dex"
set PROJ_DIR=%CD%
set VERBOSE=0

echo.
echo Compiling the Java service activity source files
echo.
mkdir output 2> nul
mkdir output\classes 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=-verbose
javac %VERBOSE_FLAG% -Xlint:deprecation -cp %ANDROID_PLATFORM%\android.jar -d output\classes src\TestClassHello\HelloWorld.java

echo.
echo Creating jar containing the new classes
echo.
mkdir output\jar 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=v
jar c%VERBOSE_FLAG%f output\jar\test_classes.jar -C output\classes TestClassHello

echo.
echo Converting from jar to dex...
echo.
mkdir output\dex 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=--verbose
call %DX_LIB%\dx.jar --dex %VERBOSE_FLAG% --output=%PROJ_DIR%\output\dex\test_classes.dex --positions=lines %PROJ_DIR%\output\jar\test_classes.jar

echo.
echo Merging dex files
echo.com.android.dx.merge.DexMerger
java -cp %DX_LIB%\dx.jar com.android.dx.merge.DexMerger %PROJ_DIR%\output\dex\classes.dex %PROJ_DIR%\output\dex\test_classes.dex %EMBO_DEX%

echo Tidying up
echo.
del output\classes\TestClassHello\HelloWorld.class
rmdir output\classes\TestClassHello
rmdir output\classes
del output\dex\test_classes.dex
del output\jar\test_classes.jar
rmdir output\jar

echo.
echo Now we have the end result, which is output\dex\classes.dex

:Exit

endlocal

Теперь разберём этот файл, процесс выполняется пошагово в соответствии с описанными выше шагами создания.

Что необходимо сделать, чтобы данный bat-файл заработал у вас:

а) Необходимо прописать глобальный путь до главной директории JAVA

Как это сделать (для Windows 7. Для остальных ОС можно почитать тут и тут):
  • Жмёте «Пуск» -> «Компьютер», далее жмёте «Свойства системы», там жмёте «Дополнительные параметры системы»
  • В открывшемся окне, выбираем вкладку «Дополнительно» и жмём на кнопку «Переменные среды…»
  • В открывшемся окне, в «Системных переменных» ищем переменную «Path», выбераем её и жмём «Изменить»
  • В конец строки добавляем путь до папки «bin», примерно такой «;C:\Program Files\Java\jdk1.7.0_25\bin», обратите внимание на точку с запятой в начале, это необходимо чтобы отделить добавляемый путь от уже указанных.
  • Жмём «OK» и всё закрываем.
Теперь командная строка умеет работать с JAVA командами.

б) Далее необходимо в коде изменить пути, в самом начале файла.
У меня в файле это:
  • ANDROID - Путь до SDK
  • ANDROID_PLATFORM – Путь до модуля SDK Platform
  • DX_LIB – каталог с библиотекой поддержки Dalvik, dx.jar
  • EMBO_DEX – путь до стандартного файла «classes.dex»
Стандартный файл «classes.dex» бывает двух видов:

Debug – «C:\Program Files\Embarcadero\Studio\14.0\lib\android\debug\classes.dex»
Release – «C:\Program Files\Embarcadero\Studio\14.0\lib\android\release\classes.dex»

в) Далее в 18 строке, в конце, необходимо изменить путь на свой или оставить также (если вы придерживаетесь статьи). Этот путь берётся из папки «\testJAVA\java\», если вы не забыли, то там у нас лежит наш класс «Desktop\testJAVA\java\src\TestClassHello\HelloWorld.java», путь указывается, начиная с папки «\src\».

На 25 строчке, в конце укажите название папки, которая вложена в папку «src». В моём случае это «TestClassHello». На 41 и 42 строчках также измените названия папок и имя класса.

Если вы всё сделали правильно, то можете смело запускать файл и смотреть результат.


Результат будет таким: в папке «java» появится папка «output», в которой будет лежать новый файл «classes.dex».


Ну вот, кончились мучения, теперь у нас есть файл «C:\Users\Infocean\Desktop\testJAVA\java\output\dex\classes.dex». Приступаем к последнему шагу.

4) Написать приложение, в котором используется наш класс

Приложение получилось простое:


Код:
  • В uses подключаем два модуля, в том числе и нашу обёртку: Androidapi.Helpers, TestClassHello;
  • Обращение к статическим методам происходит через "JavaClass", в коде это "Button1".
  • Обычные методы – перед обращением необходимо создать объект, в коде это "Button2".
uses
  Androidapi.Helpers, TestClassHello;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Text := 'Answer: ' + #13#10 +
  JStringToString(TJHelloWorld.JavaClass.getStrStatic(StringToJString(Edit1.Text)));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  TestClass: JHelloWorld;
begin
  TestClass := TJHelloWorld.Create;
  Label1.Text := 'Answer: ' + #13#10 +
  JStringToString(TestClass.getStrNoStatic(StringToJString(Edit1.Text)));
end;

Также необходимо задеплоить новый файл «classes.dex», а со стандартного снять галочку.


Результат:




На этом всё. Как видите всё получилось хорошо. Статья получилась большой, но надеюсь читабельной.

Часть статьи писалась давно, поэтому пришлось вносить некоторые изменения, чтобы она более соответствовала времени. В связи с этим возможны неточности, опечатки и т.п., об этом сообщайте мне на почту infocean @ gmail . com.

Благодарю Anatoly Zakusilov, именно ваши вопросы подтолкнули меня на то чтобы дописать эту статью :)

Если у вас возникли вопросы, то пишите в комментарии.

Исходный код: Скачать с Google Drive

p.s. Ну вот, юбилейная запись в блоге - 50-ая :)

13 комментариев:

  1. Подскажите, пожалуйста, как будет выглядеть код использования на С++ Builder XE6?

    ОтветитьУдалить
  2. Спасибо большое за статью, она пришла мне как раз в тему. Все понятно и доходчиво, но есть один вопрос.
    У меня вот такой JAVA класс
    public class SMSReceiver extends BroadcastReceiver
    {
    @Override
    public void onReceive(Context context, Intent intent)
    {
    Bundle myBundle = intent.getExtras();
    SmsMessage [] messages = null;
    String strMessage = "";

    if (myBundle != null)
    {
    Object [] pdus = (Object[]) myBundle.get("pdus");
    messages = new SmsMessage[pdus.length];

    for (int i = 0; i < messages.length; i++)
    {
    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
    strMessage += "SMS From: " + messages[i].getOriginatingAddress();
    strMessage += " : ";
    strMessage += messages[i].getMessageBody();
    strMessage += "\n";
    }

    Toast.makeText(context, strMessage, Toast.LENGTH_SHORT).show();
    }
    abortBroadcast();
    }
    }
    у которого есть событие onRecive. Как его можно использовать в Делфи, то есть, чтобы код исполнялся по событию onRecive в моем делфи-приложении.
    Заранее спасибо за ответ

    ОтветитьУдалить
  3. Спасибо за статью, возможно, она спасёт меня.

    Почему выдаётся такая ошибка:
    E2003 Undeclared identifier: 'TRegTypes'

    Полностью скопировал текст примера из статьи. В каком модуле описывается TRegTypes?

    ОтветитьУдалить
    Ответы
    1. Модуль Androidapi.JNIBridge.
      В XE7:
      Добавление java-библиотеки в проект - http://docwiki.embarcadero.com/RADStudio/XE7/en/What%27s_New_in_Delphi_and_C%2B%2BBuilder_XE7#Add_Java_Libraries_to_Your_Applications_for_Android_from_the_Project_Manager

      Автоматическая генерация обёртки - http://docwiki.embarcadero.com/RADStudio/XE7/en/What%27s_New_in_Delphi_and_C%2B%2BBuilder_XE7#Command-Line_Utility_Changes_for_XE7

      Удалить
    2. Ах, то есть дело в том, что у меня ХЕ5. Я обречён. )

      Благодарю.

      А не подскажете в соседней теме по поводу:

      http://delphifmandroid.blogspot.ru/2014/07/java.html?showComment=1419845534457#c2508662264066479928

      ?

      Удалить
    3. Статья писалась на XE6, на XE5 не пробовал, возможно, нужно сменить название модуля, т.к. от версии к версии, насколько я помню, менялись названия модулей, посмотрите в исходниках студии. Честно говоря, ещё не сталкивался с aidl файлами, но возможно обращение к ним из Делфи происходит также через обёртки.

      Удалить
  4. ALARMMANNAGER OR APPLICATION IN BACKGROUND, GPS FOR BRING TO SLEEP the coordinates.
    YOU HAVE SOME EXAMPLE OF HOW TO DO IT IN DELPHI

    ОтветитьУдалить
  5. Этот комментарий был удален автором.

    ОтветитьУдалить
  6. Пытаюсь по аналогии создать файл classes.dex, но при выполнении батника получаю ошибку:

    src\com\hoho\android\usbserial\driver\UsbSerialPort.java:87: error: cannot find symbol
    public UsbSerialDriver getDriver();
    ^
    symbol: class UsbSerialDriver
    location: interface UsbSerialPort

    Не подскажете в чем здесь дело?

    Заранее спасибо!

    Исходники java и батник: https://yadi.sk/d/gr6sePEKmiKud

    ОтветитьУдалить
  7. С батником разобрался. А вот вопрос: в интерфейсе надо прописывать все поля и методы класса или только используемые?

    ОтветитьУдалить
  8. Делаю bat файл как написано, только после строчки
    call %DX_LIB%\dx.jar --dex %VERBOSE_FLAG% --output=%PROJ_DIR%\output\dex\test_classes.dex --positions=lines %PROJ_DIR%\output\jar\test_classes.jar
    должен создаться файл test_classes.dex, он не создался, ошибок нет
    Подскажите в чем может быть проблема? за ранее благодарен!

    ОтветитьУдалить
  9. Спасибо за статью! Выложил скрипты для общего пользования https://github.com/naXa777/dex-explorer-kit

    ОтветитьУдалить
  10. Хочу отметить что начиная с версии XE7 можно подключать сразу JAR файлы через Project Manager. DEX файлы не нужны.
    Детали автор отметил в другой статье, там же и обновленный батник. http://delphifmandroid.blogspot.com/2016/02/alarmmanager-rad-studio.html

    ОтветитьУдалить