在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);
然而,有时应用程序崩溃,我不知道为什么。我怎么能避免这种情况?
答案 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
之后,尝试发布它再次导致例外。