Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Q/A (was: Some doubts) #42

Open
mheras opened this issue Sep 12, 2015 · 64 comments
Open

Q/A (was: Some doubts) #42

mheras opened this issue Sep 12, 2015 · 64 comments
Labels

Comments

@mheras
Copy link

mheras commented Sep 12, 2015

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?:

  1. 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?

  2. 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?

  3. 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.

@konmik
Copy link
Owner

konmik commented Sep 12, 2015

Hi!

  1. This is the default Nucleus behaviour.
  2. Presenters get destroyed on pause because it is simpler. Note that they get destroyed only during the latest onPause. All activities happen between onResume onPause so this is ok.
  3. Yes, the documentation could be better. I'm not going to improve it right now because I'm busy on another project, but. The library is very simple, so reading its code should be as easy as reading docs.

@mheras
Copy link
Author

mheras commented Sep 23, 2015

@konmik Thanks for your response!
I have another one:

  1. I'm trying to find the destroyPresenter method on NucleusLayout. Is it somewhere else now?

@mheras
Copy link
Author

mheras commented Sep 23, 2015

@konmik Something like ((ViewWithPresenter)myView).getPresenter().destroy();?

@konmik
Copy link
Owner

konmik commented Sep 23, 2015

Yes, this is how it should work now.

I use this method for fragments as well: https://github.com/konmik/nucleus/blob/master/nucleus-example-with-fragments/src/main/java/nucleus/example/main/MainActivity.java#L19

@mheras
Copy link
Author

mheras commented Sep 25, 2015

@konmik Great!
Yet another question for you:

I need the deliver and deliverLatest functionalities (actually the restartable* alternatives) that are explained in your MVP Introduction, but I cannot find them in the latest version of the library. Did you move them to another place?

I'm trying to achieve a paginated list (with a RecyclerView), but every time the view is taken by the presenter, the latest response is getting appended to the list (because I'm using restartableLatestCache). I think something similar to deliver will help me with this use case.

As always, thank you for your time.

P.S.: Please let me know if you don't feel comfortable with this issue being treated as a Q/A thread :)

@konmik
Copy link
Owner

konmik commented Sep 25, 2015

I think that paging should deserve a wiki page: https://github.com/konmik/nucleus/wiki/Paging-example

deliver and deliverLatest has been superseded by deliverLatestCache, deliverFirst and deliverReplay.

@theManikJindal
Copy link

Hey, just wanted to pop in and ask a question. I have used the deliver() transformer before. Coming into v2.0.1, I had to settle for deliverReplay(). I am observing that myObservable.compose(deliverReplay()) is emitting source observable's onError events as onNext events. Any insights into this if this is not a bug but a feature?

@konmik
Copy link
Owner

konmik commented Oct 3, 2015

Sure, this is not a bug.

When we construct a connection we don't want to create it every time we request something - we want our connections to be reused. This is why all events come in the materialized state and this is why we use split at the end of a chain.

@inorichi
Copy link
Contributor

I also have some doubts when subscribing in onTakeView method. All the examples I've seen here use onCreate, but what if you need some data from the view to make a request? (for example, an id from an activity's intent), and surviving screen rotation?

At the moment, I'm following this pattern https://gist.github.com/inorichi/158fa261f8ef7d3e7dde

Is this approach the way to go? Or did I miss something? I haven't tried with restartables in this case, is it an option?

Edit: I've edited the gist to show two options. One of them for what I've said before, and the other for reacting to user input.

PD: Thank you so much for Nucleus. Before this, I was using some kind of MVP where the presenter was doing almost everything and the code flow was getting very ugly.

@konmik
Copy link
Owner

konmik commented Oct 18, 2015

Hi,

How about writing something like this? (pseudocode)

class MyActivity {
     onCreate() {
        if (savedState == null)
            getPresenter().requestData(getIntent.getExtra());
     }

class MyPresenter {

    int id;

    onCreate(state) {
        this.id = state.id;

        registerRestartable(1, () -> api.getData(id),
            MyView::onData,
            MyView::onError);
    }

    void request(int id) {
        this.id = id;
        start(1);
    }

    Bundle save() {
         return new Bundle("id" = this.id);
    }

@inorichi
Copy link
Contributor

This is what I was looking for. Thanks!

@gautamjain
Copy link

Hello. Quick question for you:

I'm trying to create a Google Now like user interface (several different CardViews inside of a RecyclerView). I'd like each CardView to have its own presenter that is responsible for loading data (from the network, DB, etc.).

It seems like the only way to detect when a view is being recycled/rebound within an RecyclerView is to override the View.onStartTemporaryDetach() and View.onFinishTemporaryDetach() callbacks. I'm considering using those to drive the PresenterDelegate.onResume() and PresenterDelegate.onPause() events.

Do you foresee any issues with that or have any other recommendations?

@konmik
Copy link
Owner

konmik commented Nov 17, 2015

@gautamjain Hi, forsee tons of problems with the approach because card's lifecycle will be too short and they will disappear while scrilling, so you will need to keep track of all presenters to destroy them manually on exit. You will also need to manually save/restore card's state because RecyclerView does not do that.

@kushaga
Copy link

kushaga commented Jan 20, 2016

hi i am using ur library to handle , states for a list view , for instance when the data is not loaded a load screen , and when the data (via api call ) has been received then i change the layout of the activity to something say loadedLayout , but as i get the activity instance from view().subscribe(Activity ) , i am getting a null instance for the activity , is there any work around this issue (attached code)

restartableLatestCache(REQUEST_ITEMS,
new Func0<Observable>() {
@OverRide
public Observable call() {
view().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() {
@OverRide
public void call(MainActivity mainActivity) {
//i get null ref of activity here
mainActivity.onLoadView();
}
});
return MockApp.getMockApi()
.getMockData().observeOn(AndroidSchedulers.mainThread());
}
},
new Action2<MainActivity, MockData>() {
@OverRide
public void call(MainActivity activity, MockData response) {
activity.onItemsNext(response.employees);
}
},
new Action2<MainActivity, Throwable>() {
@OverRide
public void call(MainActivity activity, Throwable throwable) {
activity.onNetworkError(throwable);
}
}
);

@konmik
Copy link
Owner

konmik commented Jan 30, 2016

@kushaga Hi, you normally do not need to use view() directly, there is example you can look up into: https://github.com/konmik/nucleus/blob/master/nucleus-example/src/main/java/nucleus/example/main/MainPresenter.java

Having null from view() is absolutely normal - you get it when view gets detached (read the view() method JavaDoc). If you don't need it just use the usual RxJava filter operator.

@kshivaram
Copy link

Hi! Quick question-
I have included the library - compile 'info.android15.nucleus:nucleus-support-v7:2.0.5' in my gradle dependencies. But I am not able to import NucleusAppCompatActivity. Am I missing something or has something changed?

@konmik
Copy link
Owner

konmik commented Feb 8, 2016

@KScoder83 nothing changed. Try to re-import project or something.

@kshivaram
Copy link

I tried re-importing, but still it is not including the support libraries.

nucleus

@inorichi
Copy link
Contributor

inorichi commented Feb 8, 2016

I would just copy NucleusAppCompatActivity to your project and only include info.android15.nucleus:nucleus:2.0.5 as dependency.

@konmik
Copy link
Owner

konmik commented Feb 8, 2016

@KScoder83 What if you just invalidate cache and restart? I have nucleus in several projects, it does fine. You can also download the support aar and expect it - classes are there. Try to invalidate caches, if it does not help then delete .gradle folder and idea files and re-create project.

@kshivaram
Copy link

Great thanks! it seems to do the trick

@bejibx
Copy link

bejibx commented Mar 1, 2016

Hi, I'm using Nucleus in my project and now trying to add Dagger 2 support to it. What I can't figure out is - do I really need to use Nucleus with Dagger 2 or I just can have pretty much same functionality with dagger 2 scope mechanism?

@duo2005duo
Copy link

Hi,I think there is no need to restart the undone job when process reboots. RxJava is just some kind of thing which can replaces the AsyncTask while it cannot replace Service. RxJava should ideally be used for short operations (a few seconds at the most.) in Android .If you need to keep threads running for long periods of time, it is highly recommended you use service.So it's not possible for the process to be killed for saving memory before the job dong by RxJava is done. We should keep long threads in the service process but not in background process.I doubt my opinion.Hope to see your reply .

@konmik
Copy link
Owner

konmik commented Mar 29, 2016

Hi @duo2005duo,

How do you solve the progress bar problem without restarting background tasks?

I don't quite get why you oppose RxJava to services. RxJava is a language extension library. Service as an Android architecting solution for synchronization and background tasks with notifications. They work together without problems.

@konmik
Copy link
Owner

konmik commented Mar 29, 2016

Hi @bejibx

You can use Nucleus with Dagger easily, here is an example (Dagger 1): https://github.com/konmik/nucleus/tree/master/nucleus-example-with-tests

Dagger can't restart background tasks.

@bejibx
Copy link

bejibx commented Mar 29, 2016

How about Dagger 2? I mean, the main idea of Nucleus is to keep presenters separated from Activity lifecycle by moving them to static storage. I am trying to understand Dagger 2 right now (btw thank you for your "Snorkeling with Dagger 2" article) and it seems scopes mechanism is doing just this - "In Dagger 2 scopes mechanism cares about keeping single instance of class as long as its scope exists". So I wonder do I really need to use Nucleus in my project if I'm already using Dagger 2? Or I can get same thing with only Dagger 2 scopes mechanism? The problem is I'm not so familliar with Dagger 2 so I'm really not sure.

@konmik
Copy link
Owner

konmik commented Mar 29, 2016

@bejibx the purpose of Nucleus is to reduce application complexity by restoring background tasks together with view state, and by automatical reattachment of background tasks if they're still running.

Dagger 2 can't do this. Everything you created with dagger 1 or dagger 2 will be wiped out during a process restart.

@bejibx
Copy link

bejibx commented Mar 29, 2016

@konmic yes, you're right. But what if my presenters are stateless?

@cavarzan
Copy link
Contributor

@duo2005duo lazy initialization or need to create the presenter using dependency injection.

@konmik
Copy link
Owner

konmik commented Mar 30, 2016

I would write something like this instead:

public abstract class AbstractActivity<P extends AbstractPresenter> extends NucleusActivity<P>

@duo2005duo
Copy link

@konmik
if so,

public abstract class AbstractPresenter<P extends AbstractActivity<******>> extends Presenter<P>

And then another Java propblem happens

public abstract class AbstractActivity<P extends AbstractPresenter<******>> extends NucleusActivity<P>

**** looks like a circle.How to sovle it?

@cavarzan
Copy link
Contributor

@duo2005duo You don't need the type in AbstractPresenter

@duo2005duo
Copy link

@cavarzan @konmik Thanks

@duo2005duo
Copy link

@konmik Hi,I comes again. Is it a correct way to keep the task going on when a activity has been finished by the system?
The system can drop the activity from memory by either asking it to finish, or simply killing its process.it seems the task is still running when system ask activity to finish although the activity may not be displayed again.

@konmik
Copy link
Owner

konmik commented Apr 2, 2016

@duo2005duo we have no way on android to cancel background tasks immediately on activity finish. If you're using nucleus, it just automatically unsubscribes from data sources and the rxjava contract is that it stops working immediately or some time after.

@duo2005duo
Copy link

@konmik Yes,the task will finish as soon as possible if it unsubscribes from data sources. But it seems the task can only unsubscribe automatically when activity runs in onPause() with the state of finish.But the system can only drop the activity after onStop() by asking it to finish if the activity has been in background for a long time.

@konmik konmik changed the title Some doubts Q/A (was: Some doubts) Apr 7, 2016
@duo2005duo
Copy link

@konmik I have proved that the task is still running and nucleus never unsubscribe from data if the system ask a activity to finish ,which I think it's a waste of system resource and it will also cause OOM if too many obervers is unsubscribed .I think retained fragment should be used to hold the Presenter instead of a StaticHolder.
PS: The isFinishing() still return false when the system ask a activity to finish.So I think using retained fragment to unsubscribe from data in fragment.onDestroy() is preferred.

@konmik
Copy link
Owner

konmik commented Apr 12, 2016

@duo2005duo This is a rare use case. But you're right, we need a fix for this. I'm thinking about calling presenter's onDestroy during activity's onDestroy.

Retained fragment does not look like a valid variant because its lifecycle is not what we need to continue background tasks, see my original MVP article.

@duo2005duo
Copy link

@konmik Yes,we need to save/restore such fragment's state anyway.Using staticholder can reduce this pocedure but it seems we cannot tell the reason why the activity run into onDestroy(Configuration change or finished by system).

@droid2009der
Copy link

@konmik, Do you have any working examples of pagination? What exactly is the request.get(page) here? Is it the getItemsList request?

restartableReplay(1,
() -> pager.pages().<PageBundle<List>>concatMap(
page -> request.get(page)
.map(it -> new PageBundle<>(page, it))
.observeOn(mainThread())),
View::onAddItems,
View::onItemsError);
}

