Glide 의 Activity 생명주기 콜백 처리

tabby platfarm
mojitok
Published in
18 min readJun 26, 2020

안드로이드 앱을 만들 때 Glide를 사용하면 서버에서 이미지를 내려받고 이미지 뷰에 표시하는 기능을 다음과 같이 간단한 코드로 만들 수 있다.

Glide.with([Activity 또는 Fragment]).load([이미지 주소]).into([이미지 뷰])

Glide는 백그라운드 스레드에서 이미지 파일을 내려받고 디코딩하여 이미지 뷰에 drawable을 설정한다. 내려받은 이미지 파일을 메모리나 디스크 캐시에 자동으로 저장 하기도 한다.

만약 내려받기 중에 Activity가 멈춘다면 Glide가 생성한 백그라운드 스레드는 중지되어야 하고 이미지 뷰에 표시 하려는 것을 멈추어야 한다. 그리고 다시 Activity가 시작되면 내려받기를 다시 시작한다. 이러한 동작들은 개발자들이 코딩할 필요없이 Glide 내부에서 처리된다.

Glide는 어떻게 Activity의 생명 주기(Life Cycle) 콜백이 호출된 것을 알 수 있을까?

먼저 안드로이드에서 Activity와 Fragment 사이의 생명 주기 콜백 실행 흐름을 살펴보자

https://medium.com/androiddevelopers/the-android-lifecycle-cheat-sheet-part-iii-fragments-afc87d4f37fd

Activity에 Fragment를 추가하면 Activity 생명 주기 콜백과 일치하는 Fragment 생명 주기 콜백이 호출되는 것을 볼 수 있다.

Glide는 RequestManagerFragment라는 UI가 없는 Fragment를 Activity에 추가하여 Activity의 생명주기 콜백 이벤트를 RequestManager에 전달한다.

Glide의 RequestManager는 Request와 Target의 시작과 종료, 제거를 관리한다. RequestManagerFragment가 어떻게 Activity의 생명주기 콜백을 RequestManager에게 이벤트로 전달하는지 알아보자.

우선 RequestManagerFragment가 어떻게 만들어지는지 with(activity) 코드를 따라가 보자. Context, Fragment, FragmentActivity, View 도 with의 인자로 전달할 수 있다. 이 글에서는 activity를 인자로 전달할 때의 코드를 따라 가본다.

백그라운드 스레드에서 get(activity)가 호출되었다면 RequestManagerFragment를 생성하지 않기 때문에 Activity의생명 주기 콜백 이벤트를 받을 수 없다. 이때는 어플리케이션의 생명주기 콜백 이벤트를 받는데 onStart() 이벤트만 받을 수 있다.

public static RequestManager with(Activity activity) {
return getRetriever(activity)
.get(activity);
}
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm,
/*parentHint=*/ null,
isActivityVisible(activity));

}
}

RequestManager를 생성하기 전에 Activity의 FragmentManager에서 현재RequestManagerFragment를 찾고 설정된 RequestManager가 있는지 확인한다. 없다면 RequestManagerFactory로 RequestManager를 새로 만든다.

private RequestManager fragmentGet(
Context context,
FragmentManager fm,
Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);

requestManager =
factory.build(
glide,
current.getGlideLifecycle(),
current.getRequestManagerTreeNode(),
context);
current.setRequestManager(requestManager);
}

return requestManager;
}

Activity의 FragmentManager 안에서 FRAGMENT_TAG 태그이름으로 이미 추가된 RequestManagerFragment를 찾는다.
없으면 RequestManagerFragment가 추가 중 인지 pendingRequestManagerFragments 해시맵 에서 찾는다.
pendingRequestManagerFragments 해시맵 에도 없다면 RequestManagerFragment를 새로 생성한 후에 commitAllowingStateLoss을 이용하여 Activity에 추가한다. 상태를 저장할 필요가 없기 때문이다.

RequestManagerFragment getRequestManagerFragment(Activity activity) {
return getRequestManagerFragment(
activity.getFragmentManager(),
/*parentHint=*/ null,
isActivityVisible(activity));
}

@SuppressWarnings("deprecation")
@NonNull
private RequestManagerFragment getRequestManagerFragment(
final FragmentManager fm,
Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current
= (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction()
.add(current, FRAGMENT_TAG)
.commitAllowingStateLoss();

handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm)
.sendToTarget();
}
}
return current;
}

