0

Некоторые предварительные данные:Android - SyncAdapter Ответный

Я должен создать API для библиотеки в Android. Этот API должен иметь дело с базовыми операциями CRUD (найти, найти, вставить, обновить и удалить) общих данных. Тогда другие приложения, которые используют эту библиотеку, могут использовать этот API с любыми данными/объектом, который они хотят. Однако эти данные хранятся на серверном сервере, где он хранится и используется всеми пользователями, которые имеют одно и то же приложение на разных устройствах (в основном, BaaS). Приложения должны иметь возможность работать без подключения к Интернету. Итак, проверили видео-ввод-вывод Google о том, как создавать REST-клиенты в android и следовать за ним.

Таким образом, в настоящее время:

  • При вставке/обновление/удаление данных, то API использует локальный Content Provider и устанавливает определенные флаги данных.
  • У меня работает SyncAdapter, который проверяет все данные в sqlite в каждого запуска.
  • Он проверяет флаг как данные (если он был включен, обновление или удален), и вызывает REST API для sincronize его с сервером

Однако, я хочу, чтобы приложения, чтобы иметь возможность пройдите обратный вызов к этой греховности. В принципе, я хочу, когда приложение вызывает метод «insert» из моего 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 
    } 
}); 

Этот обратный вызов, как annonymous класса, будет вызван из 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 завершил синхронизацию строки вставленных данных, он создаст конкретное намерение и передаст его. Это намерение имело бы в качестве дополнительных данных идентификатор обработанной строки и статус синхронизации (если это удалось или если она не удалась). Когда вызывается получатель, он получает идентификатор строки из намерения, получает конкретный обратный вызов для этого идентификатора и в зависимости от состояния вызовов синхронизации callBack.onSuccess(data) или callBack.onFailure().

Проблема связана с определением того, какой callback для вызова. Каждый метод «API.insert (..)» передается подклассом Callback, который является анонимным классом. Я не знаю, как его сериализовать, или «сохранить» его с определенным идентификатором строки. Если он каким-то образом может быть сериализован, тогда BroadcastReceiver просто сделает Callback callback = lookup(id) и получит обратный вызов, связанный с этим идентификатором. Однако я не знаю, возможно ли это, потому что я попытался выполнить сериализацию обратного вызова, и это не сработало. Я считаю, что проблема заключается в том, что сама операция (откуда вызывается метод insert) не сериализуется сама, поэтому обратный вызов также не может быть сериализован.

Я думал о том, что, возможно, не использовал анонимный класс, но вместо этого использовал именованный класс обратного вызова. Но опять же, если я разрешаю передать объект Callback в методе, то вы все равно можете передать ему анонимный класс. Таким образом, любое приложение, которое делает именно это, будет иметь ошибки. Таким образом, обратные вызовы должны работать, даже если они переданы как анонимные классы, если нет альтернативы для этой ситуации выше.

Кроме того, мне бы хотелось, чтобы это был анонимный класс внутри Activity, поэтому обратный вызов может использовать переменные, определенные в действии. Например, чтобы вставить 2 разных объекта в строку (обратный вызов будет использовать объект OtherData data, определенный в действии). Если это можно как-то сделать, то если действие все еще запущено и работает, обратный вызов должен использовать тот же объект Activity. Но если активность закрыта, как-то сериализуйте ее, поэтому, когда обратный вызов вызывается позже, он использует переменные/данные из действия справа до того, как он был взломан/уничтожен.

Любые идеи?

P.S: Также, как я уже сказал, обратные вызовы и данные должны быть общими, поэтому метод вызова обратного вызова должен поддерживать любой возможный тип. Я предполагаю, что это не будет проблемой в любом случае, например, с помощью подстановочного знака Callback<?> callback = lookupCallback(id).


Update:

Хорошо, я думаю, что у меня есть решение, возможно.

Тогда основная проблема, с которой я столкнулся, заключалась в том, что мне нужно, чтобы 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 и может снова создать объект Address в onSuccess(). После того, как обратный вызов теперь сериализуется, всякий раз, когда SyncAdapter завершает вставку Пользователя успешно, он может получить сериализованный обратный вызов из базы данных, десериализировать его и вызвать из него «onSuccess (insertUser)».

