기본 콘텐츠로 건너뛰기

[Android][Dagger2][Firebase] Android에 Dagger를 통한 Firebase Database DI 하기

본 글은 Android에서 Dagger2를 사용하여 Firebase DatabaseReference를 DI 하는 것을 설명하므로


Dagger2, Firebase, DI에 대한 설명은 생략합니다.


관련하여 구글링을 했지만 개념을 이해하기 쉽지 않았습니다.


큰 개념은 이해가 가지만 실 구현은 더더욱 와닿지가 않더군요.


우선 간단한 수준으로 DI를 성공시킨 뒤 까먹지 않기 위해 기록을 남깁니다.




우선 DI를 하는데 사용된 라이브러리 Dagger2에서 사용되는 핵심 구성 요소들은 다음과 같이 3가지가 있다.

  1. @Module + @Provides
  2. @Inject
  3. @Component



1. @Module + @Provides

- Class에 선언되는 어노테이션으로 DI에 사용될 실제 구현체들을 모듈 단위로 모아놓은 어노테이션으로 이해된다.

- Module 어노테이션이 선언된 클래스에는 @Provides 어노테이션을 메서드 앞에 붙여주어 DI에 사용될 구현체를 정의할 수 있다.


2. @Inject

@Module + @Provides의 구현체를 주입받을 타겟이 된다.

Class, Method, Field에 사용 가능하지만 본 글에서는 Field에 사용한 예를 보인다.


3. @Component

@Module + @Provides와 @Inject를 연결해주는 Bridge라고 보면 된다.




예제의 구성 요소

  1. UsersActivity.java (Android Activity - @Inject)
  2. UserDataSource.java (Interface)
  3. UserRepository (Class - UserDataSource의 구현체)
  4. RepositoryComponent (@Component)
  5. RepositoryModule (@Module + @Provides)



1. 먼저 UserDataSource Interface를 보겠습니다.

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface UserDataSource {
    
    interface LoadUsersCallback {
 
        void onUsersLoaded(List<User> users);
 
        void onDataNotAvailable();
    }
 
    interface GetUserCallback {
 
        void onUserLoaded(User user);
 
        void onDataNotAvailable();
    }
 
    void getUser(@NonNull String userId, @NonNull GetUserCallback callback);
 
    void saveUser(@NonNull User user);
 
}
 

cs


2. 다음은 이를 구현한 구현체 UserRepository 클래스입니다.


우선 getUser는 구현하지 않았고, saveUser만 구현된 상태입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class UserRepository implements UserDataSource {
 
    private DatabaseReference ref;
 
    public UserRepository(DatabaseReference databaseReference) {
        this.ref = databaseReference;
    }
 
    @Override
    public void getUser(@NonNull String userId, @NonNull GetUserCallback callback) {
 
    }
 
    @Override
    public void saveUser(@NonNull final User user) {
 
        ref.child("users").addListenerForSingleValueEvent(new ValueEventListener() {
 
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                ref.child("users").child(user.getId()).setValue(user);
            }
 
            @Override
            public void onCancelled(DatabaseError databaseError) {
 
            }
        });
    }
}
 
cs



 Firebase의 실시간 데이터베이스 사용법은 아래 링크를 통해 확인 바랍니다.


3. RepositoryModule: 이제 본격적인 DI를 위해 @Module + @Provides를 살펴보겠습니다.
 
본 예제의 RepositoryModule은 2개의 @Provides 메서드를 갖고 있습니다.

하나는 UserDataSource를 리턴하는 것이고,
다른 하나는 GroupDataSource를 리턴합니다.

Context 객체를 인자로 받아 Firebase DatabaseReference 객체를 얻어다 놓고
이를 통해 원하는 XXXRepository를 생성합니다.

각 메서드는 @Singleton 어노테이션이 붙어있기 때문에 Singleton 패턴이 적용되어 하나의 객체만 유지됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Module
public class RepositoryModule {
 
    private DatabaseReference databaseReference;
 
    public RepositoryModule(Context context) {
        int playServicesStatus = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
        if (playServicesStatus == ConnectionResult.SUCCESS) {
            databaseReference = FirebaseDatabase.getInstance().getReference();
        }
    }
 
    @Singleton
    @Provides
    UserDataSource provideUserDataSource() {
        return new UserRepository(databaseReference);
    }
 
    @Singleton
    @Provides
    GroupDataSource provideGroupDataSource() {
        return new GroupRepository(databaseReference);
    }
}
 
cs


4. RepositoryComponent: @Component 를 정의한 인터페이스입니다.


@Component는 반드시 Interface에 붙여져야 합니다.


1
2
3
4
5
6
@Singleton
@Component(modules = {RepositoryModule.class})
public interface RepositoryComponent {
    void inject(UsersActivity target);
}
 
cs




 5. UsersActivity: DI를 받아 사용할 Activity 입니다.

line 4를 보면 RepositoryComponent를 통해 Inject를 받을 수 있도록 @Inject 어노테이션이 정의되어 있습니다.