RequestManager를 RequestManagerFactory로 생성할 때RequestManagerFragment의 Lifecycle을 인자로 전달한다.

private static final RequestManagerFactory DEFAULT_FACTORY =
new RequestManagerFactory() {
@NonNull
@Override
public RequestManager build(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode requestManagerTreeNode,
Context context) {
return new RequestManager(
glide,
lifecycle,
requestManagerTreeNode,
context);
}

RequestManager는 LifecycleListener 인터페이스의 onStart, onStop, onDestroy를 구현한다. Request, Target의 시작, 중지, 제거를 위한 인터페이스이다.

public interface LifecycleListener {
void onStart();
void onStop();
void onDestroy();

}

RequestManager는 생성자에서 받은 매개변수 lifecycle의 Listener로 자신을 추가한다.

이제 RequestManager는 Activity의 생명주기 콜백 이벤트를 받을 수 있다. Activity의 onStart, onStop, onDestroy호출될 때 Listener로 추가된 RequestManager의 onStart, onStop, onDestroy가 호출 될 수 있다.

RequestManager가 Activity의 onDestroy 이벤트를 받는다면 Target들과 Request들을 제거하고 추가했던 Listener들에서 자신을 제거할 것이다.

public class RequestManager implements LifecycleListener, ... {
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;

connectivityMonitor =
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));

if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);

defaultRequestListeners =
new CopyOnWriteArrayList<>
(glide.getGlideContext().getDefaultRequestListeners());
setRequestOptions(glide.getGlideContext()
.getDefaultRequestOptions());

glide.registerRequestManager(this);
}

@Override
public synchronized void onStart() {
//Initiate request
resumeRequests();
//View's Target callback
targetTracker.onStart();
}

@Override
public synchronized void onStop() {
//Pause request
pauseRequests();
//View's Target callback
targetTracker.onStop();
}

@Override
public synchronized void onDestroy() {
////Destruction request
targetTracker.onDestroy();
//View's Target callback
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
//Clear callback information
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
}

RequestManagerFragment는 onStart에서 lifecycle.onStart(), onStop에서 lifecycle.onStop(), onDestroy에서 lifecycle.onDestroy()를호출한다.

이런 방식으로 lifecycle에 추가된 Listener들에게 Activity의 이벤트가 전달된다.

public class RequestManagerFragment extends Fragment {
private static final String TAG = "RMFragment";
private final ActivityFragmentLifecycle lifecycle;
private final RequestManagerTreeNode requestManagerTreeNode =
new FragmentRequestManagerTreeNode();

private final Set<RequestManagerFragment>
childRequestManagerFragments = new HashSet<>();

private RequestManager requestManager;
private RequestManagerFragment rootRequestManagerFragment;

private Fragment parentFragmentHint;

public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}

RequestManagerFragment(ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}


public void setRequestManager(RequestManager requestManager) {
this.requestManager = requestManager;
}
... ActivityFragmentLifecycle getGlideLifecycle() {
return lifecycle;
}

public RequestManager getRequestManager() {
return requestManager;
}

@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}

@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}

@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}

...
}

ActivityFragmentLifecycle은 LifecycleListener 들을 해시맵에 추가한 뒤에현재 생명 주기 상태(isStarted, isDestroyed)에 해당하는 LifecycleListener의 콜백을 호출한다.

RequestManagerFragment가 ActivityFragmentLifecycle의 onStart, onStop, onDestroy를 호출할 것이다.

class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners =
Collections.newSetFromMap(
new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;

//Registered monitoring
@Override
public void addListener(LifecycleListener listener) {
lifecycleListeners.add(listener);

if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}

//Remove listening
@Override
public void removeListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}

void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener :
Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}

void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener :
Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}

void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener :
Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
}

여기까지 Glide가 Activity에 UI가 없는 Fragment를 추가하고 Fragment에서 생명주기 콜백을 LifecycleListener들에게 이벤트로 전달하는 과정을 살펴 보았다.

[1]: Jose Alcérreca (Dec 5, 2017) Part III: Fragments — activity and fragment lifecycle

https://medium.com/androiddevelopers/the-android-lifecycle-cheat-sheet-part-iii-fragments-afc87d4f37fd

[2]: Analysis of Glide binding life cycle and callback monitoring principle

https://programmer.help/blogs/analysis-of-glide-binding-life-cycle-and-callback-monitoring-principle.html

--

--