konmik / nucleus

Nucleus is an Android library, which utilizes the Model-View-Presenter pattern to properly connect background tasks with visual parts of an application.
MIT License
1.97k stars 253 forks source link

Q/A (was: Some doubts) #42

Open mheras opened 9 years ago

mheras commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

@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 commented 9 years ago

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

konmik commented 9 years ago

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 commented 9 years ago

@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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

This is what I was looking for. Thanks!

gautamjain commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

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

kshivaram commented 8 years ago

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

nucleus

inorichi commented 8 years ago

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

konmik commented 8 years ago

@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 commented 8 years ago

Great thanks! it seems to do the trick

bejibx commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

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

konmik commented 8 years ago

@bejibx You can't save background task arguments then, so you can't restart those tasks with Nucleus.

duo2005duo commented 8 years ago

@konmik Take downloader for example,when the user press home button in a activity with a downloading progressbar ,you have to start a foreground to push noifications to indicate the current progress and the service will keep the process going on.The process will not be killed for saving memory when there is a foreground service in the process. In my opinion,the long pieriods of task should be place in the service and you do not need to restart tasks when process reboots. I'm talking about the reboot of the process but not the recreate of the activity. In the source of Nucleus,you regist a lot of restarable task and put the startId into the ”savedInstanceState“ which make the task restart if process reboot. I think there is no need to do so becasue long pieriods of task always come with foreground service.

konmik commented 8 years ago

@duo2005duo sure, if a task is going to live longer than activity it is a good idea to put it into a service. But if it doesn't it is better to have it restartable.

I think you may probably do not get what happens with background tasks when your app is in the background and Android decides to free some memory. There is a clear statement about this in the project readme and in the article. I tried to be as clear as possible, put it looks like some people just do not believe that Android works like that. You may play around with lifecycles and see it yourself. Restarting background tasks is the only reliable solution to Android lifecycles.

duo2005duo commented 8 years ago

@konmik I had read the readme before. if a task is going to live shorter than activity,it is going to live shorter than process.Is there anything wrong?So startable can work for activity-recreate and process-reboot,but there is no need to make it work during process-reboot because we use service instead,if process-reboot may be going to happen during the task,service is a better choice to be the holder of task.

konmik commented 8 years ago

@duo2005duo Activity life is longer than life of its process!

duo2005duo commented 8 years ago

@konmik Oh, I make a mistake,I think I get it. Thanks a lot!!!

duo2005duo commented 8 years ago

another question: In your demo

@RequiresPresenter(MainPresenter.class)
public class MainActivity extends NucleusActivity<MainPresenter>

I think the following code blocks are better if we do not use Dagger,which separate P from V

@RequiresPresenter(MainPresenter.class)
public class MainActivity extends AbstractMainActivity //abstract
...
public abstract class AbstractMainActivity extends NucleusActivity<AbstractMainPresenter>

Do you think so? If you think so,how to separate P from M in Nucleus in this way?

cavarzan commented 8 years ago

@duo2005duo I think that you can use the AbstractMainPresenter and return any implementation in getPresenterFactory() method override. Without use @RequiresPresenter

duo2005duo commented 8 years ago

@cavarzan Yes,but what's the benifit about using getPresenterFactory() instead of @RequiresPresenter ?

cavarzan commented 8 years ago

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

konmik commented 8 years ago

I would write something like this instead:

public abstract class AbstractActivity<P extends AbstractPresenter> extends NucleusActivity<P>
duo2005duo commented 8 years ago

@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 commented 8 years ago

@duo2005duo You don't need the type in AbstractPresenter

duo2005duo commented 8 years ago

@cavarzan @konmik Thanks

duo2005duo commented 8 years ago

@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 commented 8 years ago

@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 commented 8 years ago

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

duo2005duo commented 8 years ago

@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 commented 8 years ago

@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 commented 8 years ago

@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 commented 8 years ago

@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 commented 8 years ago

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