【问题标题】:Replicating Swift completion handler on Android & Java在 Android 和 Java 上复制 Swift 完成处理程序
【发布时间】:2019-05-27 12:22:36
【问题描述】:

多年后,我正在尝试使用 Firebase Firestore 开发一款 Android 应用。我基本上是在尝试复制这个 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
        );
    }
}

【问题讨论】:

    标签: java android ios google-cloud-firestore completionhandler


    【解决方案1】:

    非常感谢四位您的帮助和领导@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
        }
    });
    

    【讨论】:

    • 对我来说是一个很大的+1,因为这也适用于AsyncTask(我知道,我知道,它们在较新的版本中已被弃用):创建处理程序final,然后调用@ 987654326@ 在onPostExecute。这似乎是在DispatchQueue.global(qos: .background).async {//Some code} 中复制DispatchQueue.main.async {completion(something)} 的好方法,而且与原始代码相比,它不需要大量代码! :)
    【解决方案2】:

    使用异步任务

    您可以使用AsyncTask。它有3个步骤。

    1. onPreExecute() - 在运行 doInBackground() 之前您想要做的事情。这发生在 UI 主线程中。

    2. doInBackground()- AsyncTask,将在后台线程中进行操作(后台线程由Android创建,因此您无需担心它)。

    3.onPostExecute() - 在这里您可以接收来自 doInBackground 方法的任何数据。 postExecute 方法在 UI 主线程中再次执行。

    所以你可以在doInBackground()中做任何I/O操作,并返回你从服务器或任何其他数据源接收到的值,而onPostExecute()相当于swift中的完成块。

    如何申报

    要使用AsyncTask,需要扩展Android AsyncTask

    所以您自己的 AsyncTask 声明将如下所示:

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

    你问的 3 个通用参数是什么?

    1. Params - 执行时发送给任务的参数类型。

    2. Progress - 在后台计算期间发布的进度单元的类型。 (几乎总是Void,除非您关心操作的实际进度。注意这是Void,带有大写字母,不是 void 作为返回类型)。

    3. Result - 后台计算结果的类型。

    完整示例

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

    在示例中,我创建了一个假的长操作,您不能在 UI 主线程上运行(因为它是一个阻塞操作)。

    当操作完成时,它返回一个String,并且在onPostExecute()方法中接收到sameString(请记住,onPostExecute()在UI主线程上运行再次)。因此,您可以使用从长阻塞操作中收到的字符串值更改您的 UI。

    如果你想要文档,这里是:

    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()

    【讨论】:

    • 你能分享你所说的代码表示吗?正如我所说,我已经有 6 年没有开发 Android 和 Java 了,而且我也不是它们的专家。现在我真的很沮丧和困惑。
    • @ArdaOğulÜçpınar 我希望这能回答你的问题 :) 如果还有什么不清楚的地方,请在这里评论,我会回复。
    • 嗯。它应该可以完成这项工作,但我不知道它是否适合我的用例。我实际上是在尝试为 Firestore 事物创建服务;将数据获取和数据解析保持在控制器之外。而且我不确定如何在我正在尝试做的事情上实现这一点。我正在编辑我的问题,以澄清我在做什么。
    • @ArdaOğulÜçpınar 我看了你的编辑,为什么不直接使用观察者设计模式呢? (快速的代表)。我不明白你为什么需要一个完成处理程序,是阻塞操作还是什么?
    • 实际上,我在 Swift 上使用的是 CH。由于我对 Java 的功能了解不多,所以我的想法停留在完成处理程序上。但如果有类似委托的模式,我当然可以使用它。谢谢@daniel-b
    【解决方案3】:

    对于 API 26, CompletionHandler 可用。 检查https://developer.android.com/reference/java/nio/channels/CompletionHandler

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多