Репликация обработчика завершения Swift на Android и Java - PullRequest
0 голосов
/ 30 декабря 2018

Спустя годы я пытаюсь разработать приложение для Android, используя Firebase Firestore.Я в основном пытаюсь воспроизвести эту функцию Swift:

func getCategories(onCompletion completionBlock: @escaping (_ categories: [Category]?, _ error: Error?) -> Void) {

  firestore.collection("cats").getDocuments { (snap, error) in
    guard let snap = snap else {
      completionBlock(nil, error ?? anUnknownError)
      return
    }

    var categories: [Category] = []
    for document in snap.documents {
        let cat = Category.init(data: document.data())
        categories.append(cat)
    }
    completionBlock(categories, nil)
  }
}

Но я понятия не имею, что эквивалентно блокам Swift, даже не знаю, существует ли она.

Я проверилИсходные коды Firebase.Query.get() возвращает Task<QuerySnapshot>, поэтому я попытался вернуть Task<List<Category>> без удачи.

Любая помощь?Спасибо.

РЕДАКТИРОВАТЬ: добавлен код Android, чтобы уточнить, что я пытаюсь сделать.

public class FirestoreService {

    private static volatile FirestoreService singleton = new FirestoreService();

    public static FirestoreService getInstance() {
        return singleton;
    }

    private FirebaseFirestore firestore() {
        // default firestore instance
        FirebaseFirestore db = FirebaseFirestore.getInstance();

        // default firestore settings
        FirebaseFirestoreSettings settings = db.getFirestoreSettings();

        // firestore settings builder
        FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(settings);

        // enable timstamps
        builder.setTimestampsInSnapshotsEnabled(true);

        // set new settings to db instance
        db.setFirestoreSettings(builder.build());


        // return db with new settings.
        return db;
    }



    public void getProductCategories(Handler? handler) {

       Task<QuerySnapshot> task = firestore().collection("coll").get();
       task.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
        @Override
        public void onComplete(@NonNull Task<QuerySnapshot> task) {
            try {
                if (task.isSuccessful()) {
                    List<Category> cats = new ArrayList<>();
                    for (QueryDocumentSnapshot doc : task.getResult()) {
                        String id = doc.getId();
                        Map<String, Object> data = doc.getData();
                        Category cat = new Category(id, data);
                        cats.add(cat);
                    }
                    // now I need completion handler
                } else {
                    Log.w("ERROR", "Error getting categories", task.getException());
                }
            } catch (Exception e) {
                Log.e("ERROR", e.getMessage());
            }
        }
    });
    }

}



public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FirestoreService.getInstance().getCategories().addCompletionListener(
            // handle List<Category> and respresent in UI
        );
    }
}

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Большое спасибо вам за помощь и руководство @ Daniel-b.

Я уже решил свою проблему.

Сначала я создал интерфейс для обработки результатов;как вы предложили.

public interface ResultHandler<T> {
    void onSuccess(T data);
    void onFailure(Exception e);
}

Затем в классе обслуживания я добавил ResultHandler к входным параметрам функции:

public void getUserInfo(String id, ResultHandler<UserInfo> handler) {
    firestore().collection("userInfo").document(id).get().addOnCompleteListener(snap -> {
        if (snap.isSuccessful()) {
           try {
               // failable constructor. use try-catch
               UserInfo info = new UserInfo(snap.getResult().getId(), snap.getResult().getData());
               handler.onSuccess(info);
           } catch (Exception e) {
                handler.onFailure(e);
           }
        } else {
            handler.onFailure(snap.getException())
        }
    });
}

И вызвал службу в Activity

FirestoreService.getInstance().getUserInfo("ZsrAdsG5HVYLTZDBeZtkGDlIBW42", new ResultHandler<UserInfo>() {
    @Override
    public void onSuccess(UserInfo data) {
        Log.i("UserInfo", data.id);                    
    }

    @Override
    public void onFailure(Exception e) {
        // getting data failed for some reason
    }
});
0 голосов
/ 30 декабря 2018

Использование AsyncTask

Вы можете использовать AsyncTask.У этого есть 3 шага к этому.

1. onPreExecute() - что вы хотите сделать перед запуском doInBackground().Это происходит в основном потоке пользовательского интерфейса .

2. doInBackground() - AsyncTask будет выполнять операции в фоновом потоке (фоновый поток создается Android, поэтому вам не нужно об этом беспокоиться).

3. onPostExecute() - здесь вы можете получить любые данные из метода doInBackground.Метод postExecute выполняется снова в основном потоке пользовательского интерфейса .

Таким образом, вы можете выполнять любые операции ввода-вывода в doInBackground() и возвращать значение, полученное с сервера или любого другогодругой источник данных, и onPostExecute(), является эквивалентом блока завершения в swift.

Как объявить

Чтобы использовать AsyncTask, вам необходимо расширить Android AsyncTask.

Итак, ваше собственное объявление AsyncTask будет выглядеть так:

private class MyAsyncTask extends AsyncTask<Void, Void, Void> { ... }

Какие 3 общих аргумента вы задаете?

1. Params - тип параметров, отправляемых заданию при выполнении.

2. Progress - тип единиц прогресса, опубликованных в фоновом режимевычисление.(Почти всегда будет Void, если вы не заботитесь о фактическом ходе операции. Обратите внимание, что это Void с заглавной буквой, а не void в качестве типа возврата).

3. Result - тип результата вычисления фона.

Full Example

private class LongOperation extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... params) {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.interrupted();
                }
            }
            return "Executed";
        }

        @Override
        protected void onPostExecute(String result) {
            TextView txt = findViewById(R.id.output);
            txt.setText(result); 
        }
    }

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

Когда операция завершена, она возвращает String, и то же самое String получено в методе onPostExecute() (и помните, onPostExecute() снова запускается в основном потоке пользовательского интерфейса).Таким образом, вы можете изменить свой пользовательский интерфейс на строковое значение, которое вы получили от длинной операции блокировки.

Если вам нужна документация, вот она:

https://developer.android.com/reference/android/os/AsyncTask

Использование шаблона наблюдателя

Вы также можете использовать шаблон наблюдателя в вашей ситуации.

Создайте интерфейс с методом onSuccess(). Имейте объект, реализующий этот интерфейс, и всякий раз, когда вам это нужно,Вы можете вызвать метод onSuccess().

Пример:

public Interface SuccessInterface{
   void onSuccess()
}

public class SuccessHandler implements SuccessInterface{
   public void onSuccess(){
         //success code goes here
   }

}

, затем в своем коде создать экземпляр SucessHandler и вызвать onSuccess(), когда вам нужно.

...