DelegationActivityTemplate Save

The sample project that demonstrate how to combine MVP and Delegation pattern to clean your Activity. The presenter usually contains business logic and we can use a delegate to init and get a value from a service, broadcast receiver or manager that depends on Android SDK.

Project README

Clean Your Activity Using Delegation Pattern

Read article here

Whenever we add Navigation Drawer to our app, we face a lot of boilerplate code that pollutes the Activity. It’ll further detract from main logic and can lead to over-proliferation of class. Side menu is a good design pattern for navigation, and most large apps use it. That’s why I decided to create template project based on MVP to solve this problem.

This project contains base classes for MVP architecture and you can use it as template.

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity
        implements BaseView {

    private Unbinder mUnBinder;

    private ProgressDialog mProgressDialog = null;

    protected @NonNull abstract P getPresenterInstance();

    protected P mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = getPresenterInstance();
        mPresenter.attachView(this);
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        super.setContentView(layoutResID);
        mUnBinder = ButterKnife.bind(this);
    }

    @Override
    protected void onDestroy() {
        mPresenter.detachView();
        mUnBinder.unbind();
        super.onDestroy();
    }
    
    @Override
    public View getContentView() {
        return getWindow().getDecorView();
    }
}

Base Presenter:

public interface BasePresenter<V extends BaseView> {

    void attachView(V view);

    void detachView();

}

Helpful simple implementation of base Presenter:

public class BasePresenterImpl<V extends BaseView> implements BasePresenter<V> {

    protected V mView;

    @Override
    public void attachView(V view) {
        mView = view;
    }

    @Override
    public void detachView() {
        mView = null;
    }

    public V getView() {
        return mView;
    }

}

Base view:

public interface BaseView {

    void showProgress();

    void hideProgress();

    View getContentView();

}

You can check out all these classes in base package of the template. It demonstrates a basic Model‑View‑Presenter (MVP) architecture and provides a foundation on which the sample is built. I also recommend you visit Android Architecture Blueprints for more details.

Delegation pattern

To reduce the amount of code in Activity, we will use the Delegation pattern. The point is that an instance handles a request by delegating to a second object (the delegate). Also, we have to keep in mind the fact that we deal with Android and adapt our Delegate class to Activity Lifecycle.

This project contains base classes which will handle main challenges:

public abstract class BaseDelegationActivity<
        V extends BaseView,
        P extends BasePresenterImpl<V>,
        D extends BaseActivityDelegate<V, P>>
        extends BaseActivity<P> {

    protected D mDelegate;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDelegate = instantiateDelegateInstance();
        mDelegate.onCreate(mPresenter);
    }

    protected abstract D instantiateDelegateInstance();

    @Override
    protected void onDestroy() {
        mDelegate.onDestroy();
        super.onDestroy();
    }
}

And base class of delegates:

public abstract class BaseActivityDelegate<
        V extends BaseView,
        P extends BasePresenterImpl<V>> {

    private Unbinder mUnBinder = null;

    protected P mPresenter;

    public void onCreate(P presenter) {
        mPresenter = presenter;
        mUnBinder = ButterKnife.bind(this, mPresenter.getView().getContentView());
    }

    public void onDestroy() {
        mUnBinder.unbind();
    }
}

Inheritors of this BaseDelegationActivity will delegate tasks to heirs of BaseActivityDelegate. Let’s take a look at the result:

public class NavigationDrawerDelegate extends BaseActivityDelegate<
        NavigationDrawerContract.NavigationDrawerView,
        NavigationDrawerPresenter> implements NavigationView.OnNavigationItemSelectedListener {

    @BindView(R.id.drawer_layout)
    protected DrawerLayout mDrawerLayout;
    @BindView(R.id.toolbar)
    protected Toolbar mToolBar;
    @BindView(R.id.nav_view)
    protected NavigationView mNavigationView;

    @Override
    public void onCreate(NavigationDrawerPresenter presenter) {
        super.onCreate(presenter);
        configureDrawer();
    }

    private void configureDrawer() {
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                mPresenter.getView().getActivity(),
                mDrawerLayout,
                mToolBar,
                R.string.navigation_drawer_open,
                R.string.navigation_drawer_close);
        mPresenter.getView().getActivity().getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        mDrawerLayout.addDrawerListener(toggle);
        toggle.syncState();
        mNavigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.nav_camera:
                mPresenter.getView().openCamera();
                break;
            case R.id.nav_gallery:

                break;
            case R.id.nav_slideshow:

                break;
            case R.id.nav_manage:

                break;
            case R.id.nav_share:

                break;
            case R.id.nav_send:
                mPresenter.doSomething();
                break;
        }
        closeDrawer();
        return true;
    }
    
    public boolean closeDrawer() {
        if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
            mDrawerLayout.closeDrawer(GravityCompat.START);
            return true;
        } else {
            return false;
        }
    }
}

As we can see, all logic of Navigation Drawer is contained in NavigationDrawerDelegate.

public class NavigationDrawerActivity extends BaseDelegationActivity<
        NavigationDrawerContract.NavigationDrawerView,
        NavigationDrawerPresenter,
        NavigationDrawerDelegate>
        implements NavigationDrawerContract.NavigationDrawerView {

    @BindView(R.id.toolbar)
    protected Toolbar mToolbar;

    public static Intent newIntent(Context context) {
        return new Intent(context, NavigationDrawerActivity.class);
    }

    @Override
    protected NavigationDrawerDelegate instantiateDelegateInstance() {
        return new NavigationDrawerDelegate();
    }

    @NonNull
    @Override
    protected NavigationDrawerPresenter getPresenterInstance() {
        return new NavigationDrawerPresenter();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_navigation_drawer);
        setSupportActionBar(mToolbar);
        super.onCreate(savedInstanceState);
    }

    @Override
    public NavigationDrawerActivity getActivity() {
        return this;
    }

    @OnClick(R.id.fab)
    public void onFabClick(View view) {
        Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show();
    }

    @Override
    public void onBackPressed() {
        if (!mDelegate.closeDrawer()) {
            super.onBackPressed();
        }
    }

    @Override
    public void onSomethingDone() {
        Snackbar.make(getContentView(), "Done", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show();
    }

    @Override
    public void openCamera() {
        Snackbar.make(getContentView(), "open camera", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show();
    }
}

We’ve succeeded and our Activity is cleaner.

Conclusions

Activity is a sample of God object anti-pattern, that’s why the problem of over-proliferation is actual. We considered using Delegation pattern to move a part of code into a separate class and created template project. You can check out article for more details.

Open Source Agenda is not affiliated with "DelegationActivityTemplate" Project. README Source: KucherenkoIhor/DelegationActivityTemplate

Open Source Agenda Badge

Open Source Agenda Rating