마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

activity에서 firebase 정보를 추출해 fragment에서 sqlite를 통해 다른 정보 추출

0 추천
activity에서 firebase를 통해 pettype과 effect의 정보를 받아 
fragment 리사이클뷰에서 이 정보를 통해
 sqlite의 다른 정보를 추출해내는 코드를 짰는데요, 
activity->framgment로 정보를 옮기는 과정에서 에러가 계속 뜨는 거같은데, 
혹시 해결방법 알려주실 분 계신가요 ㅠㅠ
//activity 부분
reference = FirebaseDatabase.getInstance().getReference("Cookforpet");
DatabaseReference usertype = reference.child("UserAccount").child(user.getUid()).child("pettype");
usertype.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        String tmp =snapshot.getValue().toString();
        if (tmp.equals("Dog"))
            pettype = 2;
        else if (tmp.equals("Cat"))
            pettype = 3;
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {
    }
});
DatabaseReference usereffect = reference.child("UserAccount").child(user.getUid()).child("effect");
usereffect.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        effect=snapshot.getValue().toString();
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {

    }
});
//activity->fragemnt 이동 코드
public void onFragmentChanged(int index){
    if (index == 0) {
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, recommendFragment).commit();
    } else if (index == 1){
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, resultFragment).commit();
    }
//fragment에서 코드 받아오는 부분
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    rootView = (ViewGroup) inflater.inflate(R.layout.fragment_recommend, container, false);
    recycler_rcp =(RecyclerView) rootView .findViewById(R.id.recycler_rcp);
    LinearLayoutManager layoutManager =
            new LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false);
    recycler_rcp.setLayoutManager(layoutManager);

    adapter = new RecipeItemAdapter();


    petType=activity.pettype;
    effect=activity.effect;
    recipe_code = activity.dbAc.getRecommendCode(petType, effect);//이부분에서 계속 에러남!!
    ArrayList<ArrayList<String>> Recipelist = activity.dbAc.getRecipelist(recipe_code);
    for (int i = 0; i < Recipelist.size(); i++) {
        adapter.addItem(new RecipeItem(Recipelist.get(i).get(0), Recipelist.get(i).get(1), Recipelist.get(i).get(8), Recipelist.get(i).get(2),
                Recipelist.get(i).get(3), Recipelist.get(i).get(4), Recipelist.get(i).get(5), Recipelist.get(i).get(6),
                Recipelist.get(i).get(7)));
    }
    return rootView;
}
haky619 (190 포인트) 님이 2022년 3월 29일 질문

1개의 답변

+1 추천

Fragment가 같은 Activity 안에 있는 거라면, Activity에서 데이터를 가져온 후 Fragment에 알려주어야 하는데, 이 부분이 한방에 되는게 아니라 좀 불편한 면이 있기 때문에, 특별한 이유가 없다면 그냥 Fragment에서 다 처리하는게 수월합니다.

Firebase에서 데이터를 먼저 가져오신 후 callback 성공시 필요한 데이터를 가져오시면 될 것 같습니다. 

 

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    rootView = (ViewGroup) inflater.inflate(R.layout.fragment_recommend, container, false);
    return rootView;
}

@Override
public void onViewCreated (View view,  Bundle savedInstanceState) {
      setupViews();
      fetchCookForPet();
}

private void setupViews() {
    recycler_rcp = (RecyclerView) rootView .findViewById(R.id.recycler_rcp);
    LinearLayoutManager layoutManager =
            new LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false);
    recycler_rcp.setLayoutManager(layoutManager);

    adapter = new RecipeItemAdapter();
}

private void setupDatabase() {
    reference = FirebaseDatabase.getInstance().getReference("Cookforpet");  
}

private void fetchCookForPet() {
  DatabaseReference petTypeRef = reference.child("UserAccount").child(user.getUid()).child("pettype");
  petTypeRef.addValueEventListener(new ValueEventListener() {
     @Override
      public void onDataChange(@NonNull DataSnapshot snapshot) {
           String tmp = snapshot.getValue().toString();
           if (tmp.equals("Dog"))
             pettype = 2;
          else if (tmp.equals("Cat"))
             pettype = 3;
          else. {
             // TODO : Dog나 Cat이 아닌 경우에 대한 처리 필요.
          }
 
          fetchEffect();
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {
    }
});

private void fetchEffect() {
  // reference.child("UserAccount").child(user.getUid()) 은 중복으로 사용되고 있으므로 멤버변수로 빼서.
  DatabaseReference usereffect = reference.child("UserAccount").child(user.getUid()).child("effect");
  usereffect.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        effect = snapshot.getValue().toString();
        fetchAndBindRecipeItems(); // <-- 여기처럼, effect를 가져온 후에 Receipt를 가져와야 함.
    }
    @Override
    public void onCancelled(@NonNull DatabaseError error) {

    }
});
}

