bluelinelabs / conductor Goto Github PK
View Code? Open in Web Editor NEWA small, yet full-featured framework that allows building View-based Android applications
License: Apache License 2.0
A small, yet full-featured framework that allows building View-based Android applications
License: Apache License 2.0
The project minsdk is set to 16. Is this a hard requirement of the library ?
Conductor uses Fragment internally as seen here, so why is it defined as fragmentless library?
can you give a sample to show toolbar as action bar in each of controllers ?
I've added the following to the MainActivity of the demo:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mRouter.setRoot(new HomeController());
}
When you use home and then launch the app again from the launcher onNewIntent()
will be called. After scrolling a bit you'll notice that the home list exists two times.
In setRoot()
the call to currentTop.controller.getView()
will return the old View and that View won't be removed from the ViewGroup. The trackDestroyingControllers(mBackStack.popAll());
call seems to remove the View reference from the old controller which means that the following Controller change will not remove the old View from the ViewGroup.
Is this by design or an error in the docs? Can you explain why if the former?
Picking up the discussion started with @sockeqwe on #3 here. I've done some work with annotation processors in the past (LoganSquare), but your FragmentArgs lib is much closer to what this would take than anything I've ever done. Any suggestions for proceeding?
I'm sure you've already put a lot of thought into this for FragmentArgs, but has anyone thought of a way to skip the step of having to build the project before the builder is generated? With LoganSquare I was able to abstract the generated code away enough that developers didn't need to care about whether or not the generated code was actually there or not, but my use case was quite a bit different. I have a feeling there probably isn't a way to abstract that away when the builder itself is what's being generated.
After compiling and installing demo, it shows only a blank screen when you rotate device.
Hi o/
For my app I decided to try to use just a single Activity
, and have different "screens" (something like an Activity
) using only controllers.
This works great. However, I'm not sure how to best simulate the startActivityForResult
functionality using controllers alone.
Basically I want the previous controller on the "task stack" to get the results of the "top" controller.
Right now I'm using something like this to pass results from one controller to the another:
val c = router.getControllerWithTag(MainActivity.TAG_MAIN_CONTROLLER)
(c as ScreenMainController).routeFromOthersScreens = MainMvpView.Route.Steps
router.popCurrentController()
This has the disadvantage of adding hard dependencies between the controllers, and they need to know how to relate between them.
My question is if there is anything similar to onActivityResult
but for controllers?
Thank you :)
Thoughts on adding a getString
method to the Controller
class? This convenience is offered by both Activity
and Fragment
in the Android framework and prevents having to reference Resources
first.
I'd be happy to make this contribution if it gets the go ahead.
Note: there is also a second, overloaded method for handling string formatting as well.
I'm working on an app that displays data in a RecyclerView
using a GridLayoutManager
. I'm trying to compute the spanCount
(the number of columns) by dividing the Controller's View's width by a desired tile width, but unfortunately view.width
is 0
even in onAttach(View)
. Is this intended behaviour? Is there an idiomatic way to deal with waiting for the initial layout apart from setting a listener in the View's ViewTreeObserver
? To be clear, the View's width is set to match_parent
.
So. I expect retained view to be reattached after activity recreation. But nothings happens.
Controller
keeps view — thats ok. But view itself keeps its parent. And SimpleSwapChangeHandler
can't add it on router recreation:
if (to != null && to.getParent() == null) {
container.addView(to);
}
I've modified the demo's NavigationDemoController
to call getRouter().getActivity().recreate()
when the up button is pressed. The Activity will be destroyed and created again, but the view is blank. Pressing back once will correctly show the view of the previous Controller in the backstack.
I want to be able to deep-link into a part of my app. As an example, an email app will want to go directly to an email from a notification. According to https://developer.android.com/training/implementing-navigation/temporal.html#SynthesizeBackStack (cached version since the docs seem to be down), the correct behavior is to add to the back stack so that it's as though the user navigated there manually. Essentially, I want to be able to add multiple Controllers to the back stack in one transaction. I could do a bunch of pushController calls in one go, but that seems wasteful since (if I understand the implementation correctly) each controller will have to create its View.
Flow solves this by using "key" objects to reference individual parts of the back stack. Basically immutable objects. It's then possible to create a History object, which is essentially a list of keys, and use that as the new backstack. Could we have something similar, maybe a Router.setBackStack(List<Controller>, ControllerChangeHandler)
method?
Hi!
Is there any way to push a [child] controller from within another child controller?
Let's say I'm displaying a child controller (named A) in a container view (named V), that it is just a part of the whole screen.
What I want is to push another controller from A and make its view be contained in V.
From what I understand, if we push a controller using the router, that would replace the view of the parent controller, and that's not what I want to achieve.
Thank you in advance!
EDIT
My use case is:
ControllerPagerAdapter
.Any chance you can expose a method for getting the current controller? Or maybe one already exists and I've missed it?
I’m using conductor for a ‘wizard type’ flow and want to simplify things by doing something like: getCurrentController().onNext() OR getCurrentController().onPrevious, etc. I noticed that I can peek at the backstack, but it’s a private package so I can’t get a reference to it.
Thoughts or possibilities?
Thanks for the awesome work.
getTargetController() returns null if target is nested too many times, this happens after process death.
I made a project to reproduce the issue.
https://github.com/adi1133/Conductor-Issue
The issue is caused by the fact that getChildControllerWithInstanceId(instanceId)
is not recursive.
Please reduce the minSdkLevel to 15 (Android 4.0.X): I've an app in production with minimal sdk level to 15 and I want to use this framework. Thanks
On Target Controller Demo part of Demo app, after typing the title and pressing "User Title" the software keybaord stays on top and covers the bottom button bar. Also keyboard could be dismissed
First of all, excellent job, another excellent library to "bypass" the Fragments nightmare !
I have some doubts regarding this lib:
Sorry to bother and thanks again!
I am not sure this is the intended behaviour, though here it seems to call the method with empty listeners.
So, is this intended? I believe it would be helpful to have all controllers changes going through the set of listeners we add at the router level.
Thanks!
Hi,
I have noticed that LifecycleListener.postCreateView()
is called after LifecycleListener.onRestoreViewState()
.
Is that per purpose? To me it sounds like a little bit a contradiction of Conductors lifecycle diagram where onCreateView()
becomes before onRestoreViewState()
so that I was expecting that LifecycleListener.postCreateView()
will be called right after onCreateView()
and before onRestoreViewState()
.
I'm facing the problem where I do some View foo = findViewById(R.id.foo)
in LifecycleListener.postCreateView()
but also wanted to recover the state of View foo
manually in LifecycleListener.onRestoreViewState()
which leads to a NullPointerException because LifecycleListener.postCreateView()
is called after LifecycleListener.onRestoreViewState()
...
Obviously I can rewrite my code to avoid that problem, I just wanted to ask if this execution order is by design?
Is it possible to have 2 activities, each with their own router?
When root controller is being popped in handleBack()
and controller does not handle back press, popCurrentController()
returns false
because backstack became empty after popping happened and thus handleBack()
returns false
. It means that router did not handle back press when it actually did by doing pop.
It works well when root controller is on root view within activity. But in my case I have root view without any Router attached and its child View with backstack controlled by Router:
+----------------------------------------+
| +-------------------+ |
| | | |
| Root View | Child View with | |
| that controls | attached Router | |
| its child | and backstack | |
| (without own | | |
| backstack) | | |
| | | |
| | | |
| | | |
| +-------------------+ |
+----------------------------------------+
So when back pressed on activity I expect it to be consumed by child View's Router even if only root controller left in backstack.
Btw thank you for the library!
When I touched "Target Controller", "PICK IMAGE (FROM GALLERY)", and picked a image,
then the demo app crashed.
Process: com.bluelinelabs.conductor.demo, PID: 5946
java.lang.RuntimeException: Failure delivering result ResultInfo{who=android:fragment:0, request=126, result=-1, data=Intent { dat=content://media/external/images/media/27659 }} to activity {com.bluelinelabs.conductor.demo/com.bluelinelabs.conductor.demo.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setImageURI(android.net.Uri)' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:3542)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3585)
at android.app.ActivityThread.access$1300(ActivityThread.java:147)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1332)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5237)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:906)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:701)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setImageURI(android.net.Uri)' on a null object reference
at com.bluelinelabs.conductor.demo.controllers.TargetDisplayController.setImageView(TargetDisplayController.java:98)
at com.bluelinelabs.conductor.demo.controllers.TargetDisplayController.onActivityResult(TargetDisplayController.java:68)
at com.bluelinelabs.conductor.Router.onActivityResult(Router.java:52)
at com.bluelinelabs.conductor.internal.LifecycleHandler.onActivityResult(LifecycleHandler.java:130)
at android.app.Activity.dispatchActivityResult(Activity.java:6145)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3538)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3585)
at android.app.ActivityThread.access$1300(ActivityThread.java:147)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1332)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5237)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:906)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:701)
Device: Xiaomi2(MI 2S), Android 5.0.2 LRX22G.
Hello,
Firstly, Thanks a lot for this great library!
I'm using ControllerPagerAdapter inside a ViewPager, and each child controller contains a RecyclerView.
The problem is that my RecyclerView displays a list of 50 items of size and I'd like to save the 50 items in a bundle so that I could prevent loading it from the backend.
The problem arises when I scroll the ViewPager and the adapter's destroyItem method is called, you destroy the Child controller completely and remove it from the host controller as a result neither onSaveViewState or onRestoreViewState is called and I'm unable to restore the removed page state when the user returns to it and everything is loaded all over again.
Perhaps restoreState and saveState should be overriden to fix this behavior?
Hope that I've been able to explain the issue clearly.
Thanks!
I'm not 100% sure, but I think that the current way Controller.setTargetController()
could cause problems because one could change the target controller afterwards. I think it would be better to set the target controller as constructor parameter to avoid such issues.
Is that nice to implement ViewAnimationUtils
for lower APIs as here?
Would it make sense to expose the Backstack
through Router
, e.g. as Iterable<RouterTransaction>
?
I'm working on a separate utility that allows binding resources to controller lifecycles and makes them available for child controllers and controllers later in the backstack. (Think: shared state of a multi-step wizard or a Dagger component). In there, I'd like to have something like findResource(someChildController, "MyResource")
that iterates back up to the parent controller and backwards through the backstack.
Backstacks obtained this way could also be built upon and used for updating history, as requested in #52.
Happy to implement this!
Edit: grammar
Shouldn't the resetFromView call reset the X translation to 0? It currently sets the Y translation.
Facebook SDK login requires us to provide a reference to an activity or fragment so it will call startActivityForResult()
internally. It means that we need to pass LifecycleHandler
fragment reference there in order to get the result to its onActivityResult()
and dispatch it to controllers. However, I didn't find an easy way to get that reference. Is there any official workaround?
What lifecycle callback should I use to call getTargetController()
as soon as possible ?
The call works from inside onCreateView()
, but fails inside onRestoreInstanceState()
or any of the constructors.
Ideally I'd use a lifecycle callback that happens only once per instance but has access to getTargetController()
My usecase is grabbing data from a "parent" controller, I set the parent controller using setTargetController()
, later I want the child to get a dagger component from the parent to inject itself.
Am I doing it completely wrong ?
I am having trouble with the onActivityResult
callback in my controller. Before 1.1.0 it was running fine, but after it, when it gets called back, my views' references are null.
I've noticed my onDestroyView
gets called before the callback gets called. A workaround is to make my Controller RETAIN_DETACH
, but not sure this is what I wanted in the first place.
I am using the speech to text API through intents. So, I guess that to reproduce the issue you can simply:
final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en");
startActivityForResult(intent, REQ_CODE_SPEECH_INPUT); // any constant value you want
My callback is like:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQ_CODE_SPEECH_INPUT
&& resultCode == Activity.RESULT_OK
&& data != null) {
final ArrayList<String> result = data
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
// Just displaying the results
spokenText.setText(spokenText.getText().toString() + " " + result.get(0));
}
}
Any help is appreciated :)
Hi
I start to using this lbrary, looking nice.
Now I have dashboard view with view pager which include recyclerView.
On rotation, i got crash (not always but very frequently ) in base Controller.java class.
Any ideas ?
Thanks!
04-26 17:34:41.439 28412-28412/com.app.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.app.app, PID: 28412
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
at com.bluelinelabs.conductor.Controller.attachChildController(Controller.java:689)
at com.bluelinelabs.conductor.Controller.attach(Controller.java:760)
at com.bluelinelabs.conductor.Controller.access$400(Controller.java:38)
at com.bluelinelabs.conductor.Controller$6.onViewAttachedToWindow(Controller.java:848)
at android.view.View.dispatchAttachedToWindow(View.java:15816)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3137)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3145)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3145)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3145)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3145)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3145)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3145)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1731)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1437)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7397)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:920)
at android.view.Choreographer.doCallbacks(Choreographer.java:695)
at android.view.Choreographer.doFrame(Choreographer.java:631)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:906)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7224)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Hi,
I'm trying to understand the lifecycle of a Controller and I have a doubt:
How can I differentiate a configuration change such as the device rotation from a navigation event such as the Controller being popped or be sent to the backstack?
When are the onSaveInstaceState
and onSaveViewState
methods called on each case?
I think more documentation on that topic would be great to fully understand why the lifecycle of a Controller is easier than the Activity one, as you state.
Thank you very much.
Your lib is great :)
I would like to convince you to combine them!
inflate
method final
and delegates to two abstract
methods for each responsibility. And potentially it only delegates to one method if you are using ButterKnife for all binding in that application-level base class.Currently developers are expected to do controller initialization in the constructor, which is great for some stuff, but makes other things a lot harder. Controllers don't have a reference to a router, activity, or parent controllers in the constructor, so things like dagger injection become more tricky. If you require references to these things for your initialization, you have to wait for the onCreateView()
callback, then make sure you only run that code block once. Awkward.
An onCreate()
method would be the callback signaling that the controller is fully configured (minus the view) and has all the references it'll need.
I'm implementing a Facebook login implementation on top of the demo app, but I can't find a way to bind the onActivitityResult from Facebook to my Controller.
What I have:
/**
* Created by leonardo on 4/11/16.
*/
public class FacebookController extends BaseController {
@Bind(R.id.login_button)
LoginButton loginButton;
private CallbackManager callbackManager;
@Override
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
return inflater.inflate(R.layout.controller_facebook, container, false);
}
@Override
protected void onViewBound(@NonNull View view) {
super.onViewBound(view);
callbackManager = CallbackManager.Factory.create();
loginButton.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Toast.makeText(getActivity(), "Logged in !", Toast.LENGTH_SHORT).show();
}
@Override
public void onCancel() {
Toast.makeText(getActivity(), "Canceled by the user!", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(FacebookException error) {
Toast.makeText(getActivity(), "Error:"+ error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
loginButton.setOnClickListener(null);
}
@OnClick(R.id.login_button)
public void onClick(View v){
LoginManager.getInstance().logInWithReadPermissions(getActivity(), null);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode, resultCode, data);
}
@Override
protected String getTitle() {
return "Facebook SDK Login";
}
}
I realized that in order to get the Activity's lifecycle callbacks I must call the Controller startActivityForResult for instance, but as I'm using Facebook SDK I don't have the control over this.
How should I proceed in that case ?
Thanks
Hi,
Using setHasOptionsMenu()
in a child controller from a ControllerPagerAdapter doesn't seem to call any of the options menu-related callbacks. Is this intentional? Love the library!
Thanks!
Shouldn't be nice to have a "@NotPersistent" annotation, so when moving to one Controller to another, maybe you don't want to save the "backstack" Controller.
For example: You have a screen with people list, you add a new person, close the current insertion Controller and go back, the list will be saved at the previous position (when the user clicked the '+' button).
There is a dependency on Conductor:conductor:unspecified, this dependency is broken.
<dependency>
<groupId>Conductor</groupId>
<artifactId>conductor</artifactId>
<version>unspecified</version>
<scope>compile</scope>
</dependency>
The workaround I am using is including the dependency using:
compile 'com.bluelinelabs:conductor:1.1.5-SNAPSHOT'
compile('com.bluelinelabs:conductor-rxlifecycle:1.1.5-SNAPSHOT') {
exclude group: 'Conductor', module: 'conductor'
}
Some time we need to listen to onLowMemory
callback in our Controller.
For example when I'm working with Mapbox, it requires to call onLowMemory
:
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
If you ok with this feature, I'm happy to do a PR.
This was odd to me, but pressing the back button on a controller (that does not override handleBack()
) should pop it. This works well but I thought it would call all the lifecycle methods such as onSaveInstanceState()
which isn`t called.
Am I missing something obvious here?
In a Controller, the only method we need to override is:
@NonNull
@Override
protected View inflateView(LayoutInflater inflater, ViewGroup container) {
return null;
}
But README shows:
@Override
protected int layoutId() {
return R.layout.controller_overlay;
}
@Override
public void onBindView(@NonNull View view) {
super.onBindView(view);
((TextView)view.findViewById(R.id.tv_title)).setText("Hello World");
}
I am wanting to switch from Flow to Conductor, but I cannot seem to grasp my head around when to create my presenter in the controller. After looking at the source, I would imagine that creating the presenter in preAttach then attaching the view to the presenter and detaching the view in postDetach would be a good route to go, but I am struggling on actually doing that. Any help/advice would be greatly appreciated!
(If this isn't the right place for this, let me know and I'll remove it)
As discussed in #42 here is just a friendly reminder how to setup authentication for snapshot deployment from travis properly (remove previous env / global / secret
definitions:
travis encrypt -r bluelinelabs/Conductor "ORG_GRADLE_PROJECT_NEXUS_USERNAME=InsertYourUsername" --add
travis encrypt -r bluelinelabs/Conductor "ORG_GRADLE_PROJECT_NEXUS_PASSWORD=InsertYourPassword" --add
When pushing a new controller, onCreateOptionsMenu is not being called on the pushed controller.
If i were to use the tested controller as the root controller on an activity, onCreateOptionsMenu is called as expected.
My scenario is:
I have 2 Controllers, a list and add button that goes to another Controller (edit or new). The second controller has a Toolbar with a back button (actionBar.setDisplayHomeAsUpEnabled(true)).
My callback:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getRouter().popCurrentController();
default:
return super.onOptionsItemSelected(item);
}
}
The add button (1st Controller):
getRouter().pushController(RouterTransaction.builder(new KidsRegisterController(false, null, this)).build());
The problem is, after the second open and go back (From 1st -> 2nd and then back for the second time)
the second Controller is never popped, and if I click the back button I get this exception:
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to destroy activity {br.com.dinda.qa/br.com.dinda.views.activities.KidsActivity}: java.util.ConcurrentModificationException
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3273)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3291)
at android.app.ActivityThread.access$1200(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1248)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
at com.bluelinelabs.conductor.Router.onActivityDestroyed(Router.java:392)
at com.bluelinelabs.conductor.internal.LifecycleHandler.onDestroy(LifecycleHandler.java:123)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:983)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1035)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1017)
at android.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:1826)
at android.app.Activity.performDestroy(Activity.java:5171)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1109)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3260)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3291)
at android.app.ActivityThread.access$1200(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1248)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
I would like to convince you to combine them!
Both pairs are really responsible for the same thing: moving the controller from a place with no view to a place with a fully usable view. In the happy case where views are saving all the state themselves, nothing changes about your implementation. And if you do need custom storage around view state it now happens at the same time and in the same place where you're already setting them up or tearing them down.
Bonus: greatly simplified lifecycle (especially combined with #8) which is focused around the larger what not the how things happen. This makes you opinionated about the lifecycle instead of the implementation of the lifecycle.
A simplified lifecycle also reduces the question of when methods are called by putting them into higher-level categories. The application-layer is allowed to decide what distinction they want to break apart based on their library and conventions. Unlike the mistakes Android made in Activity and Fragment, you don't need to cater to subclasses trying to incorrectly mix-in functionality through inheritance instead of composition via delegation. When the lifecycle is simplified you even make delegation easier since there's less for libraries wanting to hook into the lifecycle to require hooks for.
What's also nice about the new lifecycle (assuming #8 as well) is that it's only ever one step to move from state to state, not multiple. This eliminates the question of conditional callback uncertainty and ordering uncertainty:
onRestoreInstanceState
.initializeView
(or whatever).onAttach
.onDetach
.destroyView
.onSaveInstanceState
.Controllers that call super(bundle) directly don't pass validation. This makes the API more verbose. I know it is not good to try and find constructors in super types but we could have an initialization flag to know if those were called.
Also, there could be a generic Controller builder that would substitute the BundleBuilder helper. This could ease a bit the API. Something like:
Controller.newBuilder(AController.class).putInt(KEY_SMTG, value).putString(KEY_SMTG_ELSE, anotherValue).pushChangeHandler(new DifferentChangeHandler()).toRouterTransaction();
If the idea is somewhat valid I can send a WIP PR to better show the interface.
conductor-1.1.5.aar
is missing on maven central https://repo1.maven.org/maven2/com/bluelinelabs/conductor/1.1.5/
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.