Jump to content
Yaroslav Brovin

[Руководство] Миграция использования старого Android моста на новый

Recommended Posts

Всем доброго дня!

Как и обещал ранее, перед релизом 1.4.0.0 выкладываю инструкцию о миграции использования старого Android-Delphi моста на новый в 1.4.0.0. Это руководство актуально тем разработчикам, которые используют Android Api в своих проектах. В этой статье я рассмотрю только те аспекты в работе моста, которые изменились.

По большей части изменения были необходимы только в свете поддержки RAD Studio 10.4, в которой была полностью удалена поддержка модели памяти ARC.

1. Создание Java объекта:

Было:

var
  Intent: TJIntent;

Intent := TJIntent.Create;

Стало: 

var
  Intent: JIntent;

Intent := TJIntent.Create;

Все объявления переменных, атрибутов и тд должны быть теперь именованы без префикса "T". TJActivity -> JActivity.

2. Передача Java объектов через Java интерфейс:

Было:

// Декларация метода. Принимает атрибут интерфейсного типа CharSequence.
TJTextView.setError(const AArg0: JCharSequence);

// Java cтрока TJString реализует интерфейс JCharSequence. Поэтому передача идет напрямую (в старой версии моста).
View.setError(StringToJString(Control.Error));

Стало:

// Декларация метода. Принимает атрибут интерфейсного типа CharSequence.
TJTextView.setError(const AArg0: JCharSequence);

// Теперь обертка Java cтроки TJString НЕ реализует явно интерфейс JCharSequence. Поэтому необходима явная конвертация JString -> JCharSequence
View.setError(TJCharSequence.Wrap(StringToJString(Control.Error)));

// Или можно воспользоваться хелпером из FGX.Helpers.Android.pas
View.setError(StringToJCharSequence(Control.Error));

3. Работа с Java массивами:

При создании экземпляра Java массива (TJavaArray<T>) вся работа теперь осуществляется через интерфейс IJavaArray<T>.

Было:

var
  Points: TJavaArray<Single>;
 
Points := TJavaArray<Single>.Create(0);
FCanvas.drawLines(Points, FStroke);

Стало:

var
  Points: IJavaArray<Single>;
 
Points := TJavaArray<Single>.Create(0);
FCanvas.drawLines(Points, FStroke);

4. Работа с Java-листенерами:

Если вы реализовывали свои листенеры через наследование класса Java.Bridge.TJavaLocal, то в 1.4.0.0 листенеры теперь поддерживают счетчик ссылок через Delphi IInterface. Это значит, что при создании листенера, его необходимо сохранять в переменную/поле интерфейсного типа, а не объектного.

Было:

  TfgAndroidCameraStateListener = class(TJavaLocal, JCameraStateListener)
  private
    [Weak] FCamera: TfgAndroidCamera;
  public
    constructor Create(const ACamera: TfgAndroidCamera);
    { JCameraStateListener }
    procedure onClosed(const AArg0: JCameraDevice);
    procedure onDisconnected(const cameraDevice: JCameraDevice);
    procedure onError(const cameraDevice: JCameraDevice; const errorCode: Integer);
    procedure onOpened(const cameraDevice: JCameraDevice);
  end;

var
  FStateListener: TfgAndroidCameraStateListener

FStateListener := TfgAndroidCameraStateListener.Create(Self);
FStateCallback.setListener(FStateListener);

Стало:

  TfgAndroidCameraStateListener = class(TJavaLocal, JCameraStateListener)
  private
    [Weak] FCamera: TfgAndroidCamera;
  public
    constructor Create(const ACamera: TfgAndroidCamera);
    { JCameraStateListener }
    procedure onClosed(const AArg0: JCameraDevice);
    procedure onDisconnected(const cameraDevice: JCameraDevice);
    procedure onError(const cameraDevice: JCameraDevice; const errorCode: Integer);
    procedure onOpened(const cameraDevice: JCameraDevice);
  end;

var
  FStateListener: JCameraStateListener

FStateListener := TfgAndroidCameraStateListener.Create(Self);
FStateCallback.setListener(FStateListener);

В противном случае, экземпляр листенера FStateListener будет удален сразу же после вызова 

FStateCallback.setListener(FStateListener);

