Under Android we can declare some listener like in following example :
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;
and then we can activate the OnErrorListener
like this :
FMediaPlayer.setOnErrorListener(FOnErrorListener);
However, deactivation of such OnErrorListener
is problematic.
In Delphi for Android UI thread and main thread are different threads and onError
will be called in context of Android UI thread because of this function in underlying Java framework:
// 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;
// }
Setting FMediaPlayer.setOnErrorListener(nil);
in the main thread can cause problems because it's different from the UI thread that might be doing some work on internal variable mOnErrorListener
.
I tried to synchronize deactivation with following code:
CallInUIThreadAndWaitFinishing(
procedure
begin
FMediaPlayer.setOnErrorListener(nil);
end);
However, sometimes application crashes at that point and I don't know why. How can I avoid that?
Like you noted CallInUIThreadAndWaitFinishing
has two overloads:
TMethodCallback = procedure of object;
TCallBack = reference to procedure;
procedure CallInUIThreadAndWaitFinishing(AMethod: TMethodCallback); overload;
procedure CallInUIThreadAndWaitFinishing(AMethod: TCallBack); overload;
In first one callback is regular method that does not have any special handling involved. After it executes your code continues to function in regular manner.
Using
CallInUIThreadAndWaitFinishing(aProcedureOfObject);
works because there is no hidden capturing behind the scenes.
In second one callback is anonymous method that comes with special feature - strong capturing of any variables used in method body. In case of
CallInUIThreadAndWaitFinishing(procedure
begin
FmyObject.dosomething
end);
it will capture FmyObject
variable.
That is first step in calling CallInUIThreadAndWaitFinishing
with anonymous method callback. Next thing what happens is that Java runnable object is created
Runnable := TRunnable.Create(AMethod);
ActiveJavaRunnables.Add(Runnable);
Runnable.Start;
Above code stores your anonymous method (AMethod
) in TRunnable
FCallback
field creating strong reference to your anonymous method as well as any variables that method captured.
And now it comes the real catch. Runnable
object is not automatically cleared when your code executes - it is stored in ActiveJavaRunnables
list and that list is cleared periodically in non-deterministic fashion - in sense that you don't know when it will be cleared and when it will actually release that Runnable
and stored FCallback
along with everything captured along the way.
If you want to use anonymous methods you have to create temporary weak reference to any object you use inside anonymous method body and use that weak reference instead.