private void fetchAndBindRecipeItems() {
    List<RecipeItem> items = getRecipeItems();
    adapter.setItems(items); //  Adapter에 List<RecipeItem>를 설정하고 notifyDataSetChanged()를 호출.
}

private List<RecipeItem> getRecipeItems() {
     // Activity.dbAc를 Fragment 로 옮길 것
    String recipeCode = dbAc.getRecommendCode(petType, effect); 
    List<List<String>> recipeLists = dbAc.getRecipelist(recipeCode);
    
    List<RecipeItem> result.= new ArrayList<>();
    for (List<String> receiptList : recipeLists) {
          // 아래 데이터타입 변환하는 코드를 보면 파이어베이스 구조를 점검해 보셔야 할 것으로 보이네요.
          // 아래처럼 리스트 구조가 아니라, 맵처럼 해당 필드를 가지는 구조로 flat하게 바꾸셔야 할 것 같은데요...
          RecipeItem item = new RecipeItem(
                 receiptList.get(0), 
                 receiptList.get(1), 
                 receiptList.get(8), 
                 receiptList.get(2),
                 receiptList.get(3), 
                 receiptList.get(4), 
                 receiptList.get(5), 
                 receiptList.get(6),
                 receiptList.get(7));
          recipeItems.add(item);
    }
    
     return result;
}

}

 

참고로 pettype을 enum  을 사용하면 더 좋을 것 같습니다. 이건 Firebase 레벨에서도 가능할 것 같고 Fragment에서도 할 수 있을 것 같네요. 예를 들면,

public enum PetType { 
        Dog, Cat;
        
        public static PetType byName(String petName) {
            if (Dog.name().equals(petName)) return Dog;
            if (Cat.name().equals(petName)) return Cat;
            return null;
        }
    };

PetType petType = PetType.byName(snapshot.getValue().toString());
switch (petType) {
   case PetType.Dog: 
            //
            break;
  case PetType.Cat: 
            //
            break;
  default:
           //
           break;
}

 

Edit: 코드를 수정했습니다. pettype과 effect를 가져오고 난 다음에 receipt를 가져오셔야 해요. 

fetchCookForPet과 fetchEffect는 순차적으로 호출될 필요가 없이 동시 호출이 되어도 될 것 같은데, reciepe를 가져오기 위해서는 pettype과 effect정보가 필요하기 때문에 fetchCookForPet을 호출한 다음 콜백에서 fetchEffect를 호출했는데, 이 두가지를 동시에 호출해서 처리할 수 있는지 확인해 보세요. 성능이 향상될 겁니다. 제일 손쉬운 방법(추천하기는 그렇지만)으로는 fetchCookForPet의 콜백과 fetchEffect에 두곳 모두에서 fetchAndBindReceipeItems하되 fetchAndBindReceipeItems pettype과 effect 모두 존재할 때만 처리하는 겁니다. RxJava나 Coroutine같은 툴이 없으면 할 수는 있지만 이런 부분이 매끄럽지 처리가 잘 안되는 것 같아요.
코드를 보시면 느끼시겠지만, reciepe를 가져오기 위해 콜백이 2번 연쇄적으로 호출되기 때문에, 파이이베이스 데이터베이스 구조가 효율적인지 점검해보실 필요가 있어 보입니다. 파이이베이스에서 추천하는 데이터 구조에 관련된 무료 튜토리얼이 많으니 찾아보시기 바랍니다. 그리고 pettype, effect같은 변수는 멤버변수일 필요가 없다면 그냥 로컬변수로 만들어서 다른 메소드의 파라미터로 넘기시는 것도 체크해 보시기 바랍니다.

무엇보다도 해당 로직에 대한 자신감이 생기시면, 파이어베이스에서 데이터를 가져오는 부분을 별도의 클래스로 빼서서 처리하는 걸 시도해 보세요. 결국은 이렇게 가야 뷰와 데이터를 다루는 로직간에 종속성이 줄어들어서 코드관리가 훨씬 편해집니다.

spark (227,530 포인트) 님이 2022년 3월 29일 답변
spark님이 2022년 3월 31일 수정
말씀해주신 방법대로 fragment로 옮겼는데, 아래와같은 에러가 계속뜨는데, 이유가 뭔가요 ?  
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.ArrayList com.example.myapplication.DatabaseAccess.getRecommendCode(int, java.lang.String)' on a null object reference
        at com.example.myapplication.RecommendFragment.onViewCreated(RecommendFragment.java:109)
...