Что в свою очередь приведет к последующим падения приложения. Так как сработает механизм подсчета ссылок Delphi интерфейсов. 

5. Конвертация string <-> JString:

До RAD Studio 10.4 мобильные компиляторы поддерживали добавление операторов неявного преобразования. Это в свою очередь позволило нам реализовать автоматическую конвертацию "на лету" Delphi string <-> Java JString. В 10.4 эту возможность убрали вместе с ARC, так как неявное преобразование реализуется через ARC. Поэтому теперь нужно явно вызывать преобразование строк через StringToJString и JStringToString:

Было:

TjUri.parse('tel:+');

Стало:

uses
  FGX.Helpers.Android;

TJUri.parse(StringToJString('tel:+'))

Это все изменения, которые необходимо сделать.

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites

Ярослав, это уже можно делать сейчас, перед релизом?

Share this post


Link to post
Share on other sites
В 27.06.2020 в 11:59, Stas сказал:

Ярослав, это уже можно делать сейчас, перед релизом?

Да, сегодня/завтра будет релиз.

Share this post


Link to post
Share on other sites

Можете поделиться обновленными Android.Api.PopupMenu, Android.Api.MediaStore, Android.Api.Build

  • Like 1

Share this post


Link to post
Share on other sites

Здравствуйте

Android.Api.Build не нужен, имхо( все вроде  в Android.Api.ActivityAndView)

Android.Api.7z

  • Like 1

Share this post


Link to post
Share on other sites

@Stas спасибо что поделились. Почему то TfgInputQuery перестал нормально работать. Заменил Android.Api.MediaStore.pas, а FGX.OpenDialog.pas оставил старое. Проект скомпилировался, дилоговое окно открывается, но при нажатии на ОК вылетает AV. 

Может FGX.OpenDialog.pas тоже нужно обновить?

Share this post


Link to post
Share on other sites

Вот тут скользкий момент

  procedure UriToFile(uri:JUri);
  var    i:integer;
         bt:byte;
         b:IJavaArray<Byte>;//Вот тут проверьте
         jis:JInputStream;
         FilePath: string;
         ms:TMemoryStream;
  begin
  ms := TMemoryStream.Create;
  try
  jis := TfgAndroidHelper.Context.getContentResolver.openInputStream(uri);
  b := TJavaArray<Byte>.Create(jis.available);
  jis.read(b);
  jis.close;
  ms.Write(b.Elements^,b.Length);
  FilePath:=System.IOUtils.TPath.GetTempFileName+'.jpg';
  ms.SaveToFile(FilePath);
  FFiles.Add(getFileName(uri)+'='+FilePath);
  finally
   ms.Free;
  end;
  end;

 

FGX.OpenDialog.pas

Share this post


Link to post
Share on other sites

@Stas не работает! У меня ошибка вот здесь:

procedure TDialogInterface_OnClickListener.onClick(
  const AArg0: JDialogInterface; const AArg1: Integer);
var S:String;
begin
 S:=JStringToString(TfgInputQuery(FOwner).ed.getText.toString); //<-- здесь ошибка FOwner=nil
 if Assigned(TfgInputQuery(FOwner).FProc) then
 TfgInputQuery(FOwner).FProc(AArg1=TjActivity.RESULT_OK,S);

end;

Переменная FOwner почему то пустое. От этого и вылетает Access Violation.

Share this post


Link to post
Share on other sites

Попробуйте конструктор переписать, чтобы объект туда передавался

Share this post


Link to post
Share on other sites

Может кто еще использует модуль FGX.PanelAnimation.pas из "Проекты пользователей" - "Проверка контрольных знаков":
https://github.com/sinuke/excises

Делал миграцию по этому руководству, "T" убирал, но до конца с "Create" ошибки не могу исправить.
Вот исходник, который разработал "sinuke", компилировался на 1.3.0.0
и ошибки при компиляции на 1.4.1.1 в связи с переходом на новый Android мост.

Помогите пожалуйста исправить.

unit FGX.PanelAnimation;

interface

uses System.SysUtils,
     FGX.Controls,
     FGX.Types,
     Android.Api.ActivityAndView,
     Java.Bridge;

type
  TTranslateAnimationListener = class(TJavaLocal, JAnimation_AnimationListener)
  protected
    FFinishCallback: TfgCallback;
  public
    constructor Create(const AFinishCallback: TfgCallback);
    destructor Destroy; override;

    {JAnimation_AnimationListener}
    procedure onAnimationEnd(const AArg0: TJAnimation);
    procedure onAnimationRepeat(const AArg0: TJAnimation);
    procedure onAnimationStart(const AArg0: TJAnimation);
  end;

  TfgPanelAnimation = class
  public
    class procedure ShowPanel(const AControl: TfgControl; const AY: Single;
                              const ADuration: Integer; const AFinishCallback: TfgCallback = nil);

    class procedure HidePanel(const AControl: TfgControl; const AY: Single;
                              const ADuration: Integer; const AFinishCallback: TfgCallback = nil);
  end;

implementation

uses FGX.Animation,
     FGX.Helpers.Android,
     FGX.Controls.Android,
     FGX.Types.AutoreleasePool,
     Android.Api.Resources;

{ TMyAnimatorListener }

constructor TTranslateAnimationListener.Create(const AFinishCallback: TfgCallback);
begin
  inherited Create;

  FFinishCallback := AFinishCallback;
end;

destructor TTranslateAnimationListener.Destroy;
begin
  FFinishCallback := nil;
  inherited;
end;

procedure TTranslateAnimationListener.onAnimationEnd(const AArg0: TJAnimation);
begin
  if Assigned(FFinishCallback) then
    FFinishCallback;
end;

procedure TTranslateAnimationListener.onAnimationRepeat(const AArg0: TJAnimation);
begin
end;

procedure TTranslateAnimationListener.onAnimationStart(const AArg0: TJAnimation);
begin
end;

{ TfgPanelAnimation }

class procedure TfgPanelAnimation.HidePanel(const AControl: TfgControl; const AY: Single; const ADuration: Integer;
  const AFinishCallback: TfgCallback);
var
  View: TJView;
  Listener: TTranslateAnimationListener;
  Animation: TJTranslateAnimation;
  Interpolator: TJPathInterpolator;
  Scale: Single;
begin
  Scale := TfgAndroidHelper.ScreenScale;
  View := AControl.Handle.View;
  Animation := TJTranslateAnimation.Create(0, 0, 0, AY * Scale);
  if ADuration = PlatformDuration then
    Animation.setDuration(TJResources.getSystem.getInteger(TJR_integer.config_shortAnimTime))
  else
    Animation.setDuration(ADuration);
  Animation.setFillBefore(True);
  Animation.setFillAfter(False);

  Interpolator := TJPathInterpolator.Create(0.64, 0, 0.78, 0);

  Listener := TTranslateAnimationListener.Create(
    procedure
    begin
      if Assigned(AFinishCallback) then
        AFinishCallback();

      TfgAutoreleasePool.Release(Listener);
      TfgAutoreleasePool.Release(Interpolator);
      TfgAutoreleasePool.Release(Animation);
    end);

  TfgAutoreleasePool.Store(Listener);
  TfgAutoreleasePool.Store(Interpolator);
  TfgAutoreleasePool.Store(Animation);

  Animation.setInterpolator(Interpolator);
  Animation.setAnimationListener(Listener);

  View.startAnimation(Animation);
end;

class procedure TfgPanelAnimation.ShowPanel(const AControl: TfgControl; const AY: Single; const ADuration: Integer;
  const AFinishCallback: TfgCallback);
var
  View: TJView;
  Listener: TTranslateAnimationListener;
  Animation: TJTranslateAnimation;
  Interpolator: TJPathInterpolator;
  Scale: Single;
begin
  Scale := TfgAndroidHelper.ScreenScale;
  View := AControl.Handle.View;
  Animation := TJTranslateAnimation.Create(0, 0, 0, AY * Scale);
  if ADuration = PlatformDuration then
    Animation.setDuration(TJResources.getSystem.getInteger(TJR_integer.config_shortAnimTime))
  else
    Animation.setDuration(ADuration);
  Animation.setFillEnabled(True);

  Interpolator := TJPathInterpolator.Create(0.22, 1, 0.36, 1);

  Listener := TTranslateAnimationListener.Create(
    procedure
    begin
      if Assigned(AFinishCallback) then
        AFinishCallback();

      TfgAutoreleasePool.Release(Listener);
      TfgAutoreleasePool.Release(Interpolator);
      TfgAutoreleasePool.Release(Animation);
    end);

  TfgAutoreleasePool.Store(Listener);
  TfgAutoreleasePool.Store(Interpolator);
  TfgAutoreleasePool.Store(Animation);

  Animation.setInterpolator(Interpolator);
  Animation.setAnimationListener(Listener);

  View.startAnimation(Animation);
