提交 5210ba8d 编写于 作者: P Paul Sowden 提交者: Sam Judd

Allow recursively pausing/resuming requests in a given context heirarchy.

上级 d520efbc
......@@ -46,6 +46,7 @@ import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapper;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
import com.bumptech.glide.manager.Lifecycle;
import com.bumptech.glide.manager.RequestManagerTreeNode;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.animation.GlideAnimation;
......@@ -137,7 +138,8 @@ public class GlideTest {
Glide.get(getContext()).register(GlideUrl.class, InputStream.class, mockUrlLoaderFactory);
Lifecycle lifecycle = mock(Lifecycle.class);
requestManager = new RequestManager(getContext(), lifecycle);
RequestManagerTreeNode treeNode = mock(RequestManagerTreeNode.class);
requestManager = new RequestManager(getContext(), lifecycle, treeNode);
requestManager.resumeRequests();
}
......
......@@ -24,6 +24,7 @@ import com.bumptech.glide.manager.ConnectivityMonitor;
import com.bumptech.glide.manager.ConnectivityMonitor.ConnectivityListener;
import com.bumptech.glide.manager.ConnectivityMonitorFactory;
import com.bumptech.glide.manager.Lifecycle;
import com.bumptech.glide.manager.RequestManagerTreeNode;
import com.bumptech.glide.manager.RequestTracker;
import com.bumptech.glide.tests.BackgroundUtil;
import com.bumptech.glide.tests.GlideShadowLooper;
......@@ -50,6 +51,7 @@ public class RequestManagerTest {
private ConnectivityListener connectivityListener;
private RequestManager.DefaultOptions options;
private Lifecycle lifecycle = mock(Lifecycle.class);
private RequestManagerTreeNode treeNode = mock(RequestManagerTreeNode.class);
@Before
public void setUp() {
......@@ -64,7 +66,8 @@ public class RequestManagerTest {
}
});
requestTracker = mock(RequestTracker.class);
manager = new RequestManager(Robolectric.application, lifecycle, requestTracker, factory);
manager =
new RequestManager(Robolectric.application, lifecycle, treeNode, requestTracker, factory);
options = mock(RequestManager.DefaultOptions.class);
manager.setDefaultOptions(options);
}
......
......@@ -65,7 +65,7 @@ public class RequestManagerRetrieverTest {
harness.doGet();
Robolectric.shadowOf(Looper.getMainLooper()).runToEndOfTasks();
assertTrue(harness.hasFragmentWithTag(RequestManagerRetriever.TAG));
assertTrue(harness.hasFragmentWithTag(RequestManagerRetriever.FRAGMENT_TAG));
}
}
......@@ -81,7 +81,7 @@ public class RequestManagerRetrieverTest {
for (RetrieverHarness harness : harnesses) {
RequestManager requestManager = mock(RequestManager.class);
harness.addFragmentWithTag(RequestManagerRetriever.TAG, requestManager);
harness.addFragmentWithTag(RequestManagerRetriever.FRAGMENT_TAG, requestManager);
assertEquals(requestManager, harness.doGet());
}
......@@ -90,7 +90,7 @@ public class RequestManagerRetrieverTest {
@Test
public void testReturnsNewRequestManagerIfFragmentExistsButHasNoRequestManager() {
for (RetrieverHarness harness : harnesses) {
harness.addFragmentWithTag(RequestManagerRetriever.TAG, null);
harness.addFragmentWithTag(RequestManagerRetriever.FRAGMENT_TAG, null);
assertNotNull(harness.doGet());
}
......@@ -99,7 +99,7 @@ public class RequestManagerRetrieverTest {
@Test
public void testSavesNewRequestManagerToFragmentIfCreatesRequestManagerForExistingFragment() {
for (RetrieverHarness harness : harnesses) {
harness.addFragmentWithTag(RequestManagerRetriever.TAG, null);
harness.addFragmentWithTag(RequestManagerRetriever.FRAGMENT_TAG, null);
RequestManager first = harness.doGet();
RequestManager second = harness.doGet();
......@@ -109,7 +109,7 @@ public class RequestManagerRetrieverTest {
@Test
public void testHasValidTag() {
assertEquals(RequestManagerRetriever.class.getPackage().getName(), RequestManagerRetriever.TAG);
assertEquals(RequestManagerRetriever.class.getPackage().getName(), RequestManagerRetriever.FRAGMENT_TAG);
}
@Test
......@@ -396,7 +396,9 @@ public class RequestManagerRetrieverTest {
@Override
public boolean hasFragmentWithTag(String tag) {
return controller.get().getFragmentManager().findFragmentByTag(RequestManagerRetriever.TAG) != null;
return null != controller.get()
.getFragmentManager()
.findFragmentByTag(RequestManagerRetriever.FRAGMENT_TAG);
}
@Override
......@@ -405,7 +407,7 @@ public class RequestManagerRetrieverTest {
fragment.setRequestManager(requestManager);
controller.get().getFragmentManager()
.beginTransaction()
.add(fragment, RequestManagerRetriever.TAG)
.add(fragment, RequestManagerRetriever.FRAGMENT_TAG)
.commitAllowingStateLoss();
controller.get().getFragmentManager().executePendingTransactions();
}
......@@ -440,7 +442,7 @@ public class RequestManagerRetrieverTest {
@Override
public boolean hasFragmentWithTag(String tag) {
return controller.get().getSupportFragmentManager().findFragmentByTag(RequestManagerRetriever.TAG)
return controller.get().getSupportFragmentManager().findFragmentByTag(RequestManagerRetriever.FRAGMENT_TAG)
!= null;
}
......@@ -450,7 +452,7 @@ public class RequestManagerRetrieverTest {
fragment.setRequestManager(manager);
controller.get().getSupportFragmentManager()
.beginTransaction()
.add(fragment, RequestManagerRetriever.TAG)
.add(fragment, RequestManagerRetriever.FRAGMENT_TAG)
.commitAllowingStateLoss();
controller.get().getSupportFragmentManager().executePendingTransactions();
}
......
......@@ -17,6 +17,7 @@ import com.bumptech.glide.manager.ConnectivityMonitor;
import com.bumptech.glide.manager.ConnectivityMonitorFactory;
import com.bumptech.glide.manager.Lifecycle;
import com.bumptech.glide.manager.LifecycleListener;
import com.bumptech.glide.manager.RequestManagerTreeNode;
import com.bumptech.glide.manager.RequestTracker;
import com.bumptech.glide.signature.ApplicationVersionSignature;
import com.bumptech.glide.signature.MediaStoreSignature;
......@@ -42,19 +43,21 @@ import java.util.UUID;
public class RequestManager implements LifecycleListener {
private final Context context;
private final Lifecycle lifecycle;
private final RequestManagerTreeNode treeNode;
private final RequestTracker requestTracker;
private final Glide glide;
private final OptionsApplier optionsApplier;
private DefaultOptions options;
public RequestManager(Context context, Lifecycle lifecycle) {
this(context, lifecycle, new RequestTracker(), new ConnectivityMonitorFactory());
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
}
RequestManager(Context context, final Lifecycle lifecycle, RequestTracker requestTracker,
ConnectivityMonitorFactory factory) {
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.glide = Glide.get(context);
this.optionsApplier = new OptionsApplier();
......@@ -145,6 +148,27 @@ public class RequestManager implements LifecycleListener {
requestTracker.pauseRequests();
}
/**
* Performs {@link #pauseRequests()} recursively for all managers that are contextually descendant
* to this manager based on the Activity/Fragment hierarchy:
*
* <ul>
* <li>When pausing on an Activity all attached fragments will also get paused.
* <li>When pausing on an attached Fragment all descendant fragments will also get paused.
* <li>When pausing on a detached Fragment or the application context only the current RequestManager is paused.
* </ul>
*
* <p>Note, on pre-Jelly Bean MR1 calling pause on a Fragment will not cause child fragments to pause, in this
* case either call pause on the Activity or use a support Fragment.
*/
public void pauseRequestsRecursive() {
Util.assertMainThread();
pauseRequests();
for (RequestManager requestManager : treeNode.getDescendants()) {
requestManager.pauseRequests();
}
}
/**
* Restarts any loads that have not yet completed.
*
......@@ -156,6 +180,19 @@ public class RequestManager implements LifecycleListener {
requestTracker.resumeRequests();
}
/**
* Performs {@link #resumeRequests()} recursively for all managers that are contextually descendant
* to this manager based on the Activity/Fragment hierarchy. The hierarchical semantics are identical as for
* {@link #pauseRequestsRecursive()}.
*/
public void resumeRequestsRecursive() {
Util.assertMainThread();
resumeRequests();
for (RequestManager requestManager : treeNode.getDescendants()) {
requestManager.resumeRequests();
}
}
/**
* Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
* permission is present) and restarts failed or paused requests.
......
package com.bumptech.glide.manager;
import com.bumptech.glide.RequestManager;
import java.util.Collections;
import java.util.Set;
/**
* A {@link RequestManagerTreeNode} that returns no relatives.
*/
final class EmptyRequestManagerTreeNode implements RequestManagerTreeNode {
@Override
public Set<RequestManager> getDescendants() {
return Collections.emptySet();
}
}
......@@ -2,11 +2,16 @@ package com.bumptech.glide.manager;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.os.Build;
import com.bumptech.glide.RequestManager;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* A view-less {@link android.app.Fragment} used to safely store an {@link com.bumptech.glide.RequestManager} that
* can be used to start, stop and manage Glide requests started for targets the fragment or activity this fragment is a
......@@ -19,7 +24,11 @@ import com.bumptech.glide.RequestManager;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class RequestManagerFragment extends Fragment {
private final ActivityFragmentLifecycle lifecycle;
private final RequestManagerTreeNode requestManagerTreeNode = new FragmentRequestManagerTreeNode();
private RequestManager requestManager;
private final HashSet<RequestManagerFragment> childRequestManagerFragments
= new HashSet<RequestManagerFragment>();
private RequestManagerFragment rootRequestManagerFragment;
public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
......@@ -51,6 +60,76 @@ public class RequestManagerFragment extends Fragment {
return requestManager;
}
public RequestManagerTreeNode getRequestManagerTreeNode() {
return requestManagerTreeNode;
}
private void addChildRequestManagerFragment(RequestManagerFragment child) {
childRequestManagerFragments.add(child);
}
private void removeChildRequestManagerFragment(RequestManagerFragment child) {
childRequestManagerFragments.remove(child);
}
/**
* Returns the set of fragments that this RequestManagerFragment's parent is a parent to. (i.e. our parent is
* the fragment that we are annotating).
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public Set<RequestManagerFragment> getDescendantRequestManagerFragments() {
if (rootRequestManagerFragment == this) {
return Collections.unmodifiableSet(childRequestManagerFragments);
} else if (rootRequestManagerFragment == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Pre JB MR1 doesn't allow us to get the parent fragment so we can't introspect hierarchy, so just
// return an empty set.
return Collections.emptySet();
} else {
HashSet<RequestManagerFragment> descendants = new HashSet<RequestManagerFragment>();
for (RequestManagerFragment fragment
: rootRequestManagerFragment.getDescendantRequestManagerFragments()) {
if (isDescendant(fragment.getParentFragment())) {
descendants.add(fragment);
}
}
return Collections.unmodifiableSet(descendants);
}
}
/**
* Returns true if the fragment is a descendant of our parent.
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private boolean isDescendant(Fragment fragment) {
Fragment root = this.getParentFragment();
while (fragment.getParentFragment() != null) {
if (fragment.getParentFragment() == root) {
return true;
}
fragment = fragment.getParentFragment();
}
return false;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
rootRequestManagerFragment = RequestManagerRetriever.get()
.getRequestManagerFragment(getActivity().getFragmentManager());
if (rootRequestManagerFragment != this) {
rootRequestManagerFragment.addChildRequestManagerFragment(this);
}
}
@Override
public void onDetach() {
super.onDetach();
if (rootRequestManagerFragment != null) {
rootRequestManagerFragment.removeChildRequestManagerFragment(this);
rootRequestManagerFragment = null;
}
}
@Override
public void onStart() {
super.onStart();
......@@ -86,4 +165,19 @@ public class RequestManagerFragment extends Fragment {
requestManager.onLowMemory();
}
}
private class FragmentRequestManagerTreeNode implements RequestManagerTreeNode {
@Override
public Set<RequestManager> getDescendants() {
Set<RequestManagerFragment> descendantFragments = getDescendantRequestManagerFragments();
HashSet<RequestManager> descendants =
new HashSet<RequestManager>(descendantFragments.size());
for (RequestManagerFragment fragment : descendantFragments) {
if (fragment.getRequestManager() != null) {
descendants.add(fragment.getRequestManager());
}
}
return descendants;
}
}
}
......@@ -25,7 +25,8 @@ import java.util.Map;
* ones from activities and fragment.
*/
public class RequestManagerRetriever implements Handler.Callback {
static final String TAG = "com.bumptech.glide.manager";
private static final String TAG = "RMRetriever";
static final String FRAGMENT_TAG = "com.bumptech.glide.manager";
/** The singleton instance of RequestManagerRetriever. */
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
......@@ -70,7 +71,7 @@ public class RequestManagerRetriever implements Handler.Callback {
// However, in this case since the manager attached to the application will not receive lifecycle
// events, we must force the manager to start resumed using ApplicationLifecycle.
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle());
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
......@@ -147,41 +148,54 @@ public class RequestManagerRetriever implements Handler.Callback {
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(TAG);
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle());
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
RequestManager supportFragmentGet(Context context, final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(TAG);
SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(
FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle());
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
......
package com.bumptech.glide.manager;
import com.bumptech.glide.RequestManager;
import java.util.Set;
/**
* Provides access to the relatives of a RequestManager based on the current context. The context hierarchy
* is provided by nesting in Activity and Fragments; the application context does not provide access to
* any other RequestManagers hierarchically.
*/
public interface RequestManagerTreeNode {
/**
* Returns all descendant {@link RequestManager}s relative to the context of the current {@link RequestManager}.
*/
Set<RequestManager> getDescendants();
}
package com.bumptech.glide.manager;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.support.v4.app.Fragment;
import com.bumptech.glide.RequestManager;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* A view-less {@link android.support.v4.app.Fragment} used to safely store an
* {@link com.bumptech.glide.RequestManager} that can be used to start, stop and manage Glide requests started for
......@@ -17,6 +22,11 @@ import com.bumptech.glide.RequestManager;
public class SupportRequestManagerFragment extends Fragment {
private RequestManager requestManager;
private final ActivityFragmentLifecycle lifecycle;
private final RequestManagerTreeNode requestManagerTreeNode =
new SupportFragmentRequestManagerTreeNode();
private final HashSet<SupportRequestManagerFragment> childRequestManagerFragments =
new HashSet<SupportRequestManagerFragment>();
private SupportRequestManagerFragment rootRequestManagerFragment;
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
......@@ -48,6 +58,77 @@ public class SupportRequestManagerFragment extends Fragment {
return requestManager;
}
/**
* Returns the {@link RequestManagerTreeNode} that provides tree traversal methods relative to the associated
* {@link RequestManager}.
*/
public RequestManagerTreeNode getRequestManagerTreeNode() {
return requestManagerTreeNode;
}
private void addChildRequestManagerFragment(SupportRequestManagerFragment child) {
childRequestManagerFragments.add(child);
}
private void removeChildRequestManagerFragment(SupportRequestManagerFragment child) {
childRequestManagerFragments.remove(child);
}
/**
* Returns the set of fragments that this RequestManagerFragment's parent is a parent to. (i.e. our parent is
* the fragment that we are annotating).
*/
public Set<SupportRequestManagerFragment> getDescendantRequestManagerFragments() {
if (rootRequestManagerFragment == null) {
return Collections.emptySet();
} else if (rootRequestManagerFragment == this) {
return Collections.unmodifiableSet(childRequestManagerFragments);
} else {
HashSet<SupportRequestManagerFragment> descendants =
new HashSet<SupportRequestManagerFragment>();
for (SupportRequestManagerFragment fragment
: rootRequestManagerFragment.getDescendantRequestManagerFragments()) {
if (isDescendant(fragment.getParentFragment())) {
descendants.add(fragment);
}
}
return Collections.unmodifiableSet(descendants);
}
}
/**
* Returns true if the fragment is a descendant of our parent.
*/
private boolean isDescendant(Fragment fragment) {
Fragment root = this.getParentFragment();
while (fragment.getParentFragment() != null) {
if (fragment.getParentFragment() == root) {
return true;
}
fragment = fragment.getParentFragment();
}
return false;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
rootRequestManagerFragment = RequestManagerRetriever.get()
.getSupportRequestManagerFragment(getActivity().getSupportFragmentManager());
if (rootRequestManagerFragment != this) {
rootRequestManagerFragment.addChildRequestManagerFragment(this);
}
}
@Override
public void onDetach() {
super.onDetach();
if (rootRequestManagerFragment != null) {
rootRequestManagerFragment.removeChildRequestManagerFragment(this);
rootRequestManagerFragment = null;
}
}
@Override
public void onStart() {
super.onStart();
......@@ -75,4 +156,18 @@ public class SupportRequestManagerFragment extends Fragment {
requestManager.onLowMemory();
}
}
private class SupportFragmentRequestManagerTreeNode implements RequestManagerTreeNode {
@Override
public Set<RequestManager> getDescendants() {
Set<SupportRequestManagerFragment> descendantFragments = getDescendantRequestManagerFragments();
HashSet<RequestManager> descendants = new HashSet<RequestManager>(descendantFragments.size());
for (SupportRequestManagerFragment fragment : descendantFragments) {
if (fragment.getRequestManager() != null) {
descendants.add(fragment.getRequestManager());
}
}
return descendants;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册