在 Android 和 Java 上复制 Swift 完成处理程序
Replicating Swift completion handler on Android & Java
多年后,我尝试使用 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()
returns Task<QuerySnapshot>
所以我尝试 return a 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
);
}
}
使用 AsyncTask
您可以使用 AsyncTask
。它有 3 个步骤。
1. onPreExecute()
- 在运行宁doInBackground()
之前你想做的事情。这发生在 UI 主线程中。
2. doInBackground()
- AsyncTask,将在后台线程中执行操作(后台线程由Android 所以你不必担心)。
3.onPostExecute()
- 在这里您可以从 doInBackground 方法接收任何数据。 postExecute方法再次执行,在UI主线程
因此您可以在 doInBackground()
中执行任何 I/O 操作,并且 return 您从服务器或任何其他数据源接收到的值 onPostExecute()
是相当于 swift.
中的完成块
如何申报
要使用 AsyncTask
,您需要扩展 Android AsyncTask
。
因此您自己的 AsyncTask 声明将如下所示:
private class MyAsyncTask extends AsyncTask<Void, Void, Void> { ... }
您问的 3 个通用参数是什么?
1. Params - 执行时发送给任务的参数类型。
2. Progress - 在后台计算期间发布的进度单位的类型。 (几乎总是 Void
,除非你关心操作的实际进度。注意这是 Void
带有大写字母,而 而不是 void
作为 return 类型)。
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 主线程(因为它是一个阻塞操作)。
操作完成后,return是一个String
,同样String
在onPostExecute()
方法(记住,onPostExecute()
运行s 再次在 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()
。
非常感谢四位的帮助和领导@Daniel-b。
我现在已经解决了我的问题。
首先我创建了一个用于处理结果的接口;按照你的建议。
public interface ResultHandler<T> {
void onSuccess(T data);
void onFailure(Exception e);
}
然后在服务 class 中,我将 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
}
});
对于API 26,
CompletionHandler 可用。
检查 https://developer.android.com/reference/java/nio/channels/CompletionHandler
多年后,我尝试使用 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()
returns Task<QuerySnapshot>
所以我尝试 return a 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
);
}
}
使用 AsyncTask
您可以使用 AsyncTask
。它有 3 个步骤。
1. onPreExecute()
- 在运行宁doInBackground()
之前你想做的事情。这发生在 UI 主线程中。
2. doInBackground()
- AsyncTask,将在后台线程中执行操作(后台线程由Android 所以你不必担心)。
3.onPostExecute()
- 在这里您可以从 doInBackground 方法接收任何数据。 postExecute方法再次执行,在UI主线程
因此您可以在 doInBackground()
中执行任何 I/O 操作,并且 return 您从服务器或任何其他数据源接收到的值 onPostExecute()
是相当于 swift.
如何申报
要使用 AsyncTask
,您需要扩展 Android AsyncTask
。
因此您自己的 AsyncTask 声明将如下所示:
private class MyAsyncTask extends AsyncTask<Void, Void, Void> { ... }
您问的 3 个通用参数是什么?
1. Params - 执行时发送给任务的参数类型。
2. Progress - 在后台计算期间发布的进度单位的类型。 (几乎总是 Void
,除非你关心操作的实际进度。注意这是 Void
带有大写字母,而 而不是 void
作为 return 类型)。
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 主线程(因为它是一个阻塞操作)。
操作完成后,return是一个String
,同样String
在onPostExecute()
方法(记住,onPostExecute()
运行s 再次在 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()
。
非常感谢四位的帮助和领导@Daniel-b。
我现在已经解决了我的问题。
首先我创建了一个用于处理结果的接口;按照你的建议。
public interface ResultHandler<T> {
void onSuccess(T data);
void onFailure(Exception e);
}
然后在服务 class 中,我将 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
}
});
对于API 26, CompletionHandler 可用。 检查 https://developer.android.com/reference/java/nio/channels/CompletionHandler