end;

end.
[DCC Error] FGX.PanelAnimation.pas(12): E2291 Missing implementation of interface method JAnimation_AnimationListener.onAnimationEnd
[DCC Error] FGX.PanelAnimation.pas(12): E2291 Missing implementation of interface method JAnimation_AnimationListener.onAnimationRepeat
[DCC Error] FGX.PanelAnimation.pas(12): E2291 Missing implementation of interface method JAnimation_AnimationListener.onAnimationStart
[DCC Error] FGX.PanelAnimation.pas(83): E2010 Incompatible types: 'TJView' and 'JView'
[DCC Error] FGX.PanelAnimation.pas(84): E2010 Incompatible types: 'TJTranslateAnimation' and 'JTranslateAnimation'
[DCC Error] FGX.PanelAnimation.pas(86): E2361 Cannot access private symbol TJAnimation.setDuration
[DCC Error] FGX.PanelAnimation.pas(88): E2361 Cannot access private symbol TJAnimation.setDuration
[DCC Error] FGX.PanelAnimation.pas(89): E2361 Cannot access private symbol TJAnimation.setFillBefore
[DCC Error] FGX.PanelAnimation.pas(90): E2361 Cannot access private symbol TJAnimation.setFillAfter
[DCC Error] FGX.PanelAnimation.pas(92): E2010 Incompatible types: 'TJPathInterpolator' and 'JPathInterpolator'
[DCC Error] FGX.PanelAnimation.pas(109): E2361 Cannot access private symbol TJAnimation.setInterpolator
[DCC Error] FGX.PanelAnimation.pas(110): E2361 Cannot access private symbol TJAnimation.setAnimationListener
[DCC Error] FGX.PanelAnimation.pas(112): E2361 Cannot access private symbol TJView.startAnimation
[DCC Error] FGX.PanelAnimation.pas(125): E2010 Incompatible types: 'TJView' and 'JView'
[DCC Error] FGX.PanelAnimation.pas(126): E2010 Incompatible types: 'TJTranslateAnimation' and 'JTranslateAnimation'
[DCC Error] FGX.PanelAnimation.pas(128): E2361 Cannot access private symbol TJAnimation.setDuration
[DCC Error] FGX.PanelAnimation.pas(130): E2361 Cannot access private symbol TJAnimation.setDuration
[DCC Error] FGX.PanelAnimation.pas(131): E2361 Cannot access private symbol TJAnimation.setFillEnabled
[DCC Error] FGX.PanelAnimation.pas(133): E2010 Incompatible types: 'TJPathInterpolator' and 'JPathInterpolator'
[DCC Error] FGX.PanelAnimation.pas(150): E2361 Cannot access private symbol TJAnimation.setInterpolator
[DCC Error] FGX.PanelAnimation.pas(151): E2361 Cannot access private symbol TJAnimation.setAnimationListener
[DCC Error] FGX.PanelAnimation.pas(153): E2361 Cannot access private symbol TJView.startAnimation
[DCC Fatal Error] F2063 Could not compile used unit 'FGX.PanelAnimation.pas'

 

FGX.PanelAnimation.pas

Share this post


Link to post
Share on other sites

Честно говоря в этом модуле вообще ничего не сделано из того, что я написал в руководстве выше 🙂

Share this post


Link to post
Share on other sites
13 часов назад, knsg12 сказал:

@Stas спасибо что поделились. Почему то TfgInputQuery перестал нормально работать. Заменил Android.Api.MediaStore.pas, а FGX.OpenDialog.pas оставил старое. Проект скомпилировался, дилоговое окно открывается, но при нажатии на ОК вылетает AV. 

Может FGX.OpenDialog.pas тоже нужно обновить?

Реализовал TfgDialogs.InputBox и TfgDialogs.InputQuery в следующем релизе 1.4.2.0.

 

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...