いくつかの予備情報:
Android ライブラリの API を作成する必要があります。この API は、汎用データの基本的な CRUD (検索、すべて検索、挿入、更新、削除) 操作を処理する必要があります。その後、このライブラリを使用する他のアプリは、必要なデータ/オブジェクトでこの API を使用できます。ただし、このデータはバックエンド サーバーに保存され、異なるデバイス (基本的には BaaS) で同じアプリを使用するすべてのユーザーによって保存され、使用されます。アプリは、インターネットに接続しなくても動作できるはずです。そこで、Android で REST クライアントを設計する方法について Google I/O が作成したビデオをチェックして、それに従いました。
したがって、今:
- データを挿入/更新/削除する場合、API はローカル コンテンツ プロバイダーを使用し、特定のフラグをデータに設定します。
- 私は SyncAdapter を実行しています。これは、実行ごとに sqlite のすべてのデータをチェックします。
- データのフラグをチェックし (挿入、更新、または削除された場合)、REST API を呼び出してサーバーと同期します。
ただし、アプリがこのシンクロナイゼーションにコールバックを渡せるようにしたいと考えています。基本的に、私が望むのは、アプリが API から「挿入」メソッドを呼び出すときに、コールバックを渡す必要があるということです。このコールバックは 2 つのことを行う必要があります。
- 挿入が失敗した場合の対処方法を定義する
- 挿入が成功した場合の処理を定義します。
これは私がやりたいことの例です:
GenericData data = new GenericData();
//Initialize data
API.insert(GenericData.class, data, new CallBack<GenericData>(){
@Override
public void onSuccess(GenericData insertedData){
//Data inserted and syncronized successfully
}
@Override
public void onFailure(){
//Data failed to be inserted an/or sincronized
}
});
このコールバックは匿名クラスとして、アプリの AsyncTask から呼び出されます。私がやりたいことはcallBack.onSuccess(data)
、同期が成功した場合は SyncAdapter からの呼び出し、またはcallBack.onFailure()
同期が失敗した場合は SyncAdapter からの呼び出しです。
したがって、Callback クラスは次のようになります。
//Concrete empty methods are set so it's not necessary to implement all these methods
public abstract class Callback<T>{
public void onSuccess(T insertedData){}
public void onFailure(){}
}
私が念頭に置いていたのはこれでした:
「挿入」コールバックを処理する BroadcastReceiver を作成します。このレシーバーは、特定のインテントをリッスンします。
SyncAdapter は、挿入されたデータの行の同期を完了すると、特定のインテントを作成してブロードキャストします。このインテントには、追加データとして、処理された行の ID と、同期のステータス (成功したか失敗したか) が含まれます。レシーバーが呼び出されると、インテントから行の ID を取得し、その ID の特定のコールバックを取得し、同期呼び出しのステータスに応じてcallBack.onSuccess(data)
またはcallBack.onFailure()
.
私の問題は、呼び出すコールバックを決定することにあります。各「API.insert(..)」メソッドCallback
には、無名クラスであるサブクラスが渡されます。それをシリアル化する方法、または行の特定のIDで「保存」する方法がわかりません。何らかの方法でシリアル化できる場合、BroadcastReceiver はCallback callback = lookup(id)
その ID に関連付けられたコールバックを取得します。ただし、コールバックをシリアル化しようとしたが機能しなかったため、これが可能かどうかはわかりません。問題は、Activity 自体 (insert メソッドが呼び出される場所) 自体がシリアル化できないため、コールバックもシリアル化できないことだと思います。
匿名クラスを使用しないことを考えましたが、代わりに名前付きの Callback クラスを使用しました。ただし、メソッドでオブジェクトを渡すことを許可した場合Callback
でも、匿名クラスを渡すことができます。したがって、まさにそれを行うアプリにはエラーが発生します。したがって、コールバックは、匿名クラスとして渡された場合でも機能するはずです。上記の状況に対する代替手段がない限り。
また、アクティビティ内で無名クラスにして、アクティビティで定義した変数をコールバックで使えるようにしてほしいです。たとえば、2 つの異なるオブジェクトを連続して挿入するには (コールバックはOtherData data
アクティビティで定義されたオブジェクトを使用します)。何らかの方法で実行でき、Activity がまだ稼働している場合、コールバックは同じ Activity オブジェクトを使用する必要があります。ただし、アクティビティが閉じている場合は、どういうわけかシリアル化して、コールバックが後で呼び出されると、クローア/破棄される直前のアクティビティの変数/データを使用します
何か案は?
PS:また、私が言ったように、コールバックとデータはジェネリックである必要があるため、コールバックを呼び出すメソッドは可能なすべての型をサポートする必要があります。とにかく、ワイルドカードを使用するなど、これは問題にならないと思いますCallback<?> callback = lookupCallback(id)
。
アップデート:
わかりました、おそらく解決策があると思います。
次に、私が抱えていた主な問題は、コールバックを匿名クラスにすると同時にシリアライズ可能にする必要があることでした。それは不可能です。シリアライズ可能であるが無名クラスではない場合、それを呼び出すアクティビティから変数/属性を使用できません (これらのコールバックを処理するために必要です)。しかし、それが匿名クラスであるがシリアライズ可能でない場合、コールバックをシリアライズしてデシリアライズし、SyncAdapter が同期を終了したときに呼び出されるようにすることはできません。
だから私は、両方の長所を含めるために、このようにすることができるのではないかと考えました:
これは CallBack クラスになります。
//Concrete empty methods are set so it's not necessary to implement all these methods
public abstract class Callback<T> implements Serializable{
public void onSuccess(T insertedData){}
public void onFailure(){}
}
すべてのコールバックは名前付きクラス (外部クラス、または静的内部クラスなど) である必要があり、その作成者でアクティビティを渡します。次に、必要なアクティビティから任意のフィールドを取得できます。
これは例を示したほうがよいと思うので、「ユーザーとそのユーザーを持つアドレスを作成します。ユーザーを挿入し、ユーザーが挿入されたことがわかったらアドレスを挿入したい」 .
この場合、次のようなコールバックがあると思います。
public class UserInsertedCallback extends Callback<User> implements Serializable{
//Here goes serialId
private Address address;
public UserInsertedCallback(UserActivity activity){
address = activity.getAddress();
}
@Override
public void onSuccess(User insertedUser){
//This is another callback we may want to use
Callback<Address> callback = createCallback();
//I create Foreign Key in Address referencing the user
address.setUserId(insertedUser.getId());
API.insert(Address.class, address, callback);
}
}
これがアクティビティになります。
public class UserActivity extends Activity{
private Address address;
....
public Address getAddress(){return address;}
private class TestTask extends AsyncTask<Void,Void,Void>{
@Override
protected Void doInBackground(Void... void){
User user = ... //create user
address = ... //create address
API.insert(User.class, user, new UserInsertedCallback(UserActivity.this));
}
}
}
私の API メソッドは UserInsertedCallback をシリアル化し、簡単に取得できるデータベース テーブルに挿入します。Address がシリアライズ可能であると仮定すると、ここで問題はありません。そうでない場合でも、開発者はシリアル化可能なオブジェクト/プリミティブを UserInsertedCallback に含めるだけで、 Address オブジェクトを で再度作成できますonSuccess()
。コールバックがシリアル化されると、SyncAdapter が User の挿入を正常に完了するたびに、シリアル化されたコールバックをデータベースから取得し、逆シリアル化し、そこから "onSuccess(insertedUser)" を呼び出すことができます。
Address オブジェクトだけが必要な場合は、代わりに UserInsertedCallback のコンストラクターがそれを受け取ることができます。しかし、おそらく開発者はアクティビティから他のものも必要とするので、アクティビティを渡すこともできます。
確かに、それはそれほど簡単ではないと確信していますが、すぐにこれが機能するかどうかを確認してみます. 人々がコールバックを匿名クラスとして渡すのを防ぐ方法はまだわかりません