【问题标题】:How to load Firebase database data in a in a synchronous way?如何以同步方式加载 Firebase 数据库数据?
【发布时间】:2016-10-31 12:31:51
【问题描述】:

我正在尝试重构我的 android 应用程序以获得更整洁和易于理解的代码。我正在使用 Firebase 数据库进行数据持久化。

我所做的主要是将我的类重构为单例,因此我可以轻松地在任何其他类中使用它们。

这种方法适用于大多数类(Firebase 存储控制器、GeoFire 控制器、ecc),但我在我的 Auth 控制器上执行此操作时遇到了麻烦,因为它也需要从 Auth 系统和 Db 中检索用户。

下面是我的代码的一些 sn-ps。

开始活动

public class StartActivity extends AppCompatActivity {

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

        AuthController.getInstance(new ResultListener<AuthController>(){
            @Override
            public void onResult(AuthController result) {
                AuthController authController = result;
                Intent intent;
                if ( authController.getUser() != null ) {
                    intent = new Intent(getApplicationContext(), MainActivity.class);
                } else {
                    intent = new Intent(getApplicationContext(), LoginActivity.class);
                }
                startActivity(intent);

                finish();
            }
        });

    }

}

我的身份验证控制器

public class AuthController {

    private static AuthController authController;
    private User user;

    public User getUser() {
        return user;
    }

    public static AuthController getInstance(){
        if( authController == null ){
            getInstance(null);
            //TODO return authController
        }
        return authController;
    }

    public static void getInstance(final ResultListener<AuthController> authControllerResultListener) {
        initAuth(new ResultListener<AuthListener>() {
            @Override
            public void OnResult(AuthController authController) {
                AuthController.authController = authController;
                if(authControllerResultListener != null) {
                    authControllerResultListener.onResult(authController);
                }
            }
        });
    }

    private static void initAuth(final ResultListener<AuthController> authListener) {
        final AuthController authController = new AuthController();
        FirebaseAuth mAuth = FirebaseAuth.getInstance();
        mAuth.addAuthStateListener(new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                final FirebaseUser authUser = firebaseAuth.getCurrentUser();
                if (authUser != null) {
                    String authUserId = authUser.getUid();
                    FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
                    firebaseDatabase.getReference("users").child(authUserId).addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(DataSnapshot dataSnapshot) {
                            authController.user = dataSnapshot.getValue(User.class);
                            authListener.onResult(authController);
                        }

                        @Override
                        public void onCancelled(DatabaseError databaseError) {
                            authController.user = null;
                            authListener.onResult(authController);
                        }
                    });
                } else {
                    authController.user = null;
                    authListener.onResult(authController);
                }
            }
        });
    }

}

我的抽象活动,这是由我需要使用身份验证控制器实例的其他活动扩展的。

public abstract class MyAppActivity extends AppCompatActivity {

    protected final AuthController authController = AuthController.getInstance();

}

结果监听器接口

public interface ResultListener<ResultType> {
    void onResult(ResultType result);
}

所以我想做的是以同步方式获取包含用户的身份验证控制器的实例,但是当然,必须从 firebase 检索用户,所以看起来很难保持同步。

一些尝试

我尝试使用新线程和暂停主线程直到子线程完成的 join(),但似乎 Firebase 侦听器在主线程上运行,因此代码在执行过程中卡住了。与 AsyncTask 相同(见下文)。

新话题

public static AuthController getInstance(){
    if( authController == null ){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                initAuth(new ResultListener<AuthController>() {
                    @Override
                    public void onResult(AuthController authController) {
                        AuthController.authController = authController;
                    }
                });
            }
        });
        thread.run();
        thread.join();
    }
    return authController;
}

异步任务

public static AuthController getInstance(){
    if( authController == null ){
        final Semaphore semaphore = new Semaphore(0);
        InitAuthAsync initAuthAsync = new AuthController().new InitAuthAsync();
        initAuthAsync.setResultListener(new ResultListener<Void>() {
            @Override
            public void onResult(Void result) {
                semaphore.release();
            }
        });
        initAuthAsync.execute();
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return authController;
}

private class InitAuthAsync extends AsyncTask<Void, Void, Void>{

    private ResultListener<Void> resultListener;
    public void setResultListener(ResultListener<Void> resultListener) {
        this.resultListener = resultListener;
    }

    @Override
    protected Void doInBackground(Void... voids) {
        initAuth(new ResultListener<AuthController>() {
            @Override
            public void onResult(AuthController authController) {
                AuthController.authController = authController;
                resultListener.onResult(aVoid);
            }
        });
        return null;
    }

}

有什么建议吗?

【问题讨论】:

  • FriebaseAuth 已经是单例了。具有讽刺意味的是,您似乎有一个意大利面条代码以使其更整洁。 AuthListener、ResultListener 代表什么?你为什么需要它们?
  • ResultListener 它只是一个接口。我已经更新了显示它的问题。任务完成后,它需要传回结果值(其中包含用户的实例化单例)。我需要这个来将当前用户对象返回到主线程。正如问题中所说:“它需要从 Auth 系统和 Db 中检索用户”
  • 真正的问题是如何将 initAuth 方法包装在一个新的线程/异步任务中,以使 firebase 侦听器不在主(睡眠)线程上执行。如我所见,从新线程调用它不是解决方案。我错过了什么吗?

标签: android multithreading firebase firebase-realtime-database firebase-authentication


【解决方案1】:

你的代码很乱。简单的观察者模式将适用于您的情况。但我建议你使用轻量级且易于使用的Eventbus Library

【讨论】:

  • 这没有解释如何处理问题中长时间解释的请求的多读功能。我使用的模式是 MVC,所以我需要在创建活动之前获取用户并将其存储在单例中。所以 Eventbus 解决方案并不能解决我的问题。
【解决方案2】:

如果有人需要,我会发布一个解决方案。请注意,这不是与 firebase 一起使用的最佳架构。

public class StartActivity extends AppCompatActivity {

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

        AuthController.initAuth(new ResultListener<AuthController>() {
            @Override
            public void onResult(AuthController authController) {
                Intent intent;
                if ( authController.getUser() != null ) {
                    intent = new Intent(getApplicationContext(), MainNavigationActivity.class);
                } else {
                    intent = new Intent(getApplicationContext(), LoginActivity.class);
                }
                startActivity(intent);

                finish();
            }
        });

    }



}

public class AuthController {

    private static AuthController authController;
    private User user;

    public User getUser() {
        return user;
    }

    public static AuthController getInstance() {
        if (authController == null) {
            throw new IllegalStateException("The auth system is not initializet. Restart the app.");
        }
        return authController;
    }

    public static void initAuth(final ResultListener<AuthController> authListener) {
        final AuthController authController = new AuthController();
        FirebaseAuth mAuth = FirebaseAuth.getInstance();

        mAuth.addAuthStateListener(new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                final FirebaseUser authUser = firebaseAuth.getCurrentUser();
                if (authUser != null) {
                    String authUserId = authUser.getUid();
                    FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
                    firebaseDatabase.getReference("users").child(authUserId).addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(DataSnapshot dataSnapshot) {
                            authController.user = dataSnapshot.getValue(User.class);
                            AuthController.authController = authController;
                            authListener.onResult(authController);
                        }

                        @Override
                        public void onCancelled(DatabaseError databaseError) {
                            authController.user = null;
                            AuthController.authController = authController;
                            authListener.onResult(authController);
                        }
                    });
                } else {
                    authController.user = null;
                    AuthController.authController = authController;
                    authListener.onResult(authController);
                }
            }
        });
    }
}

【讨论】:

    猜你喜欢
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多