Delphi XE6のモバイルアプリケーションを使って、AndroidアプリケーションのJavaのスレッドを実行してみます。
AndroidのSDKが提供しているクラスには、Runnableインターフェースを引数に受け取るメソッドがあります。
そのようなメソッドをできるだけ手軽に使いたい。
Runnableインターフェースを継承したJavaのクラスを作成して、作成したJavaのクラスからDelphiのコードを呼び出す方法もありますが、ちょっと手間がかかります。
できれば、Delphiだけで完結したい。
そこで、今回紹介する方法になります。
Runnableインターフェースを継承したDelphiのクラスを作成します。
このクラスはコンストラクタの引数にスレッドで実行する処理をとります。
type
TRunner = class(TJavaLocal, JRunnable)
private
FProc: TThreadProcedure;
public
constructor Create(Proc: TThreadProcedure); overload;
procedure run; cdecl;
end;
implementation
constructor TRunner.Create(Proc: TThreadProcedure);
begin
inherited Create;
FProc := Proc;
end;
procedure TRunner.run;
begin
FProc;
end;
TThreadProcedure型はSystem.Classesユニットで次のように定義されています。
TThreadProcedure = reference to procedure;
このクラスをAndroidアプリケーションで使ってみます。
begin
SharedActivity.runOnUiThread(TRunner.Create(
procedure
begin
Log.d('TRunner.run!');
end));
end;
コンパイルはできますが、実行するとエラーが発生してアプリケーションが強制終了してしまいます。
原因は、スレットが開始する前にTRunnerオブジェクトが破棄されてしまうためです。
TRunnerオブジェクトが自動的に解放されないように、変数に保持するようにしてみます。
type
TRunner = class(TJavaLocal, JRunnable)
private
// ↓追加
class var JRunnables: System.Generics.Collections.TThreadList<JRunnable>;
class constructor Create;
class destructor Destroy;
クラスが読み込まれたされたときにクラス変数JRunnablesを生成し、クラスが破棄されたときにクラス変数JRunnablesを破棄します。
implementation
class constructor TRunner.Create;
begin
JRunnables := TThreadList<JRunnable>.Create;
end;
class destructor TRunner.Destroy;
begin
JRunnables.DisposeOf;
end;
TRunnerクラスのインスタンスが生成されたときに、JRunnables変数に登録します。
constructor TRunner.Create(Proc: TThreadProcedure);
begin
inherited Create;
FProc := Proc;
JRunnables.Add(Self);
end;
スレッドの処理が終了したら、JRunnables変数から削除します。
procedure TRunner.run;
begin
try
FProc;
finally
JRunnables.Remove(Self);
end;
end;
アプリケーションを実行します。
SharedActivity.runOnUiThread(TRunner.Create(
procedure
begin
Log.d('TRunner.run!');
end));
今度は正しく動作しました。