본문 바로가기

Android

[Android] Fragment에서 Activity 접근하기

안드로이드 개발을 하다보면 프래그먼트에서 액티비티를 접근해야 할 때가 있습니다.

이번 포스트에서는 어떻게 하면 프래그먼트에서 액티비티에 접근할 수 있는지를 알아보겠습니다.

 

Bad:  Static Context 사용

필자는 2020년 초에 프로그래머스에서 주관한 라인플러스 앱개발 챌린지에 참여한 적이 있습니다.

당시에 주어진 과제는 이미지 첨부가 가능한 노트앱을 만드는 것이었습니다.

MainActivity를 노트 리스트로 만들고, 노트작성 화면을 또 다른 액티비티의 프래그먼트로 만들었습니다.

유저가 새노트를 작성 후 자동적으로 노트 리스트로 돌아오는데 유저는 업데이트된 리스트를 보아야 합니다.

MainActivity에 있는 리스트 Adapter에 접근하기 위해 제가 사용한 방법은 아래 코드와 같이 MainActivity의 Context를 Static으로 선언하고, 어디서나 접근하는 방법이었습니다.

당시 구글링 했을 때 많은 블로그들이 추천한 방법이었죠.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MainActivity
public class MainActivity extends AppCompatActivity {
    public static Context mContext;
    // ...
}
 
 
// 기타 Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    // ...
    ((MainActivity)MainActivity.mContext).adapter.addListItem(...);
    // ...
}
cs

(Memory Leak를 유발하는 안 좋은 방식)

 

해당 앱개발 챌린지에서 좋은 결과를 받기를 기대하며 제출했지만, Context를 Static으로 사용하는 방식은 Memory Leak(메모리 누수)를 유발하는 안 좋은 방식이라는 피드백을 받았습니다.

 

당시에 제 나름대로 보완한 방법은 Fragment 생성자로 Activity의 Context를 받아서 Activity의 멤버나 메서드에 접근하는 것이었습니다.

하지만 이 또한 Context를 사용하는 데 있어서 형변환이 필요해 불편하고, Activity의 멤버를 직접 참조하여 사용한다는 면에서 그렇게 좋은 방식은 아닙니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 기타 Fragment
public class SomeFragment extends Fragment {
    Context mainContext;
 
    public SomeFragment (Context mainContext) {
        this.mainContext = mainContext;
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        // ...
        ((MainActivity)mainContext).adapter.addListItem(...);
        // ...
    }
}
cs

(Fragment 생성자에서 Activity의 Context를 받는 방식)

 

Good:  Owner 사용

실무를 하며 Fragment에서 Activity의 메서드를 사용하는 방식을 배울 수 있었습니다. 바로 Owner를 사용하는 방식입니다. 물론 이것이 정식적인 용어인지는 모르겠습니다만, Fragment의 부모격이 되는 Activity를 Owner라고 이해하시면 됩니다.

 

사용 방법은 간단합니다. Fragment에 Owner라는 Interface를 정의하고, 그 안에 Activity의 메서드 중 Fragment에서 사용할 메서드들을 정의해두는 것이죠. 그리고 해당 Interface를 Activity에서 Implement하여 사용하면 됩니다. 코드는 아래와 같습니다.

1
2
3
4
5
6
7
public class SomeFragment extends Fragment {
    // ...
 
    public interface Owner() {
        void methodA();
    }
}
cs

 

methodA는 Activity에 정의되고, Fragment에서 사용하고 싶은 메서드입니다. 기존에 Fragment에서 Activity의 Context를 통해 직접 Activity의 멤버를 참조한 코드를 간단하게 methodA();로 대체합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SomeFragment extends Fragment {
    // ...
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        // ...
        this.methodA(); // ((MainActivity)MainActivity.mContext).adapter.addListItem(...);를 대체
        // ...
    }
 
    interface Owner() {
        void methodA();
    }
}
cs

 

그리고 나서 Activity가 Owner 인터페이스를 Implement하게 하고, methodA를 Override하며, 직접 참조했던 코드를 Activity의 메서드 methodA 안으로 옮겨줍니다.

1
2
3
4
5
6
7
8
9
10
public class MainActivity extends AppCompatActivity implements SomeFragment.Onwer {
    private Adapter adapter;
    
    @Override
    public void methodA() {
        // ...
        this.adapter.addListItem(...);
        // ...
    }
}
cs

 

Onwer 방법의 장점은 Activity의 코드와 Fragment의 코드를 분리할 수 있다는 것입니다. Fragment에서 Activity의 멤버를 직접 참조하지 않아도 되어 의존도를 낮출 수 있습니다. 또한 멤버에 직접 참조하지 않기 때문에 멤버변수를 private으로 사용할 수도 있겠죠. 여하튼 앱개발 챌린지에 참여했던 학생시절 고민이 많았던 주제였기 때문에 잘 사용하셨으면 좋겠습니다.