Search code examples
androidandroid-mvp

The model layer (MVP) should use the Activity reference


How can I make a proper separation between the Model layer and the View layer, when I have an operation in the Model that needs the current activity instance?

For example, I've integrated Linkedin SDK in my Android app (written in MVP). In the auth process I have the following code snippet, when init() method's first argument type is Activity:

public void authWithLinkedin(final IAuth listener, Activity activity) {
    LISessionManager.getInstance(MyApplication.getContext()).init(activity, buildScope(), new AuthListener() {

        @Override
        public void onAuthSuccess() {
            listener.onSuccess();
        }

        @Override
        public void onAuthError(LIAuthError error) {
            listener.onError();
        }

    }, true);
}

If my Model layer should get to know Android framework components, what options do I have left to preserve the MVP architecture clean?


Solution

  • You can use software conventions / principles like

    "dependency inversion principle"

    "ports and adapters"

    Your model layer should not know about Android if you can avoid it is the point.


    Try something like this:

    Model:

    private final SocialLoginProvider socialLoginProvider;
    
    public MyModel(SocialLoginProvider socialLoginProvider) {
           this.socialLoginProvider = socialLoginProvider;
    }
    
    public void authWithLinkedin(final IAuth listener) {
           socialLoginProvider.init(buildScope(), new SocialLoginProvider.Listener() {
    
            @Override
            public void onAuthSuccess() {
                listener.onSuccess();
            }
    
            @Override
            public void onAuthError() {
                listener.onError();
            }
    
        }, true);
    }
    

    Factory:

    public MyModel getModel(Context context) {
        LISessionManager li = LISessionManager.getInstance(context);
        SocialLoginProvider provider = new LinkedInSocialLoginProvider(context, li);
        return new MyModel(provider);
    }
    

    Interface:

    public interface SocialLoginProvider {
         void init(Scope scope, Listener listener);
    
         interface Listener {
               void onAuthSuccess();
               void onAuthError();
         } 
    }
    

    Adapter for SocialLoginProvider:

    public class LinkedInSocialLoginProvider implements SocialLoginProvider {
    
              private final Context context;
              private final LISessionManager linkedInSessionManager;
    
              public LinkedInSocialLoginProvider(Context context, LISessionManager linkedInSessionManager) {
                   this.context = context;
                   this.linkedInSessionManager = linkedInSessionManager;
              }
    
              @Override
              public void init(Scope scope, Listener listener) {
                  linkedInSessionManager.init(context, scope, 
                        new AuthListener() {
                          @Override
                          public void onAuthSuccess() {
                             listener.onSuccess();
                          }
    
                          @Override
                          public void onAuthError(LIAuthError error) {
                             listener.onError();
                          }
                       }, true);
               }
    
    }