@konmik
Copy link
Owner

konmik commented Apr 21, 2016

@droid2009der Yes it is. The working example is in the wiki.

@inorichi
Copy link
Contributor

@konmik, Do you think it's worth to cache the presenter class from the annotation RequiresPresenter in a map so that reflection is used only once per view class? I can work on it if you like this.

@konmik
Copy link
Owner

konmik commented Apr 25, 2016

@inorichi Thanks for the suggestion. But I think it doesn't. The performance benefit will be less than 1 ms.

@PaulWoitaschek
Copy link

@inorichi
I'm always using the factory methods to eliminate reflection and use the constructor to set the factory.

So I have

/**
 * Base class that forces setting a presenter factory so reflection will be eliminated.
 *
 * @author Paul Woitaschek
 */
public abstract class NucleusBaseActivity<V, P extends Presenter<V>> extends NucleusAppCompatActivity<P> {

   public NucleusBaseActivity() {
      setPresenterFactory(newFactory());
   }

   @NonNull protected abstract PresenterFactory<P> newFactory();
}

@kshivaram
Copy link

@konmik, while using pagination, what is the expected behavior when an activity is restarted? Suppose I have populated my recyclerview with 4 pages of 20 items each, when the activity is restarted again, does the presenter redeliver 4 request results for the 4 different pages to the view with 20 items each?

