如何正确清理Android监听器?

时间:2017-02-13 16:58:23

标签: android delphi firemonkey delphi-10.1-berlin

在Android下,我们可以像下面的示例一样声明一些监听器:

TErrorListener = class(TJavaLocal, JMediaPlayer_OnErrorListener)
  private
  public
  function onError(mp: JMediaPlayer; what: Integer; extra: Integer): Boolean; cdecl;
end;    

TmyObject = class
private
  FMediaPlayer: jmediaplayer;
  FOnErrorListener: TErrorListener;
end;

然后我们可以像这样激活OnErrorListener

FMediaPlayer.setOnErrorListener(FOnErrorListener);

然而,此类OnErrorListener停用存在问题。

在Delphi for Android UI线程和主线程是不同的线程,onError将在Android UI线程的上下文中调用,因为在底层Java框架中有这个功能:

  //   Looper looper;
  //   if ((looper = Looper.myLooper()) != null) {
  //     mEventHandler = new EventHandler(this, looper);
  //   } else if ((looper = Looper.getMainLooper()) != null) {
  //     mEventHandler = new EventHandler(this, looper);
  //   } else {
  //     mEventHandler = null;
  //   } 

在主线程中设置FMediaPlayer.setOnErrorListener(nil);可能会导致问题,因为它与可能正在对内部变量mOnErrorListener进行一些工作的UI线程不同。

我尝试将停用与以下代码同步:

  CallInUIThreadAndWaitFinishing(
    procedure
    begin
      FMediaPlayer.setOnErrorListener(nil);
    end);

然而,有时应用程序崩溃,我不知道为什么。我怎么能避免这种情况?

2 个答案:

答案 0 :(得分:3)

就像你提到的那样CallInUIThreadAndWaitFinishing有两个重载:

TMethodCallback = procedure of object;
TCallBack = reference to procedure;

procedure CallInUIThreadAndWaitFinishing(AMethod: TMethodCallback); overload;
procedure CallInUIThreadAndWaitFinishing(AMethod: TCallBack); overload;

在第一个回调中,常规方法没有涉及任何特殊处理。执行后,您的代码将继续以常规方式运行。

使用

CallInUIThreadAndWaitFinishing(aProcedureOfObject);

有效,因为幕后没有隐藏的捕捉。

在第二个回调中,回调是带有特殊功能的匿名方法 - 强大捕获方法体中使用的任何变量。在

的情况下
CallInUIThreadAndWaitFinishing(procedure
begin
  FmyObject.dosomething
end);

它将捕获FmyObject变量。

这是使用匿名方法回调调用CallInUIThreadAndWaitFinishing的第一步。接下来发生的事情是创建Java可运行对象

Runnable := TRunnable.Create(AMethod);
ActiveJavaRunnables.Add(Runnable);
Runnable.Start;

上面的代码将您的匿名方法(AMethod)存储在TRunnable FCallback字段中,从而创建对您的匿名方法以及捕获的方法的任何变量的强引用。

现在它真正成功了。代码执行时,Runnable对象不会自动清除 - 它存储在ActiveJavaRunnables列表中,并且该列表会以非确定性方式定期清除 - 从某种意义上说,您不知道何时清除它当它实际释放Runnable并存储FCallback以及沿途捕获的所有内容时。

如果要使用匿名方法,则必须为在匿名方法体内使用的任何对象创建临时弱引用,并使用该弱引用。

答案 1 :(得分:0)

我发现如何避免零星的应用程序崩溃:

永远不要打电话

CallInUIThreadAndWaitFinishing(procedure
begin
  FmyObject.dosomething
end);

使用以下代替

CallInUIThreadAndWaitFinishing(aProcedureOfObject);

为什么?

因为第一个版本会保留强引用(不会增加引用计数 - 我不知道为什么)到FmyObject,稍后,在您已经发布FmyObject之后,尝试发布它再次导致例外。