1 package com.bumptech.glide.manager; 2 3 import android.annotation.TargetApi; 4 import android.app.Activity; 5 import android.app.Application; 6 import android.content.Context; 7 import android.content.ContextWrapper; 8 import android.os.Build; 9 import android.os.Handler; 10 import android.os.Looper; 11 import android.os.Message; 12 import android.support.v4.app.Fragment; 13 import android.support.v4.app.FragmentActivity; 14 import android.support.v4.app.FragmentManager; 15 import android.util.Log; 16 17 import com.bumptech.glide.RequestManager; 18 import com.bumptech.glide.util.Util; 19 20 import java.util.HashMap; 21 import java.util.Map; 22 23 /** 24 * A collection of static methods for creating new {@link com.bumptech.glide.RequestManager}s or retrieving existing 25 * ones from activities and fragment. 26 */ 27 public class RequestManagerRetriever implements Handler.Callback { 28 static final String TAG = "com.bumptech.glide.manager"; 29 30 /** The singleton instance of RequestManagerRetriever. */ 31 private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever(); 32 33 private static final int ID_REMOVE_FRAGMENT_MANAGER = 1; 34 private static final int ID_REMOVE_SUPPORT_FRAGMENT_MANAGER = 2; 35 36 /** The top application level RequestManager. */ 37 private volatile RequestManager applicationManager; 38 39 // Visible for testing. 40 /** Pending adds for RequestManagerFragments. */ 41 final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments = 42 new HashMap<android.app.FragmentManager, RequestManagerFragment>(); 43 44 // Visible for testing. 45 /** Pending adds for SupportRequestManagerFragments. */ 46 final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = 47 new HashMap<FragmentManager, SupportRequestManagerFragment>(); 48 49 /** Main thread handler to handle cleaning up pending fragment maps. */ 50 private final Handler handler; 51 52 /** 53 * Retrieves and returns the RequestManagerRetriever singleton. 54 */ get()55 public static RequestManagerRetriever get() { 56 return INSTANCE; 57 } 58 59 // Visible for testing. RequestManagerRetriever()60 RequestManagerRetriever() { 61 handler = new Handler(Looper.getMainLooper(), this /* Callback */); 62 } 63 getApplicationManager(Context context)64 private RequestManager getApplicationManager(Context context) { 65 // Either an application context or we're on a background thread. 66 if (applicationManager == null) { 67 synchronized (this) { 68 if (applicationManager == null) { 69 // Normally pause/resume is taken care of by the fragment we add to the fragment or activity. 70 // However, in this case since the manager attached to the application will not receive lifecycle 71 // events, we must force the manager to start resumed using ApplicationLifecycle. 72 applicationManager = new RequestManager(context.getApplicationContext(), 73 new ApplicationLifecycle()); 74 } 75 } 76 } 77 78 return applicationManager; 79 } 80 get(Context context)81 public RequestManager get(Context context) { 82 if (context == null) { 83 throw new IllegalArgumentException("You cannot start a load on a null Context"); 84 } else if (Util.isOnMainThread() && !(context instanceof Application)) { 85 if (context instanceof FragmentActivity) { 86 return get((FragmentActivity) context); 87 } else if (context instanceof Activity) { 88 return get((Activity) context); 89 } else if (context instanceof ContextWrapper) { 90 return get(((ContextWrapper) context).getBaseContext()); 91 } 92 } 93 94 return getApplicationManager(context); 95 } 96 get(FragmentActivity activity)97 public RequestManager get(FragmentActivity activity) { 98 if (Util.isOnBackgroundThread()) { 99 return get(activity.getApplicationContext()); 100 } else { 101 assertNotDestroyed(activity); 102 FragmentManager fm = activity.getSupportFragmentManager(); 103 return supportFragmentGet(activity, fm); 104 } 105 } 106 get(Fragment fragment)107 public RequestManager get(Fragment fragment) { 108 if (fragment.getActivity() == null) { 109 throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); 110 } 111 if (Util.isOnBackgroundThread()) { 112 return get(fragment.getActivity().getApplicationContext()); 113 } else { 114 if (fragment.isDetached()) { 115 throw new IllegalArgumentException("You cannot start a load on a detached fragment"); 116 } 117 FragmentManager fm = fragment.getChildFragmentManager(); 118 return supportFragmentGet(fragment.getActivity(), fm); 119 } 120 } 121 122 @TargetApi(Build.VERSION_CODES.HONEYCOMB) get(Activity activity)123 public RequestManager get(Activity activity) { 124 if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 125 return get(activity.getApplicationContext()); 126 } else { 127 assertNotDestroyed(activity); 128 android.app.FragmentManager fm = activity.getFragmentManager(); 129 return fragmentGet(activity, fm); 130 } 131 } 132 133 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) assertNotDestroyed(Activity activity)134 private static void assertNotDestroyed(Activity activity) { 135 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) { 136 throw new IllegalArgumentException("You cannot start a load for a destroyed activity"); 137 } 138 } 139 140 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) get(android.app.Fragment fragment)141 public RequestManager get(android.app.Fragment fragment) { 142 if (fragment.getActivity() == null) { 143 throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); 144 } 145 if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 146 return get(fragment.getActivity().getApplicationContext()); 147 } else { 148 assertNotDetached(fragment); 149 android.app.FragmentManager fm = fragment.getChildFragmentManager(); 150 return fragmentGet(fragment.getActivity(), fm); 151 } 152 } 153 154 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) assertNotDetached(android.app.Fragment fragment)155 private static void assertNotDetached(android.app.Fragment fragment) { 156 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2 && fragment.isDetached()) { 157 throw new IllegalArgumentException("You cannot start a load on a detached fragment"); 158 } 159 } 160 161 @TargetApi(Build.VERSION_CODES.HONEYCOMB) fragmentGet(Context context, final android.app.FragmentManager fm)162 RequestManager fragmentGet(Context context, final android.app.FragmentManager fm) { 163 RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(TAG); 164 if (current == null) { 165 current = pendingRequestManagerFragments.get(fm); 166 if (current == null) { 167 current = new RequestManagerFragment(); 168 pendingRequestManagerFragments.put(fm, current); 169 fm.beginTransaction().add(current, TAG).commitAllowingStateLoss(); 170 handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); 171 } 172 } 173 RequestManager requestManager = current.getRequestManager(); 174 if (requestManager == null) { 175 requestManager = new RequestManager(context, current.getGlideLifecycle()); 176 current.setRequestManager(requestManager); 177 } 178 return requestManager; 179 180 } 181 supportFragmentGet(Context context, final FragmentManager fm)182 RequestManager supportFragmentGet(Context context, final FragmentManager fm) { 183 SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(TAG); 184 if (current == null) { 185 current = pendingSupportRequestManagerFragments.get(fm); 186 if (current == null) { 187 current = new SupportRequestManagerFragment(); 188 pendingSupportRequestManagerFragments.put(fm, current); 189 fm.beginTransaction().add(current, TAG).commitAllowingStateLoss(); 190 handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget(); 191 } 192 } 193 RequestManager requestManager = current.getRequestManager(); 194 if (requestManager == null) { 195 requestManager = new RequestManager(context, current.getGlideLifecycle()); 196 current.setRequestManager(requestManager); 197 } 198 return requestManager; 199 } 200 201 @Override handleMessage(Message message)202 public boolean handleMessage(Message message) { 203 boolean handled = true; 204 Object removed = null; 205 Object key = null; 206 switch (message.what) { 207 case ID_REMOVE_FRAGMENT_MANAGER: 208 android.app.FragmentManager fm = (android.app.FragmentManager) message.obj; 209 key = fm; 210 removed = pendingRequestManagerFragments.remove(fm); 211 break; 212 case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER: 213 FragmentManager supportFm = (FragmentManager) message.obj; 214 key = supportFm; 215 removed = pendingSupportRequestManagerFragments.remove(supportFm); 216 break; 217 default: 218 handled = false; 219 } 220 if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) { 221 Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key); 222 } 223 return handled; 224 } 225 } 226