0

私はもっときちんとした理解できるコードを得るために私のアンドロイドアプリをリファクタリングしようとしています。私はFirebaseデータベースをデータ永続性のために使用しています。Firebaseデータベースのデータを同期的にロードするにはどうすればいいですか?

私がやったことは、ほとんどが自分のクラスをシングルトンにリファクタリングすることなので、他のクラスでも簡単に使うことができます。

このアプローチは、ほとんどのクラス(Firebase Storage Controller、GeoFire Controller、ecc)で機能しましたが、AuthシステムとDbからユーザを取得する必要があるため、Authコントローラでこれを行うのに問題があります。 。

以下、私のコードの抜粋です。

開始アクティビティ

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から検索する必要があるため、同期を維持することは難しいようです。

子スレッドが終了するまで、メインスレッドを一時停止するいくつかの試み

私は新しいスレッドを使用してみましたし、参加してきましたが()、しかし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; 
} 

AsyncTask

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

} 

任意の提案?

+0

FriebaseAuthはすでにシングルトンです。皮肉なことに、スパゲッティのコードを使って、もっときれいにするようです。 AuthListener、ResultListenerの略語は何ですか?なぜあなたはそれらを必要としますか? – uguboz

+0

ResultListenerそれは単なるインタフェースです。私はそれを示す質問を更新しました。タスクが完了すると、結果値(ユーザが内部でインスタンス化されたシングルトン)を返す必要があります。現在のユーザーオブジェクトをメインスレッドに戻すには、これが必要です。 質問:「AuthシステムとDbからユーザを取得する必要があります」 –

+0

実際の問題は、firebaseリスナーが新しいスレッド/非同期タスクにinitAuthメソッドをラップする方法ですメイン(スリープ)スレッドでは実行されません。私が見ることができるように、それを新しいスレッドから呼び出すことは解決策ではありません。何か不足していますか? –

答えて

0

あなたのコードは面倒です。単純なオブザーバーのパターンは、あなたのケースで動作します。私はあなたが軽量で使いやすいEventbus Libraryを使用することをお勧めします。

+0

これは、質問で長らく説明されているように、要求されたマルチスレッド機能に対処する方法を説明していません。私が使用しているパターンはMVCなので、アクティビティが作成されてシングルトンに格納される前にそのユーザーを取得する必要があります。したがって、Eventbusのソリューションは私の問題を解決しません。 –

0

誰かが必要な場合は、私が投稿する解決策が見つかりました。これが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); 
       } 
      } 
     }); 
    } 
} 
関連する問題