@konmik
Copy link
Owner

konmik commented May 6, 2016

@KScoder83

When an activity is restarted due to process restoration RxPager must re-fetch only the first page.

When the activity is recreated due to config change the pager must just pass all the cached pages to the activity.

I will probably update the paging code soon, I already have a much better (simpler, flexible) implementation.

@konmik
Copy link
Owner

konmik commented May 7, 2016

I've just got some free time and implemented this: https://github.com/konmik/nucleus/tree/master/nucleus-example-real-life

Adds: Dagger 2, Butterknife, Retrolambda, Icepick, paging with a progress indicator.

@wisien92
Copy link

wisien92 commented Jun 6, 2016

@konmik how should I setup my view from presenter? right now I fetch data in onCreate in presenter then I run setUp methods in onTakeView, but I would like to make these methods called only once ... request from view would be better?

@emwno
Copy link

emwno commented Jun 10, 2016

@konmik hi. Could you tell me why you extend NucleusAppCompatActivity, when the presenter is actually for a fragment hosted by the activity?
thanks

@konmik
Copy link
Owner

konmik commented Jun 10, 2016

@wisien92 you do not setup your view from presenter. Nucleus way is absolutely opposite: your View uses presenter to fetch data. View is the King. Presenter just presents data. It does not controls View, otherwise I would call it Controller or something like that. MVC does not work well - just look at the failure of Fragments API. Fragment is a classical controller, it has even more .lifecycles than the view itself, increasing complexity instead of reducing it.

@konmik
Copy link
Owner

konmik commented Jun 10, 2016

@emwno Very often having a fragment is an unneeded complexity. Plain activities are simpler. Plain views are even more simpler. Less lifecycles = simpler = less bugs.

@emwno
Copy link

emwno commented Jun 10, 2016

@konmik say for my use-case i have 3 fragments, each of which requests data (different) from a server backend and all request types are similar. Should I use a separate presenter for each fragment? And if so, should my activity extend NucleusAppCompatActivity<?> (and what to place at the ? ).

@konmik
Copy link
Owner

konmik commented Jun 10, 2016

I would use a separate presenter for each fragment. One view - one presenter.

If you do not need a presenter for the activity itself, you don't need to extend NucleusAppCompatActivity. All view classes can work independently. I advise you to look inside them, they are very simple and must be very easy to read.

@nbsith
Copy link

nbsith commented Jun 24, 2016

Hello. I have a question about example https://github.com/konmik/nucleus/blob/master/nucleus-example/src/main/java/nucleus/example/main/MainPresenter.java

Parameter "name" from request(String name) is stored under "NAME_KEY" key in case of process restart. What happens if I call request() many times with different parameters and then process restarts?

In onCreate we will register restartableLatestCache using only latest value of "name" passed to request().
I would expect that after calling request() with different parameters, my Activity will receive all results (from calls with different parameters) after it's recreation, not only the latest one.

How it could be achieved in Nucleus?

@konmik
Copy link
Owner

konmik commented Jun 25, 2016

Hi @nbsith ,

Nucleus can have only one restartable per restartableId. If you need a custom behavior - feel free to implement your own. Core functions that allow coupling data and view are public and can be reused easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests