Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2147b2aa5e | |||
| 19418617dd | |||
| e1924bf8a7 | |||
| 6d3faaebe3 | |||
| 17639129b9 | |||
| 1c809095ec | |||
| 3bc563de38 | |||
| 7beb94f8cc |
@@ -4,8 +4,8 @@
|
||||
|
||||
A small, yet full-featured framework that allows building View-based Android applications. Conductor provides a light-weight wrapper around standard Android Views that does just about everything you'd want:
|
||||
|
||||
| Conductor
|
||||
------|------------------------------
|
||||
| | Conductor |
|
||||
|-----------|-------------|
|
||||
:tada: | Easy integration
|
||||
:point_up: | Single Activity apps without using Fragments
|
||||
:recycle: | Simple but powerful lifecycle management
|
||||
@@ -24,22 +24,22 @@ compile 'com.bluelinelabs:conductor:2.1.1'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.1'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.2'
|
||||
|
||||
// If you want RxJava lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.1'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.2'
|
||||
|
||||
// If you want RxJava2 lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.1'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.2'
|
||||
```
|
||||
|
||||
SNAPSHOT:
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.1.2-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.2-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.2-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.2-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor:2.1.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.3-SNAPSHOT'
|
||||
```
|
||||
|
||||
You also have to add the url to the snapshot repository:
|
||||
@@ -55,7 +55,7 @@ allprojects {
|
||||
|
||||
## Components to Know
|
||||
|
||||
| Conductor Components
|
||||
| | Conductor Components |
|
||||
------|------------------------------
|
||||
__Controller__ | The Controller is the View wrapper that will give you all of your lifecycle management features. Think of it as a lighter-weight and more predictable Fragment alternative with an easier to manage lifecycle.
|
||||
__Router__ | A Router implements navigation and backstack handling for Controllers. Router objects are attached to Activity/containing ViewGroup pairs. Routers do not directly render or push Views to the container ViewGroup, but instead defer this responsibility to the ControllerChangeHandler specified in a given transaction.
|
||||
@@ -118,7 +118,7 @@ public class HomeController extends Controller {
|
||||
|
||||
The lifecycle of a Controller is significantly simpler to understand than that of a Fragment. A lifecycle diagram is shown below:
|
||||
|
||||

|
||||

|
||||
|
||||
## Advanced Topics
|
||||
|
||||
|
||||
@@ -8,14 +8,15 @@ import android.support.annotation.UiThread;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.internal.LifecycleHandler;
|
||||
import com.bluelinelabs.conductor.internal.ThreadUtils;
|
||||
|
||||
/**
|
||||
* Point of initial interaction with Conductor. Used to attach a {@link Router} to your Activity.
|
||||
*/
|
||||
public final class Conductor {
|
||||
|
||||
|
||||
private Conductor() {}
|
||||
|
||||
|
||||
/**
|
||||
* Conductor will create a {@link Router} that has been initialized for your Activity and containing ViewGroup.
|
||||
* If an existing {@link Router} is already associated with this Activity/ViewGroup pair, either in memory
|
||||
@@ -30,6 +31,8 @@ public final class Conductor {
|
||||
*/
|
||||
@NonNull @UiThread
|
||||
public static Router attachRouter(@NonNull Activity activity, @NonNull ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
LifecycleHandler lifecycleHandler = LifecycleHandler.install(activity);
|
||||
|
||||
Router router = lifecycleHandler.getRouter(container, savedInstanceState);
|
||||
|
||||
@@ -55,7 +55,7 @@ public abstract class Controller {
|
||||
private static final String KEY_OVERRIDDEN_PUSH_HANDLER = "Controller.overriddenPushHandler";
|
||||
private static final String KEY_OVERRIDDEN_POP_HANDLER = "Controller.overriddenPopHandler";
|
||||
private static final String KEY_VIEW_STATE_HIERARCHY = "Controller.viewState.hierarchy";
|
||||
private static final String KEY_VIEW_STATE_BUNDLE = "Controller.viewState.bundle";
|
||||
static final String KEY_VIEW_STATE_BUNDLE = "Controller.viewState.bundle";
|
||||
private static final String KEY_RETAIN_VIEW_MODE = "Controller.retainViewMode";
|
||||
|
||||
private final Bundle args;
|
||||
@@ -87,18 +87,24 @@ public abstract class Controller {
|
||||
private final ArrayList<String> requestedPermissions = new ArrayList<>();
|
||||
private final ArrayList<RouterRequiringFunc> onRouterSetListeners = new ArrayList<>();
|
||||
private WeakReference<View> destroyedView;
|
||||
private boolean isPerformingExitTransition;
|
||||
|
||||
@NonNull
|
||||
static Controller newInstance(@NonNull Bundle bundle) {
|
||||
final String className = bundle.getString(KEY_CLASS_NAME);
|
||||
//noinspection ConstantConditions
|
||||
Constructor[] constructors = ClassUtils.classForName(className, false).getConstructors();
|
||||
Class cls = ClassUtils.classForName(className, false);
|
||||
Constructor[] constructors = cls.getConstructors();
|
||||
Constructor bundleConstructor = getBundleConstructor(constructors);
|
||||
|
||||
Controller controller;
|
||||
try {
|
||||
if (bundleConstructor != null) {
|
||||
controller = (Controller)bundleConstructor.newInstance(bundle.getBundle(KEY_ARGS));
|
||||
Bundle args = bundle.getBundle(KEY_ARGS);
|
||||
if (args != null) {
|
||||
args.setClassLoader(cls.getClassLoader());
|
||||
}
|
||||
controller = (Controller)bundleConstructor.newInstance(args);
|
||||
} else {
|
||||
//noinspection ConstantConditions
|
||||
controller = (Controller)getDefaultConstructor(constructors).newInstance();
|
||||
@@ -124,7 +130,7 @@ public abstract class Controller {
|
||||
* @param args Any arguments that need to be retained.
|
||||
*/
|
||||
protected Controller(@Nullable Bundle args) {
|
||||
this.args = args != null ? args : new Bundle();
|
||||
this.args = args != null ? args : new Bundle(getClass().getClassLoader());
|
||||
instanceId = UUID.randomUUID().toString();
|
||||
ensureRequiredConstructor();
|
||||
}
|
||||
@@ -211,6 +217,10 @@ public abstract class Controller {
|
||||
childRouter = new ControllerHostedRouter(container.getId(), tag);
|
||||
childRouter.setHost(this, container);
|
||||
childRouters.add(childRouter);
|
||||
|
||||
if (isPerformingExitTransition) {
|
||||
childRouter.setDetachFrozen(true);
|
||||
}
|
||||
}
|
||||
} else if (!childRouter.hasHost()) {
|
||||
childRouter.setHost(this, container);
|
||||
@@ -1026,7 +1036,7 @@ public abstract class Controller {
|
||||
view.saveHierarchyState(hierarchyState);
|
||||
viewState.putSparseParcelableArray(KEY_VIEW_STATE_HIERARCHY, hierarchyState);
|
||||
|
||||
Bundle stateBundle = new Bundle();
|
||||
Bundle stateBundle = new Bundle(getClass().getClassLoader());
|
||||
onSaveViewState(view, stateBundle);
|
||||
viewState.putBundle(KEY_VIEW_STATE_BUNDLE, stateBundle);
|
||||
|
||||
@@ -1039,7 +1049,9 @@ public abstract class Controller {
|
||||
private void restoreViewState(@NonNull View view) {
|
||||
if (viewState != null) {
|
||||
view.restoreHierarchyState(viewState.getSparseParcelableArray(KEY_VIEW_STATE_HIERARCHY));
|
||||
onRestoreViewState(view, viewState.getBundle(KEY_VIEW_STATE_BUNDLE));
|
||||
Bundle savedViewState = viewState.getBundle(KEY_VIEW_STATE_BUNDLE);
|
||||
savedViewState.setClassLoader(getClass().getClassLoader());
|
||||
onRestoreViewState(view, savedViewState);
|
||||
|
||||
restoreChildControllerHosts();
|
||||
|
||||
@@ -1080,7 +1092,7 @@ public abstract class Controller {
|
||||
}
|
||||
outState.putParcelableArrayList(KEY_CHILD_ROUTERS, childBundles);
|
||||
|
||||
Bundle savedState = new Bundle();
|
||||
Bundle savedState = new Bundle(getClass().getClassLoader());
|
||||
onSaveInstanceState(savedState);
|
||||
|
||||
List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
|
||||
@@ -1115,6 +1127,9 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
this.savedInstanceState = savedInstanceState.getBundle(KEY_SAVED_STATE);
|
||||
if (this.savedInstanceState != null) {
|
||||
this.savedInstanceState.setClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
performOnRestoreInstanceState();
|
||||
}
|
||||
|
||||
@@ -1133,6 +1148,7 @@ public abstract class Controller {
|
||||
|
||||
final void changeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (!changeType.isEnter) {
|
||||
isPerformingExitTransition = true;
|
||||
for (ControllerHostedRouter router : childRouters) {
|
||||
router.setDetachFrozen(true);
|
||||
}
|
||||
@@ -1148,6 +1164,7 @@ public abstract class Controller {
|
||||
|
||||
final void changeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (!changeType.isEnter) {
|
||||
isPerformingExitTransition = false;
|
||||
for (ControllerHostedRouter router : childRouters) {
|
||||
router.setDetachFrozen(false);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class ControllerHostedRouter extends Router {
|
||||
|
||||
@IdRes private int hostId;
|
||||
private String tag;
|
||||
private boolean isDetachFrozen;
|
||||
|
||||
ControllerHostedRouter() { }
|
||||
|
||||
@@ -69,6 +70,7 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
final void setDetachFrozen(boolean frozen) {
|
||||
isDetachFrozen = frozen;
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.setDetachFrozen(frozen);
|
||||
}
|
||||
@@ -80,6 +82,24 @@ class ControllerHostedRouter extends Router {
|
||||
super.destroy(popViews);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
if (isDetachFrozen) {
|
||||
entry.controller.setDetachFrozen(true);
|
||||
}
|
||||
super.pushToBackstack(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, @Nullable ControllerChangeHandler changeHandler) {
|
||||
if (isDetachFrozen) {
|
||||
for (RouterTransaction transaction : newBackstack) {
|
||||
transaction.controller.setDetachFrozen(true);
|
||||
}
|
||||
}
|
||||
super.setBackstack(newBackstack, changeHandler);
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public Activity getActivity() {
|
||||
return hostController != null ? hostController.getActivity() : null;
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ abstract public class RestoreViewOnCreateController extends Controller {
|
||||
|
||||
@Override @NonNull
|
||||
protected final View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return onCreateView(inflater, container, viewState);
|
||||
return onCreateView(inflater, container, viewState == null ? null : viewState.getBundle(KEY_VIEW_STATE_BUNDLE));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ThreadUtils;
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -83,6 +84,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public boolean handleBack() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
if (!backstack.isEmpty()) {
|
||||
//noinspection ConstantConditions
|
||||
if (backstack.peek().controller.handleBack()) {
|
||||
@@ -103,6 +106,8 @@ public abstract class Router {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public boolean popCurrentController() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction transaction = backstack.peek();
|
||||
if (transaction == null) {
|
||||
throw new IllegalStateException("Trying to pop the current controller when there are none on the backstack.");
|
||||
@@ -118,6 +123,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public boolean popController(@NonNull Controller controller) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction topController = backstack.peek();
|
||||
boolean poppingTopController = topController != null && topController.controller == controller;
|
||||
|
||||
@@ -151,6 +158,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public void pushController(@NonNull RouterTransaction transaction) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction from = backstack.peek();
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, from, true);
|
||||
@@ -165,6 +174,8 @@ public abstract class Router {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public void replaceTopController(@NonNull RouterTransaction transaction) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
if (!backstack.isEmpty()) {
|
||||
trackDestroyingController(backstack.pop());
|
||||
@@ -235,6 +246,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public boolean popToRoot() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
return popToRoot(null);
|
||||
}
|
||||
|
||||
@@ -247,6 +260,8 @@ public abstract class Router {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public boolean popToRoot(@Nullable ControllerChangeHandler changeHandler) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
if (backstack.size() > 1) {
|
||||
//noinspection ConstantConditions
|
||||
popToTransaction(backstack.root(), changeHandler);
|
||||
@@ -264,6 +279,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public boolean popToTag(@NonNull String tag) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
return popToTag(tag, null);
|
||||
}
|
||||
|
||||
@@ -277,6 +294,8 @@ public abstract class Router {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public boolean popToTag(@NonNull String tag, @Nullable ControllerChangeHandler changeHandler) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (tag.equals(transaction.tag())) {
|
||||
popToTransaction(transaction, changeHandler);
|
||||
@@ -294,6 +313,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public void setRoot(@NonNull RouterTransaction transaction) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
List<RouterTransaction> transactions = Collections.singletonList(transaction);
|
||||
setBackstack(transactions, transaction.pushChangeHandler());
|
||||
}
|
||||
@@ -362,6 +383,8 @@ public abstract class Router {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, @Nullable ControllerChangeHandler changeHandler) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
|
||||
boolean newRootRequiresPush = !(newBackstack.size() > 0 && backstack.contains(newBackstack.get(0)));
|
||||
@@ -447,6 +470,8 @@ public abstract class Router {
|
||||
*/
|
||||
@UiThread
|
||||
public void rebindIfNeeded() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
@@ -679,7 +704,7 @@ public abstract class Router {
|
||||
ControllerChangeHandler.executeChange(toController, fromController, isPush, container, changeHandler, changeListeners);
|
||||
}
|
||||
|
||||
private void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
protected void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
backstack.push(entry);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.AndroidRuntimeException;
|
||||
|
||||
public class ThreadUtils {
|
||||
|
||||
public static void ensureMainThread() {
|
||||
if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
|
||||
throw new CalledFromWrongThreadException("Methods that affect the view hierarchy can can only be called from the main thread.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CalledFromWrongThreadException extends AndroidRuntimeException {
|
||||
CalledFromWrongThreadException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=2.1.2-SNAPSHOT
|
||||
VERSION_NAME=2.1.3-SNAPSHOT
|
||||
VERSION_CODE=2
|
||||
GROUP=com.bluelinelabs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user