konmik / nucleus Goto Github PK
View Code? Open in Web Editor NEWNucleus is an Android library, which utilizes the Model-View-Presenter pattern to properly connect background tasks with visual parts of an application.
License: MIT License
Nucleus is an Android library, which utilizes the Model-View-Presenter pattern to properly connect background tasks with visual parts of an application.
License: MIT License
Inside my fragment, I call startActivityForResult where I launch a camera activity, and I get back a result and ask the presenter to do some work on the returned data. When the presenter calls getView(), it returns a null object.
Seems to be similar to this issue: #35
For now I am doing this (the suggestion from issue #35) :
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
getPresenter().takeView(this);
getPresenter().doXYZ();
}
I am using nucleus ver. 2.0.3
First of all - thanks for this gr8 library! It's really osom!
Would be nice to see a more complex example of using Nucleus. For example, let's say we have a Social Network with user profile page (RecyclerView). This page has basic user info (getProfile(id) request), and user's posts (getUserPost(offset) request) which could be lazily loaded. How would you code a Presenter? How would u save/restore RecyclerView state (with deliverLatestCache?))? I'm new to RxJava also and i think this example will help a lot of people to get in MVP much faster!
Hi. Thank you for this great lib. I just wonder if it can be used when programming in Kotlin.
Are there any issues with annotations such as @RequiresPresenter?
Hi there!
Thank you for this great library! But I have a little question about usage it along with Dagger 2.
How can I inject my precious arguments in presenter's constructor and do not break functionality of Nucleus?
If I use @RequiresPresenter(RxMainPresenter.class)
, I can't pass arguments to my presenter.
If I use @Inject RxMainPresenter mainPresenter;
, my presenter will not be cached.
Thank you in advance!
Here is some code:
public class RxMainPresenter extends RxAbstractPresenter<RxMainPresenter.View> {
private final RxInstancesObservable instancesObservable;
@Inject
public RxMainPresenter(final ExecutorService executorService, final RxInstancesObservable instancesObservable) {
super(executorService);
this.instancesObservable = instancesObservable;
}
@Override
protected void onCreate(final Bundle savedState) {
super.onCreate(savedState);
Observable.create(instancesObservable)
.retry(3)
.subscribeOn(Schedulers.from(executorService))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<CabinetInstance[]>() {
@Override
public void onCompleted() {
}
@Override
public void onError(final Throwable e) {
getView().displayInstancesError(e);
}
@Override
public void onNext(final CabinetInstance[] instances) {
getView().displayInstances(instances);
}
});
}
public interface View extends RxAbstractPresenter.View {
void displayInstances(final CabinetInstance[] instances);
void displayInstancesError(final Throwable throwable);
}
}
It seems the subclassing of NucleusActivity
is the only option for now. I can see from your code that all the logic actually lies in PresenterHelper, but it would probably be a little bit verbose to use it in all activities. I guess that's why you introduced NucleusActivity
.
Starting from API 14 we can register callbacks on activity lifecycle, so the subclassing could probably be replaced by a simple call to something like Nucleus.bind(this)
in the onCreate()
of each activity.
What do you think?
EDIT: It would probably be a second option
rather than a replacement, since it wouldn't be compatible with API < 14.
Currently, the PresenterHelper class requires the Activity in the takeView()
method for the sole purpose of determining whether the presenter should be destroyed when dropView is called.
This logic, of deciding whether or not to destroy the presenter should be extracted from the PresenterHelper, completely freeing it from ties to the Activity.
I propose updating the dropView()
method to something like dropView(boolean destroy)
. This way, it's up to the container (ViewGroup, Fragment, Activity, View, Service, etc) to determine when the presenter should be destroyed.
This will better support things like single activity, fragment/view only setups, and ensure the PresenterHelper performs only it's very specific tasks.
What is proper way to use restartables? create one per task or reuse it?
Below are 2 examples (in kotlin)
...
val LOAD_TRACKS = 1
val SAVE_TRACK = 2
var track: Track
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
restartableLatestCache(LOAD_TRACKS,
{ TrackModel.getTracks().applySchedulers() },
{ view, content -> view.showContent(content) })
{ view, error -> view.showError(error) }
restartableLatestCache(SAVE_TRACK,
{ TrackModel.saveTrack(track).applySchedulers() },
{ view, content -> view.showContent(content) })
{ view, error -> view.showError(error) }
}
fun getTracks() {
start(LOAD_TRACKS)
}
fun saveTrack(track Track) {
this.track = track
start(SAVE_TRACK)
}
...
...
val RESTARTABLE_ID = 1
var SAVE_LOAD_FLAG
var track: Track
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
restartableLatestCache(RESTARTABLE_ID,
when(save_load_flag){
save_flag -> save
load_flag -> load
},
{ view, content -> view.showContent(content) })
{ view, error -> view.showError(error) }
}
val save = fun(track: Track): Observable<List<Track>> { return TrackModel.saveTrack(track) }
val load = fun(): Observable<List<Track>> { return TrackModel.getTracks() }
fun request(save_load_flag default load, track default null) {
this.save_load_flag = save_load_flag
this.track = track
start(RESTARTABLE_ID)
}
...
Not so much an issue as a question of practices:
Most Dagger 2 examples that I see rely on a "context" (generally from the Activity or Application) to get/create components and modules. Presenters don't inherently have a context, and the Presenter itself can have a different lifecycle from it's Activity. This makes me hesitant to use the Activity context. I could get the application context but based on what I have read that seems like bad practice. In my particular case I'm making use of a factory to create my Presenter, so I don't think constructor injection in the Presenter class can work, and integrating Dagger into the factory strikes me as a bit odd.
How would you recommend handling DI in a case like this, using Dagger 2? (I'm currently leaning towards just getting/using the application context, but that feels sloppy)
a view contain a list๏ผlist have add more data interface
I use Rxpresenter๏ผwhen getData in backgroud๏ผthen i offen lock screen and open screen๏ผthen presenter would try view.onNext again๏ผthen view will be add more the same dataใ
can you change onNext will call once again for list then load more๏ผ
Overrides to getPresenterFactory()
is not called in version 2.
@Override
public PresenterFactory<NetworkListPresenter> getPresenterFactory() {
return new PresenterFactory<NetworkListPresenter>() {
@Override
public NetworkListPresenter createPresenter() {
return presenter;
}
};
}
This is working only by setting manually the presenter via setPresenterFactory
:
@Override
protected void inject() {
getComponent(SetupNetworkComponent.class).inject(this);
setPresenterFactory(new PresenterFactory<NetworkListPresenter>() {
@Override
public NetworkListPresenter createPresenter() {
return presenter;
}
});
}
Hello!
Is there any issue with RxPresenter
that makes the onCreate(Bundle savedState)
receive the bunde always null
after orientation changes?
I notice that every time a new Presenter is created, this is causing the null
Bundle
at onCreate
...
Also, this seems to make the presenter's deliverLatestCache()
being called several times after the fragment onResume
is called.
Hi!
I'm starting to implement the library now, but I have a question for reuse of fragments.
Let me explain my doubt:
Considering I have two fragments, one is a map, and another a list, put into a ViewPager. Can I use the same presenter in two fragments to reuse the API calls? In this hypothetical example both fragments query for the same data in API.
Or the best approach is use the Activity presenter to query the data and than delivery to the fragments?
It's just a question about architecture and things that I can archieve using the library, fell free to close this issue if you think is off-topic.
And thanks for the amazing project.
First, thanks a lot for your work on nucleus.
It seems that master holds version 1.3.1, whereas "only" 1.3.0 is available using Gradle. When can we expect to see the new version available as a library from Gradle?
Thanks!
Hi Konmik,
what is your opinion about MVVM pattern and its implementation that provides Google?
Is there any chance to see nucleus for MVVM?
Imagine this scenario:
public class LauncherPresenter extends RxPresenter<LauncherActivity> {
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
SettingsModel settings = Settings.getInstance(view).readSavedSelectedUsageMode();
if (settings != null && settings.getMode() != SettingsModel.NONE) {
switch (settings.getMode()) {
case SettingsModel.ONE:
getView().ShowOnething();
break;
case SettingsModel.TWO:
getView().ShowAnotherThing();
break;
}
} else {
getView().ShowSomethingCompletlyDifferent();
}
}
}
Using nucleus i'd imagine you want to handle this from the Presenter. You load a saved setting from your chosen persistence library. Check what setting was loaded and then tell the activity it should show something specific. Now the problem here is that this can only take place in the onTakeView()-method of nucleaus, because before that the Activity has not yet been created.
Would it be correct to implement this code in onTakeView? Or would you rather just implement it into the Activity all together? I'd rather keep being consistent in using presenters in all activities.
Hello!
Is it possible to inject the presenters into the activities with Dagger instead of using the @RequiresPresenter
annotation?
Thanks,
Martin
Hi!
I'm working on an app I want to use Nucleus for MVP+Rx.
I have some doubts, and I couldn't find any solution in the documentation/wiki. Could you please help me?:
How can I use two instances of the same view, and make each of them have their own instances of their presenters? Is it possible by telling the PresenterStorage
with ID to use? Or by any other way?
Why is the presenter being destroyed and pointed to null in the onPause
method instead of doing so in onDestroy
? The documentation seems to be out of date. What if I want to stack several activities of the same type, and want them to keep their presenters while not destroyed by the system? Is that working because you pass isFinishing()
to the delegate?
I think we need more documentation about the restartable*
methods in RxPresenter
. I know that it is documented in the code with Javadoc, but I think it would be great to add some explanation in the wiki. Something like the deliver*
methods documentation (btw, some of them seem to be out of date since you released version 2.0.1).
Thank you very much.
Your library is great.
Cheers,
M.
hello
i use this lib
if press phone home key
and netowrk thread run
call getView() is null....
why getview() null?
i want background thread run and ui change
ps : sry im not write english very well...
my think use Taking its place will be AppCompatActivity.
I can't tell if this is just my code or if it's something to do with Nucleus but I have LeakCanary attached to my app and I am noticing that PresenterStorage.Instance is leaking my activity. This was the trace that was provided:
It seems to be the Nucleus library that is leaking the activity. Would you be able to shed more light into the issue? Let me know if you would like more details.
I'm attempting to use a NucleusLayout as the base class for my view. Because the view life cycle is not tied to the activity life cycle, Nucleus is creating a new Presenter each time addView() is used, but not destroying it when removeView() is used. Due to the way PresenterHelper and NucleusLayour are implemented I am unsure how to fix this issue without duplicating the classes in their entirety.
Hi, Not sure if this is an issue, or I just don't know the correct way to handle this.
In my MainActivity, I fired: startActivityForResult()...
Then onActivityResult() comes back, and I do stuff with my presenter. getPresenter() is fine, but within my presenter, getView() returns null.
Some logging tells me this is the flow:
onResume
startActivityForResult
onPause
onActivityResult
onResume
....
Thanks,
Jia
Is currently under testing.
I hope that it will be production-ready in the middle of August.
Want to join beta?
compile "info.android15.nucleus:nucleus:2.0.0-beta8"
Primary goals are:
RxPresenter
to be completely usable without getView()
- 100% done!OperatorSemaphore
with standard RxJava operators. - 100% done!Update: a little note about the new rx.Single
. rx.Single
is awesome and I'm already playing with it. However, creation of additional methods for it will cause much more confusion than advantage (I already implemented this, but dropped the implementation because it just looks bulky, imagine stranger method names like deliverSingleCache
, restartableSingleCache
). The right way to handle rx.Single
in Nucleus is to cast it to Observable
.
There is a possibility so slightly improve restartable API.
Instead of
registerRestartable(REQUEST_ITEMS, new Func0<Subscription>() {
@Override
public Subscription call() {
final String name1 = name;
return App.getServerAPI()
.getItems(name.split("\\s+")[0], name.split("\\s+")[1])
.observeOn(AndroidSchedulers.mainThread())
.compose(MainPresenter.this.<ServerAPI.Response>deliverLatestCache())
.subscribe(new Action1<ServerAPI.Response>() {
@Override
public void call(ServerAPI.Response response) {
getView().onItems(response.items, name1);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
getView().onNetworkError(throwable);
}
});
}
});
we can write something like this:
restartable(REQUEST_ITEMS)
.switchMap(new Func1<Object, Observable<ServerAPI.Response>>() {
@Override
public Observable<ServerAPI.Response> call(Object o) {
return App.getServerAPI()
.getItems(name.split("\\s+")[0], name.split("\\s+")[1])
.observeOn(AndroidSchedulers.mainThread());
}
})
.compose(this.<ServerAPI.Response>deliverLatestCache())
.subscribe(new Action1<ServerAPI.Response>() {
@Override
public void call(ServerAPI.Response response) {
getView().onItems(response.items, name);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
getView().onNetworkError(throwable);
}
});
Pros:
Cons:
final
variable, so more code will be used for this in some cases.Are there any other pros and cons of having such API?
These look very handy and sounds like what I need in my app, but trying to find an example of how to use it is tricky.
To allow easier passing parameters to a presenter factory in some cases.
Origin: #12 (comment)
Hi konmik,
I'm using this library into my app and I find it great and easy to use. I have a little problem with deliverLatestCache() method. You said that:
deliverLatestCache() is the same as deliverLatest() but in addition it will keep the latest result in memory and will re-deliver it when another instance of a view becomes available (i.e. on configuration change). If you don't want to organize save/restore of a request result in your view (in case if a result is big or it can not be easily saved into Bundle) this method will allow you to make user experience better.
and I'm trying to get this latest result but I don't understand what I have to do with it. Can you make an example of use for this method?
Thanks a lot.
The only real Presenter class in 2.0.0 is now RxPresenter. This seems kind of silly. Can we get a basic presenter that at least has "getView()" something similar?
Can you please give an example of using Nucleus with Material Design - like Toolbar (with tabs)? Right now your sample has hard-coded tabs and no Toolbar.
I was going through Paging Example and implemented it but it never did more then one extra page request. I noticed the following snippet in RxPager
.
public Observable<Integer> pages() {
return requests
.concatMap(targetPage -> targetPage <= requestedCount ?
Observable.<Integer>never() :
Observable.range(requestedCount, targetPage - requestedCount))
.startWith(Observable.range(0, initialPageCount))
.doOnNext(it -> requestedCount = it + 1);
}
The logic seems to handle the case if you would try to request the same page number over and over again.
Shouldn't Observable.<Integer>never()
be replaced with Observable.<Integer>empty()
? If a requestNext(page)
is fired multiple times (as can happen if you don't block it in OnScrollPaging
, the Observable.<Integer>never()
will seem to block the stream and no more valid requests will seem to get fired anymore. I replaced it with an empty Observable and then everything seems to work fine.
Can you give me your thoughts on this?
At the moment its (=Delivery) split function takes two Action2 parameters: One for onNext the other for onError.
If you are not interested in onError you can pass null to that. (do I understand correctly?)
I want to propose to add a simple overload:
public void split(Action2<View, T> onNext) {
split(onNext, null);
}
This would make it clearer and more consistent to RxJava. I generally don't like to study the javadoc of each method to find out what happens when I pass null and find that approach quite error prone.
I have a normal presenter and an activity. In onTakeView the presenter passes a boolean to a method in the activity which sets the drawable of a button to an image on true or to null on false.
What happens is that since I switched to nucleus I can see the image for the fraction of a second before it vanishes (when the presenter calls it).
This causes an annoying visual flicker. How can I fix that? Or is it a bug? I don't use any other threads than the main thread.
I am not sure what the best way is to bind to a view.
I always am not sure and have two approaches so far. They are both supposed to do the same thing.
add(view().flatMap { view -> bookChest.removedObservable().map { Pair(view, it) } }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it.first.bookRemoved(it.second) })
add(bookChest.removedObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { view?.bookRemoved(it) })
The removedObservable
is a PublishSubject
. I am only interested in the emissions while the view exists.
I find Approach 1 kind of hacky-ish. And it creates lots of Pair Objects.
Approach 2 has a serious problem: It throws backpressure exceptions when the subject emits at the start at observeOn
. I thik this is because the view is being created at that time.
I could add a onBackPressureDrop to Approach 2, but that seems also hacky.
What is a common pattern to handle this?
It is written in kotlin but it should be clear what happens. the view?.bookRemoved...
would translate to a if (view != null) view.bookRemoved...
in java. The it
is just short for the single emission.
The current version, 1.3.1, is not available in the gradle repositories. Can I help to update?
As someone asked you in your article, I think Presenter shouldn't have any Android stuff. So, the onSave and onCreate should use Map or something else.
Just a small addition to be able to use the new toolbar from materialdesign.
Adds NucleusActionBarActivity as class :)
Hi!
First of all, thank you for this awesome approach to handling MVP and configuration changes! I have run into some doubts/problems regarding the RxPresenter implementation, hope you could help me clear them up.
The SubscriptionList
inside the presenter gets only unsubscribed onDestroy
. This apparently means that when we have a configuration change (isFinishing() not called) it's causing a "transient" memory leak (it gets detected by LeakCanary), where the Subscription
s inside SubscriptionList
will keep a reference to the views that were active before the config change. The following screenshot shows what I mean, as found in MAT:
I mean "transient" since as soon as the current view is really destroyed the presenter is gone for good, so the old views can finally be garbage collected; there's however a period of time where we can have whole view hierarchies leaked.
The workaround I found was simply not adding the Subscriptions
to the SubscriptionList
with add()
(note that I am not using the restartable functionality, and always using DeliverFirst). What's the downside of this? Everything seems to work as expected, but I've noticed the Presenter is kept around until the task is finished, which is bad but at least the views are not kept (avoiding all the problems this brings). Moreover, this way makes LeakCanary not complain any more.
Am I missing something? Please also note that I'm a novice in RxJava matters, so that could be my main problem.
Thanks a lot!
Is this by design?
I wanted to use a RecyclerView inside my fragment and I noticed that I could use deliverLatestCache()
to handle restoring state during a config change (like rotation). But I don't want to set the adapter when the fragment is resumed (i.e. coming back to the fragment after navigating away). So from the onNext
I call this method in my view:
public void setAdapter(List<Item> items){
if(recyclerView.getAdapter() == null){
recyclerView.setAdapter(new ItemListAdapter(items));
}
}
deliverLatestCache()
will call setAdapter()
both during rotation and when the fragment is resumed. And since I only want it to set the adapter during a config change, I check to see if recyclerView already has an adapter. If it doesn't I assign it one using the data given back from the Presenter.
I wanted to know if this sort of approach is what you had envisioned when you wrote deliverLatestCache()
or am I using it wrong?
I have this piece of code if my presenter:
add(instancesProvider
.provideObservable()
.retry(1)
.compose(this.<CabinetInstance[]>deliverLatestCache())
.observeOn(uiScheduler)
.subscribeOn(ioScheduler)
.subscribe(new Action1<CabinetInstance[]>() {
@Override
public void call(final CabinetInstance[] instances) {
Logger.i("instances");
getView().displayInstances(instances);
}
}, new Action1<Throwable>() {
@Override
public void call(final Throwable throwable) {
getView().displayInstancesError(throwable);
}
}));
And here is provide
method:
@Override
public Observable<CabinetInstance[]> provideObservable() {
return Observable.create(new Observable.OnSubscribe<CabinetInstance[]>() {
@Override
public void call(final Subscriber<? super CabinetInstance[]> subscriber) {
try {
Logger.i("server");
JSONObject jsonObject = cabinetApi.getInstances(session.getToken());
subscriber.onNext(CabinetInstance.createArrayFromJson(jsonObject.optJSONArray(ALIAS)));
subscriber.onCompleted();
} catch (final Exception e) {
session.invalidateToken();
subscriber.onError(e);
}
}
});
}
As result I can see instances
message twice. If I remove deliver* method from the chain, onNext
is called only once.
Logger prints:
server
instances
instances (with a delay)
How can we restart the process manually when the app is in the background and make sure that everything works properly? Thanks.
Hello.
If I need activity or application context in onCreate(), how can I get them?
how to use with https://github.com/trello/RxLifecycle
This is more of a general question than an issue.
I wanted to use RxView.clicks()
from the RxBinding library inside an RxPresenter
. Because the RxPresenter
has the handy add(subscription)
method, it seemed fitting to write something in onTakeView()
like
add(RxView.clicks(getView().getActionReload()).subscribe(new ReloadSubscriber()));
But then i noticed the subscription will only get auto-destroyed if onDestroy()
in RxPresenter
is called. But this is not the case when you rotate the device, because the presenter is cached and so any subscriptions are not cancelled. This would leak the Activity through the listener which is added with RxView.click()
.
I would like to hear your input on how to use Nucleus with Observables that are tied to their Activities.
My current predicament is one where an item is selected in activity A, moving to activity B, where the itemId is passed via an intent from activity A to B. Pre-2.0 it was possible to simply execute any call in the onTakeView method getting the itemId from the activity, but now this seems like it would less practical (calling both start and restartable in the onTakeView method seems unnecessary). What is the best practice for passing data between presenters/activities in 2.0?
I have some DIalogFragments where I want to enforce MVC. Can the library add this functionallity?
I think it should work if all code from Fragment is just copied to DialogFragment as it inherits from it.
Much like how an Activity will throw an exception if super.onCreate(bundle) is not called, a presenter should do the same if super.onTakeView(view) is not called. Failing to call the super method means that no deliver*() calls will ever hit onNext(). For an such easily missable mistake with such a large impact, it seems reasonable to just throw an exception instead of failing silently. Thoughts?
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.