0

一些初步信息:

我必须为 Android 库创建一个 API。此 API 必须处理通用数据的基本 CRUD(查找、查找全部、插入、更新、删除)操作。然后使用此库的其他应用程序可以将此 API 与他们想要的任何数据/对象一起使用。但是,这些数据存储在后端服务器中,由在不同设备(基本上是 BaaS)中拥有相同应用程序的所有用户存储和使用。这些应用程序应该能够在没有互联网连接的情况下工作。因此,查看了 Google I/O 制作的有关如何在 android 中设计 REST 客户端的视频并进行了关注。

因此现在:

  • 在插入/更新/删除数据时,API 使用本地 Content Provider 并为数据设置特定标志。
  • 我有一个 SyncAdapter 正在运行,它会在每次运行时检查 sqlite 中的所有数据。
  • 它检查数据具有的标志(如果它已被插入、更新或删除),并调用 REST API 以将其与服务器同步

但是,我希望应用程序能够将回调传递给这个 sincronization。基本上,我想要的是,当应用程序从我的 API 调用“插入”方法时,它应该向它传递一个回调。这个回调应该做两件事:

  • 定义在插入失败的情况下要做什么
  • 定义在插入成功的情况下要做什么。

这是我想做的一个例子:

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)如果同步成功完成,callBack.onFailure()则从 SyncAdapter 调用,如果同步失败,则从 SyncAdapter 调用。

因此,回调类看起来像这样:

//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。该接收器将监听特定的 Intent。

当 SyncAdapter 同步完一行插入的数据后,它会创建一个特定的 Intent 并广播它。作为附加数据,此 Intent 将包含已处理行的 id 和同步状态(如果成功或失败)。当接收器被调用时,它会从意图中获取行的 id,获取该 id 的特定回调,并根据同步调用的状态callBack.onSuccess(data)callBack.onFailure().

我的问题来自确定要调用哪个回调。每个“API.insert(..)”方法都传递一个Callback子类,它是一个匿名类。我不知道如何序列化它,或者用行的特定 id “存储”它。如果可以以某种方式对其进行序列化,那么 BroadcastReceiver 就会执行Callback callback = lookup(id)并获取与该 id 关联的回调。但是我不知道这是否可能,因为我尝试序列化回调并且它不起作用。我认为问题在于 Activity 本身(从中调用 insert 方法)本身不可序列化,因此回调也无法序列化。

我考虑过可能不使用匿名类,而是使用命名的回调类。但是话又说回来,如果我允许Callback在方法中传递一个对象,那么您仍然可以将一个匿名类传递给它。因此,任何完全做到这一点的应用程序都会出错。因此,即使回调作为匿名类传递,它们也应该可以工作,除非上述情况有替代方案。

另外,如果它是 Activity 中的匿名类,我会喜欢它,因此回调可以使用 Activity 中定义的变量。例如,要在一行中插入 2 个不同的对象(回调将使用OtherData data活动中定义的对象)。如果它可以以某种方式完成,那么如果 Activity 仍然启动并运行,则回调应该使用相同的 Activity 对象。但是如果 Activity 已关闭,则以某种方式对其进行序列化,因此当稍后调用回调时,它会使用 Activity 中的变量/数据,然后再进行 cloaed/destroyed

有任何想法吗?

PS:另外,就像我说的回调和数据应该是通用的,所以调用回调的方法应该支持任何可能的类型。我想这无论如何都不是问题,例如使用通配符Callback<?> callback = lookupCallback(id)


更新:

好吧,我想我有一个解决方案,也许。

然后我遇到的主要问题是,我需要 Callback 是一个匿名类并同时可序列化,这是不可能的。如果它是可序列化的,但不是一个令人讨厌的类,我不能使用调用它的 Activity 中的变量/属性(这是处理这些回调所必需的)。但是,如果它是一个匿名类,但不可序列化,那么我无法序列化回调,以便在 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 中包含可序列化的对象/原语,并可以在onSuccess(). 随着回调的序列化,只要 SyncAdapter 成功插入用户,它就可以从数据库中获取序列化的回调,反序列化它,并从中调用“onSuccess(insertedUser)”。

如果只需要 Address 对象,那么 UserInsertedCallback 的构造函数可以取而代之。但也许开发人员还需要 Activity 中的其他东西,所以他们也有能力传递 Activity。

当然,我敢肯定这并不那么容易,但我很快就会尝试看看这是否可行。我仍然不知道如何防止人们将回调作为匿名类传递

4

2 回答 2

1

So I was finally able to get this going on.
It worked like I put it in my Edit, but I'll give some more explanations:

Basically, the Callback class is similar to how I defined it above, except I changed the methods a little bit:

public abstract class Callback<T> implements Serializable{
    private static final long serialVersionID = ...; //version Id goes here
    public void onSuccess(T insertedData){}
    public void onFailure(T failedData, CustomException ex){}
}

I pass more info to the onFailure event, such as the data that failed the synchronization, and a custom exception thrown by the synchronization (since the synchronization can fail for a lot of reasons).

Then each app can create a named class that extends Callback<T>, and can have any field it wants (as long as it's serializable), just like I mentioned in the Edit. My API call takes this callback as a parameter, serializes it, and stores it in the database alongside with the _ID of the data to synchronize.
Later on, my SyncAdapter takes that data and tries to synchronize it with the server. If a fatal error occurs (one it can't recover from and try again later for instance), it reverts the data back to its original state and sends a broadcast message passing the serialized data, a serialized exception (both via JSON), the _ID field, and passing a parameter stating the synchronization failed.
Then I set up a BroadcastReceiver who listens for this broadcast, gets all this information and starts a Service with the same extras.
This new service gets the _ID field, looks up the serialized callback from the database, deserializes it, deserializes the data and the custom exception, and calls callback.onFailure(data, ex), and it works pretty well!
Once the callback is finished being called, it is deleted from the database. All of this (except defining the extended callback class and calling the API) is done in the Android library itself.

P.S: Actually, the data is in JSON format and de/serialized with GSON. To do this I added a Class<T> data_class; field to Callback<T>, and a onFailureFromJson(String json, CustomException ex) final method which deserializes the JSON into an object of type T, and calls onFailure(entity,ex) with it. Thus the service calls this onFailureFromJson method instead.
Here's the final Callback<T> class:

public abstract class Callback<T> implements Serializable{
    private static final long serialVersionID = ...; //version Id goes here
    private Class<T> data_class;

    public Callback(Class<T> data_class){this.data_class = data_class;}
    public abstract void onSuccess(T insertedData){}
    public abstract void onFailure(T failedData, CustomException ex){}

    public final void onSuccessFromJson(String json){
        Gson gson = new GsonBuilder().create();
        T entity = gson.fromJson(json,data_class);
        onSuccess(entity);
    }
    public final void onFailureFromJson(String json, CustonException ex){
        Gson gson = new GsonBuilder().create();
        T entity = gson.fromJson(json,data_class);
        onFailure(entity,ex);    
    }    
}
于 2013-11-16T04:44:27.487 回答
0

虽然可能有更好的方法我不知道,但一种工作技术

public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {

   //do your CURD actions
   boolean isSuccess = false; //according to your operation action set this to true or false

   if(isSuccess){
        new ExtendedCallback.onSuccess(); 
        //ExtendedCallback is the extension of Callback class to suit your requirement
        //of course yu would have to define this new class class and send it to you SyncAdapter class before using
   }
}
于 2013-11-08T17:44:43.720 回答