Зависящие друг от друга функции обратного вызова в Android Studio - более элегантное решение - PullRequest
0 голосов
/ 28 мая 2020

В моем приложении есть 2 функции обратного вызова, одна из которых требует результата другой, поэтому я хочу, чтобы они выполнялись асинхронно. Сценарий такой:

  1. Я получаю токен устройства с FirebaseInstanceId.getInstance().getInstanceId(). Это 1-й обратный вызов.

  2. Когда у меня есть токен, я использую его для доступа к базе данных Firebase Realtime для получения данных пользователя. Это второй обратный вызов.

В моем текущем решении я использую AsyncTask с Semaphore в функции doInBackground, например:

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

        @Override
        protected String doInBackground(Void... voids) {
            final Semaphore semaphore = new Semaphore(0);
            FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
                @Override
                public void onSuccess(InstanceIdResult instanceIdResult) {
                    token = instanceIdResult.getToken();
                    JSONObject requestObject = new JSONObject();
                    try {
                        requestObject.put("token", token);
                        semaphore.release();
                    } catch (JSONException e) {
                        Log.e(TAG_MAINACTIVITY, token);
                        semaphore.release();
                    }
                    JsonObjectRequest req = new JsonObjectRequest(Request.Method.POST, REQUEST_URL + "token",
                            requestObject, new Response.Listener<JSONObject>() {
                        @Override
                        public void onResponse(JSONObject response) {
                            Log.i(TAG_MAINACTIVITY, "Token saved successfully");
                        }
                    },
                            new Response.ErrorListener() {
                                @Override
                                public void onErrorResponse(VolleyError error) {
                                    Log.e(TAG_MAINACTIVITY, "Failed to save token - " + error);
                                }
                            });

                    _queue.add(req);
                }
            });

            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return token;
        }

        @Override
        protected void onPostExecute(String token) {
            getUser(token);
        }
    }

 public void getUser(String token) {
        db.child("users").child(token).addListenerForSingleValueEvent(new ValueEventListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                if (!dataSnapshot.exists()) {
                    // Navigate to fragment where new users can sign up:
                    goToSignup();
                } else {
                    currentUser = dataSnapshot.getValue(User.class);
                    assert currentUser != null;
                    updateHeadline(currentUser.getName()); // Update UI to display the user's name
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
                Log.d(TAG_MAINACTIVITY, databaseError.getMessage());
            }
        });
    }

, а в моем OnCreate я выполняю следующее:

_queue = Volley.newRequestQueue(this);
AsyncOp getToken = new AsyncOp();
getToken.execute();

Он работает отлично, но я не могу не чувствовать, что мне не хватает всего смысла здесь и что есть лучшее решение, которое я могу использовать. Я изучил другие вопросы StackOverflow, связанные с аналогичными проблемами, и нашел несколько предложений, но я запутался, пытаясь реализовать их, будучи довольно новичком во всем этом испытании (на самом деле это мой первый «настоящий» проект). Я попытался найти что-то, что работало бы как Promise, и нашел Future, но, вероятно, я сделал это неправильно, так как это не сработало. Это единственное решение, которое сработало для меня, кроме простого вложения getUser(token) внутри первого обратного вызова. Так что, если у вас есть идеи, которые подойдут мне, я буду рад их услышать.

1 Ответ

1 голос
/ 28 мая 2020

Firebase выполняет весь сетевой и дисковый ввод-вывод вне основного потока. Это означает, что редко возникает причина запускать взаимодействия Firebase в фоновой задаче.

Кроме того, при использовании семафора поток значительно усложняется. Я бы подумал о том, чтобы просто поместить вызов, чтобы пользователь попал в обработчик завершения предыдущего вызова.

В совокупности получается:

FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
    @Override
    public void onSuccess(InstanceIdResult instanceIdResult) {
        token = instanceIdResult.getToken();
        JSONObject requestObject = new JSONObject();
        requestObject.put("token", token);

        JsonObjectRequest req = new JsonObjectRequest(Request.Method.POST, REQUEST_URL + "token",
                requestObject, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                Log.i(TAG_MAINACTIVITY, "Token saved successfully");
                getUser(token);
            }
        },
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(TAG_MAINACTIVITY, "Failed to save token - " + error);
            }
        });
        _queue.add(req);
    }
});
...