Dagger에 의해 자동으로 생성된 클래스를 사용하여 repositoryComponent 객체를 생성하고,
DI까지 하고 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UsersActivity extends AppCompatActivity {
 
    RepositoryComponent repositoryComponent;
    @Inject UserDataSource userDataSource;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bank_accounts);
 
        repositoryComponent = DaggerRepositoryComponent.builder()
                .repositoryModule(new RepositoryModule(getApplicationContext())).build();
        repositoryComponent.inject(this);
 
    }
 
}
 
cs

 
 기본적으로 위와 같이 Inject를 받게 되면 
UserDataSource를 통해 Firebase 실시간 데이터베이스를 사용할 수 있습니다.

Dagger2는 이게 전부가 아니며, 아주 기본적인 예제일 뿐입니다.

기본 3개 구성 요소 외에 다양한 어노테이션이 있습니다.
 

댓글

이 블로그의 인기 게시물

[Android] Fragment 위에 Dialog 띄우기

배경:  - Fragment에 Google Maps를 올려 사용자에게 보여주고 있다.  - 사용자가 Dialog 창을 열어 검색을 할 수 있게 하고 싶다. 1. Dialog layout xml 생성 Dialog 창에는 하나의 EditText를 추가하여 사용자로 하여금 String을 입력받게 한다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 < LinearLayout   xmlns:android = "http://schemas.android.com/apk/res/android"         android:orientation = "vertical"         android:layout_width = "wrap_content"         android:layout_height = "wrap_content" >     < LinearLayout                 android:orientation = "horizontal"                 android:layout_width = "match_parent"                 android:layout_height = "wrap_content" >         < TextView                         android:layout_width = "match_parent"                         android:layout_height = "match_parent&quo

[Android] Fragment에 RecyclerView 추가하기

배경:  - List View를 보여주는 Fragment로 전환 RecyclerView  위젯은  ListView 의 업그레이드 버전으로 데이터 양이 많은 경우 스크롤을 효율적으로 수행할 수 있는 위젯이다. refer: http://developer.android.com/intl/ko/training/material/lists-cards.html 위 그림과 같이 Adapter를 통해 데이터에 접근하며,  LayoutManager를 통해 위젯 내부 항목들을 배치한다. 따라서 RecyclerView를 적용하기 위해서는     - LayoutManager     - Adaper 를 지정해주어야 한다. 1. 리스트에 보여질 각 항목 item layout 생성 다음과 같이 "id contents" 로 구성하였다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 < ?xml version = "1.0"  encoding = "utf-8" ? > < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"     android:layout_width = "wrap_content"     android:layout_height = "wrap_content"     android:orientation = "horizontal" >      < TextView         android:id = "@+id/id"         android:layout_width = "wrap_content&

미니 메추리 키우기 - 사육장 만들기

미니 메추리는 우리가 알고 있는 일반 메추리보다 조금 작은 개체입니다. 버튼퀼(버튼퀘일)이라고도 불리죠. 일반 메추리보다 작기도 하고 짝이 맞는 암수가 같이 있으면 그리 시끄럽지도 않습니다. 여러 모로 키우기가 좀더 수월하죠. 첫 번째 단계로 먼저 아이들이 지낼 집을 만들어 주었습니다. 사실 여러 고민을 많이 했어요 지금 소개하는 집을 만들기 전에는 120L 짜리 대형 리빙 박스로 집을 만들어 주었었죠. 값이 저렴하고 개량하는 것이 크게 어렵지 않기 때문에 많은 분들이 리빙 박스를 개조하여 집을 만들어 주고 있어요. 저 같은 경우는 보온을 생각해서 안쪽에는 단열재를 덧대기도 했죠. 하지만 사실 리빙 박스로 집을 만드는게 아주 쉽지만은 않아요. 물론 있는 그대로를 사용하신다면 어려울 건 전혀 없죠. 그런데 만약 전구를 달기 위해 구멍을 뚫거나, 환기 구멍을 뚫거나 기타 여러 필요에 의해 리빙 박스를 뜯어 고쳐야 한다면 이야기가 달라지죠. 저도 사실 이런 불편함에 고민고민을 하다가 오늘 소개해 드릴 두 번째 집과 같은 것을 생각하게 되었어요. 바로 시중에서 쉽게 구할 수 있는 종이 박스를 활용한 것인데요. 위쪽 뚜껑에는 구멍을 두 개를 뚫었어요. 작은 구멍은 온도 조절을 위한 전구 바로 위쪽으로 온도가 너무 올라갈 경우 온도 조절을 위해 뚫어 놓았고요.  아래 좀더 큰 구멍은 물, 먹이 등을 교체해주기 위한 구멍이에요.  정면에는 창을 내어 관찰할 수 있게 했어요. 지금은 저 가운데도 잘라내서 크게 창 하나로 만들었어요. 안쪽에는 온도계를 비치하여 내부 온도를 확인할 수 있게 해두었습니다. (지금 생각해보니 전구 바로 아래쪽에 위치한 탓에 제대로 된 온도 측정이 될지 모르겠네요;;;) 그리고 보셔서 아시겠지만, 내부 바닥, 옆면에 단