Если объект Address является единственным необходимым, конструктор UserInsertedCallback мог бы использовать его вместо этого. Но, возможно, разработчику понадобится и другой материал из Activity, так что у них также есть возможность передать активность.

Конечно, я уверен, что это не так просто, но я скоро попытаюсь посмотреть, работает ли это. я до сих пор не знаю, как предотвратить человек от прохождения обратного вызова в качестве annonymous класса, хотя

+0

Для справки, я хочу сделать что-то похожее на это: http://devcenter.kinvey.com/android/guides/datastore – gonzaw

ответ

1

Так что я был наконец, смог добиться этого.
Он работал, как я положил его в Edit, но я дам еще несколько объяснений:

В основном, Callback класса аналогично тому, как я определил его выше, за исключением того, я изменил методы немного:

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){} 
} 

я прохожу больше информации на onFailure события, такие как данные, которые не удалось синхронизации, и пользовательские исключения, брошенной синхронизации (так как синхронизация может потерпеть неудачу по многим причинам).

Тогда каждое приложение может создать именованный класс, который расширяет Callback<T>, и может иметь любое поле, которое он хочет (до тех пор, пока оно сериализуется), как я уже упоминал в Edit. Мой вызов API принимает этот обратный вызов как параметр, сериализует его и сохраняет в базе данных вместе с _ID данных для синхронизации.
Впоследствии мой SyncAdapter берет эти данные и пытается синхронизировать их с сервером. Если возникает фатальная ошибка (одна из них не может восстановиться и повторить попытку позже, например), она вернет данные обратно в исходное состояние и отправит широковещательное сообщение, передающее сериализованные данные, сериализованное исключение (как через JSON), так и _ID и прохождение параметра с сообщением о невозможности синхронизации.
Затем я установил BroadcastReceiver, который прислушивается к этой трансляции, получает всю эту информацию и начинает Service с такими же дополнениями.
Эта новая услуга получает поле _ID, просматривает сериализованный обратный вызов из базы данных, десериализует его, десериализует данные и настраиваемое исключение и вызывает callback.onFailure(data, ex), и это работает очень хорошо!
После завершения вызова обратного вызова он удаляется из базы данных.Все это (кроме определения расширенного класса обратного вызова и вызова API) выполняется в самой базе Android.

P.S: Фактически, данные находятся в формате JSON и деаэрированы с использованием GSON. Для этого я добавил поле Class<T> data_class; в Callback<T> и метод onFailureFromJson(String json, CustomException ex)final, который десериализует JSON в объект типа T и вызывает с ним onFailure(entity,ex). Таким образом, служба вызывает этот метод onFailureFromJson.
Вот окончательный Callback<T> класс:

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);  
    }  
} 
+0

Попробуйте также определить свои методы 'abstract':' public abstract void onSuccess (T insertData); 'и' public abstract void onFailure (T failedData, CustomException ex); ' – JJD

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 
    } 
} 
+0

Привет, спасибо за ответ. Я не уверен, что получаю, где определен ExtendedCallback? В API или в приложении, использующем API ?. – gonzaw

+0

не extendedCall обратно не является расширенным классом вашего класса Callback например: [им не обращая внимания на genrics здесь ради простоты] класса 'ExtendedCallback расширяет Callback { общественных недействительный OnSuccess (T insertedData) { // делает пользовательское OnSuccess действия } общественных недействительный OnFailure() {// делать заказ OnFailure действия } } ' – ManZzup

+0

этих "extendedCallbacks" должен быть определен в приложении. Сам API не знает какого-либо подкласса Callback, так как это просто библиотека, которую может использовать любое приложение. Поэтому сам API не может вызывать 'ExtendedCallback.onSuccess();' поскольку он не будет определен. SyncAdapter находится в API, кстати (хотя конфигурация выполняется в AndroidManifest.xml конкретного приложения) – gonzaw

Смежные вопросы