Skip to content

Instantly share code, notes, and snippets.

@senneco
Last active June 9, 2016 17:27
Show Gist options
  • Save senneco/c659a7022ffbab4ee970884c8c62f3ee to your computer and use it in GitHub Desktop.
Save senneco/c659a7022ffbab4ee970884c8c62f3ee to your computer and use it in GitHub Desktop.
Fix bug of mvp delegate
package com.arellomobile.mvp;
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import com.arellomobile.mvp.presenter.PresenterType;
/**
* Date: 18-Dec-15
* Time: 13:51
* <p>
* This class represents a delegate which you can use to extend Mvp's support to any class.
* <p>
* When using an {@link FixedMvpDelegate}, lifecycle methods which should be proxied to the delegate:
* <ul>
* <li>{@link #onCreate(Bundle)}</li>
* <li>{@link #onStart()}</li>
* <li>{@link #onSaveInstanceState(android.os.Bundle)}</li>
* <li>{@link #onStop()}</li>
* <li>{@link #onDestroy()}</li>
* </ul>
* <p>
* Every {@link Object} can only be linked with one {@link FixedMvpDelegate} instance,
* so the instance returned from {@link #FixedMvpDelegate(Object)}} should be kept
* until the Object is destroyed.
*
* @author Alexander Blinov
*/
public class FixedMvpDelegate<Delegated>
{
private static final String KEY_TAGS = "com.arellomobile.mvp.MvpDelegate.KEY_TAGS";
private String mKeyTags = KEY_TAGS;
private String mDelegateTag;
private final Delegated mDelegated;
private int mStateSavedCounter;
private boolean mIsAttached;
private FixedMvpDelegate mParentDelegate;
private List<MvpPresenter<? super Delegated>> mPresenters;
private List<FixedMvpDelegate> mChildDelegates;
private Bundle mBundle;
public FixedMvpDelegate(Delegated delegated)
{
mDelegated = delegated;
mChildDelegates = new ArrayList<>();
}
public void setParentDelegate(FixedMvpDelegate delegate, String childId)
{
if (mBundle != null)
{
throw new IllegalStateException("You should call setParentDelegate() before first onCreate()");
}
if (mChildDelegates != null && mChildDelegates.size() > 0)
{
throw new IllegalStateException("You could not set parent delegate when it already has child presenters");
}
mParentDelegate = delegate;
mKeyTags = mParentDelegate.mKeyTags + "$" + childId;
delegate.addChildDelegate(this);
}
private void addChildDelegate(FixedMvpDelegate delegate)
{
mChildDelegates.add(delegate);
}
/**
* <p>Similar like {@link #onCreate(Bundle)}. But this method try to get saved
* state from parent presenter before get presenters</p>
*/
public void onCreate()
{
Bundle bundle = new Bundle();
if (mParentDelegate != null)
{
bundle = mParentDelegate.mBundle;
}
onCreate(bundle);
}
/**
* <p>Get(or create if not exists) presenters for delegated object and bind
* them to this object fields</p>
*
* @param bundle with saved state
*/
public void onCreate(Bundle bundle)
{
mStateSavedCounter = 0;
mIsAttached = false;
mBundle = bundle;
//get base tag for presenters
if (bundle == null || !mBundle.containsKey(mKeyTags))
{
mDelegateTag = generateTag();
}
else
{
mDelegateTag = bundle.getString(mKeyTags);
}
//bind presenters to view
mPresenters = MvpFacade.getInstance().getMvpProcessor().getMvpPresenters(mDelegated, mDelegateTag);
for (FixedMvpDelegate childDelegate : mChildDelegates)
{
childDelegate.onCreate(bundle);
}
}
/**
* <p>Attach delegated object as view to presenter fields of this object.
* If delegate did not enter at {@link #onCreate(Bundle)}(or
* {@link #onCreate()}) before this method, then view will not be attached to
* presenters</p>
*/
public void onStart()
{
mStateSavedCounter = 0;
for (MvpPresenter<? super Delegated> presenter : mPresenters)
{
if (mIsAttached && presenter.getAttachedViews().contains(mDelegated))
{
continue;
}
presenter.attachView(mDelegated);
}
for (FixedMvpDelegate<?> childDelegate : mChildDelegates)
{
childDelegate.onStart();
}
mIsAttached = true;
}
public void onStop()
{
for (FixedMvpDelegate<?> childDelegate : mChildDelegates)
{
childDelegate.onStop();
}
}
/**
* <p>Detach delegated object from their presenters and destroy presenters if
* they are not needed.</p>
*/
public void onDestroy()
{
for (MvpPresenter<? super Delegated> presenter : mPresenters)
{
presenter.detachView(mDelegated);
}
if (mStateSavedCounter == 0)
{
destroyPresenters();
}
mStateSavedCounter = Math.max(mStateSavedCounter - 1, 0);
for (FixedMvpDelegate<?> childDelegate : mChildDelegates)
{
childDelegate.onDestroy();
}
}
/**
* Save presenters tag prefix to save state for restore presenters at future after delegate recreate
* @param outState out state from Android component
*/
public void onSaveInstanceState(Bundle outState)
{
mStateSavedCounter++;
outState.putString(mKeyTags, mDelegateTag);
for (FixedMvpDelegate childDelegate : mChildDelegates)
{
childDelegate.onSaveInstanceState(outState);
}
}
private void destroyPresenters()
{
PresenterStore presenterStore = MvpFacade.getInstance().getPresenterStore();
for (MvpPresenter<?> presenter : mPresenters)
{
if (presenter.getPresenterType() == PresenterType.LOCAL)
{
presenter.onDestroy();
presenterStore.remove(PresenterType.LOCAL, presenter.getTag(), presenter.getClass());
}
}
}
/**
* @return generated tag in format: Delegated_class_full_name$MvpDelegate@hashCode
*
* example: com.arellomobile.com.arellomobile.mvp.sample.SampleFragment$MvpDelegate@32649b0
*/
private String generateTag()
{
return mDelegated.getClass().getName() + "$" + getClass().getSimpleName()+ toString().replace(getClass().getName(),"");
}
}
package net.senneco.mvpfragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.arellomobile.mvp.FixedMvpDelegate;
/**
* Created by senneco on 09.06.2016
*/
public class MvpFragment extends Fragment
{
private Bundle mTemporaryBundle;// required for view destroy/restore
private FixedMvpDelegate<? extends MvpFragment> mFixedMvpDelegate;
public MvpFragment()
{
mTemporaryBundle = null;
}
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getMvpDelegate().onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
if (mTemporaryBundle != null)
{
getMvpDelegate().onCreate(mTemporaryBundle);
mTemporaryBundle = null;
}
return super.onCreateView(inflater, container, savedInstanceState);
}
public void onStart()
{
super.onStart();
getMvpDelegate().onStart();
}
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
getMvpDelegate().onSaveInstanceState(outState);
}
public void onStop()
{
super.onStop();
getMvpDelegate().onStop();
}
@Override
public void onDestroyView()
{
super.onDestroyView();
mTemporaryBundle = new Bundle();
getMvpDelegate().onSaveInstanceState(mTemporaryBundle);
getMvpDelegate().onDestroy();
}
@Override
public void onDestroy()
{
super.onDestroy();
getMvpDelegate().onDestroy();
}
public FixedMvpDelegate getMvpDelegate()
{
if (mFixedMvpDelegate == null)
{
mFixedMvpDelegate = new FixedMvpDelegate<>(this);
}
return mFixedMvpDelegate;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment