如何正确清理 Android 侦听器?
How to properly clean an Android listener?
在 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
是有问题的。
In Delphi for Android UI 线程和主线程是不同的线程,onError
将在 Android [=41= 的上下文中调用] 线程因为底层 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);
但是,有时应用程序会在此时崩溃,我不知道为什么。我怎样才能避免这种情况?
我找到了如何避免偶发性应用程序崩溃的方法:
永远不要打电话给
CallInUIThreadAndWaitFinishing(procedure
begin
FmyObject.dosomething
end);
改用以下内容
CallInUIThreadAndWaitFinishing(aProcedureOfObject);
为什么?
因为第一个变体将保持对 FmyObject
的强引用(不增加引用计数 - 我不知道为什么),稍后,在你已经释放 FmyObject
之后,尝试释放它再次导致异常。
如您所述,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
列表中并且该列表以 non-deterministic 方式定期清除 - 从某种意义上说您不知道它什么时候会被清除,什么时候它会真正释放 Runnable
并存储 FCallback
以及沿途捕获的所有内容。
如果您想使用匿名方法,您必须为您在匿名方法体内使用的任何对象创建临时弱引用,并改用该弱引用。
在 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
是有问题的。
In Delphi for Android UI 线程和主线程是不同的线程,onError
将在 Android [=41= 的上下文中调用] 线程因为底层 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);
但是,有时应用程序会在此时崩溃,我不知道为什么。我怎样才能避免这种情况?
我找到了如何避免偶发性应用程序崩溃的方法:
永远不要打电话给
CallInUIThreadAndWaitFinishing(procedure
begin
FmyObject.dosomething
end);
改用以下内容
CallInUIThreadAndWaitFinishing(aProcedureOfObject);
为什么?
因为第一个变体将保持对 FmyObject
的强引用(不增加引用计数 - 我不知道为什么),稍后,在你已经释放 FmyObject
之后,尝试释放它再次导致异常。
如您所述,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
列表中并且该列表以 non-deterministic 方式定期清除 - 从某种意义上说您不知道它什么时候会被清除,什么时候它会真正释放 Runnable
并存储 FCallback
以及沿途捕获的所有内容。
如果您想使用匿名方法,您必须为您在匿名方法体内使用的任何对象创建临时弱引用,并改用该弱引用。