The application of Dagger2 in MVP
Reprint please explain source: http://blog.csdn.net/a15286856575/article/details/53405630 need basic Dagger2 MVP
Suggested that the basic society to see the following article to understand the point. Why should the MVP use Dagger2?
Let's first look at the drawbacks of a traditional MVP.
The coupling of presenter in activity
We know that in the traditional MVP Preseter is initialized in activity, that is, an explicit new object, then there is a coupling within the activity. Why is there a coupling? Scenario 1: If you use this presetner more than once in your project, now there's a requirement that this presenter relies on an object that needs to be passed in to the constructor, and we're not going to find all the objects that find all the initialization presenter and then modify it, Small projects can also if it is a big item, we are not going to find all the changes. This creates a coupling. How to solve. Daggger2 is a dependency injection framework, the current activity does not care about presenter, how it was created, and the specific creation to module. We just need to modify the module. . Scenario 2: If presenter needs object A, object A needs object B, object B needs object C, we are not first C c= new B (), B = new B (c), a A = new A (b) and then initializing presenter. In this case, whether we need to write this every time, in the activity also related to their creation order. Very cumbersome, with Dagger2 can solve this problem. We are done by injecting ourselves into the objects we need.
The coupling of model in presenter
The traditional MVP model is initialized in presenter, which is also a new object displayed. There will also be a coupling in it. Scene 1: Multiple presenter used the same model class, have the same needs, model needs to pass in an object, we are not to find the model with all the presenter, a change. There is not a coupling inside. Also we can inject model through Dagger2, in dagger2 module modify this model on the line. Scenario 2: Initialize a model, need object A, object A needs object B, object B needs object C, we are not first C c= new B (), b b = new B (c), a A = new A (b), and then initialize model. When we reuse the model, it is cumbersome to do this every time, through the creation of module in Dagger2, we can inject this model, is not very convenient.
Note that module is DAGGER2, model is in the MVP
Of course, Dagger2 is not limited to MVP, but it can be used where there is coupling. The realization of Dagger2 in MVP
Architecture idea: For both of the above cases. They can have the same module provided, then we can have a dependency system implementation, such as login, we loginmodule provide presenter and Model,logincomponent responsible for injection, on the line. But we also need a global appmodule that provides okhttpclient, Sevivce,retofit. Then let logincomponent depend on him. Then we can get all the module in the logincomponent. Figure:
We need two component: appcomponent
Appcompponet is all the global component code initialized in Applcation as follows
@Singleton
@Component (modules = {appmodule.class, clientmodule.class, servicemodule.class})
Public Interface Appcomponent {
application application ();
Apiservice Apiservice ();
}
This component is equivalent to a factory administrator who manages the Appmodule,clentmodule,servicemodule
Through him we can find the examples provided by these module. If the other component relies on this component, we need those examples provided by those module, then we need to expose those objects in appcomponent, where we expose application apiservice; Appmodule mainly provides application objects.
@Module public
class Appmodule {
private application mapplication;
Public Appmodule (Application application) {
this.mapplication = Application;
}
@Singleton
@Provides Public
Application Provideapplication () {return
mapplication;
}
@Singleton
@Provides public
Gson Providegson () {return new Gson ();}
}
Clientmodule
Mainly provides Retofit objects. which includes configuring the Retofit we Need
/** * @author V.wenju.tian * Using builder mode, modular components/@Module public class Clientmodule {private static final int
Tome_out = 10;
public static final int http_response_disk_cache_max_size = 10 * 1024 * 1024;//cache file maximum value is 10Mb private Httpurl mapiurl;
Private Globehttphandler Mhandler;
Private interceptor[] minterceptors; /** * @author: Jess * @date 8/5/16 11:03 AM * @description: Set BaseURL/Private Clientmodule (BUiD
Ler buidler) {this.mapiurl = Buidler.apiurl;
This.mhandler = Buidler.handler;
This.minterceptors = buidler.interceptors;
public static Buidler Buidler () {return new Buidler (); /** * @param Cache buffer * @param intercept Interceptor * @return * @author: Jess * @date 8/30/16 1:12 PM * @description: Provide okhttpclient * * * @Singleton @Provides okhttpclient provideclient (Cache Cach E, Interceptor Intercept) {final Okhttpclient.buildeR okhttpclient = new Okhttpclient.builder ();
Return Configureclient (okhttpclient, cache, intercept);
/** * @param client * @param httpurl * @return * @author: Jess * @date 8/30/16 1:13 PM * @description: Provide retrofit/@Singleton @Provides Retrofit Provideretrofit (okhttpclient client, Httpur
L Httpurl) {final Retrofit.builder Builder = new Retrofit.builder ();
return Configureretrofit (builder, client, httpurl);
@Singleton @Provides Httpurl Providebaseurl () {return mapiurl; /** * @param builder * @param client * @param httpurl * @return * @author: Jess * @d Ate 8/30/16 1:15 PM * @description: Configuration Retrofit * * Private Retrofit Configureretrofit (Retrofit.builder Builder , okhttpclient client, Httpurl httpurl) {return builder. BaseURL (httpurl)//domain name. cl Ient (client)/set okhttp. ADDCAlladapterfactory (Rxjavacalladapterfactory.create ())//Use Rxjava. Addconverterfactory (gsonconverterfactory.c
Reate ())//Use Gson. Build (); @Singleton @Provides Cache Providecache (File cachefile) {return new cache (Cachefile, http_response _disk_cache_max_size)/Set cache path and size}/** * Provide cache address/@Singleton @Provides File PROVIDECAC
Hefile (Application application) {return datahelper.getcachefile (application); @Singleton @Provides Interceptor Provideintercept () {return new requestintercept (Mhandler);//Print request letter /** * Configuration okhttpclient * * @param okhttpclient * * @return * * Private Okhttpclie NT Configureclient (Okhttpclient.builder okhttpclient, cache cache, Interceptor intercept) {Okhttpclient.builder Builder = okhttpclient. ConnectTimeout (Tome_out, Timeunit.seconds). ReadTimeout (Tome_out, Timeunit.seconds). cache (cached)/set caching. Addnetworkinterceptor (intercept); if (minterceptors!= null && minterceptors.length > 0) {//If an externally supplied array of interceptor is traversed add for (Intercept
or interceptor:minterceptors) {builder.addinterceptor (Interceptor);
} return to builder. Build ();
public static final class Buidler {private Httpurl Apiurl = Httpurl.parse ("https://api.github.com/");
Private Globehttphandler handler;
Private interceptor[] interceptors; Private Buidler () {} public Buidler BaseURL (String baseurl) {//Base URL if Textutils.isempty (b
Aseurl)) {throw new IllegalArgumentException ("BaseURL can not be empty");
} This.apiurl = Httpurl.parse (BaseURL);
return this;
Public Buidler Globehttphandler (Globehttphandler handler) {//used to process HTTP response results This.handler = handler;
return this; Public Buidler interceptors (interceptor[] interceptors) {//dynamically add any interceptor this.interceptors = i
Nterceptors;
return this; Clientmodule Build () {if (Apiurl = = null) {throw new illegalstateexception
("BaseURL is required");
Return to New Clientmodule (this); }
}
}
In Clientmodule, we found a way to add a @providers annotation back to Retofit.
@Singleton
@Provides
Retrofit Provideretrofit (okhttpclient client, Httpurl httpurl) {
final Retrofit.builder Builder = new Retrofit.builder ();
return Configureretrofit (builder, client, httpurl);
}
Inside you need to pass in the Okhttpclient,httpurl object. How did they initialize it? In the current clientmodule, we found another.
@Singleton
@Provides
okhttpclient provideclient (cache cache, Interceptor intercept) {
final Okhttpclient.builder okhttpclient = new Okhttpclient.builder ();
Return Configureclient (okhttpclient, cache, intercept);
}
@Singleton
@Provides
httpurl providebaseurl () {return
mapiurl;
}
In order to find the parameters to complete the initialization of the work, here is not written servicemodule is mainly to provide Apisevice objects
@Module public
class Servicemodule {
@Singleton
@Provides
apiservice providecommonservice (Retrofit Retrofit) {return
retrofit.create (apiservice.class);
}
}
Appcomponent initialization
From the above Apiservice initialization, you need a retofit, this retrofit is that since appcomponent is a global component then we need to initialize the work in Applcation, Then its life cycle is just as long as application.
First, baseapplication.
Public abstract class Baseapplication extends application {static private baseapplication mapplication;
Public linkedlist<baseactiviy> mactivitylist;
Private Clientmodule Mclientmodule;
Private Appmodule Mappmodule;
Private Servicemodule Servicemodule;
Protected final String TAG = This.getclass (). Getsimplename ();
@Override public void OnCreate () {super.oncreate ();
Mapplication = this; This.mclientmodule = clientmodule//is used to provide a single column of Okhttp and retrofit. Buidler (). BaseURL (Getbaseurl () ). Globehttphandler (Gethttphandler ()). Interceptors (Getinterceptors ()). Bu
ILD ();
This.mappmodule = new Appmodule (this);//provide application This.servicemodule = new Servicemodule ();
/** * provides the base URL to retrofit * * @return/protected abstract String getbaseurl (); Public Servicemodule Getservicemodule () {return ServicEmodule;
Public Clientmodule Getclientmodule () {return mclientmodule;
Public Appmodule Getappmodule () {return mappmodule;
/** * Here can provide a global processing HTTP response results of the processing class, * here can be a step ahead of the client to get the results of the server return, you can do some operations, such as token timeout, regain * default is not implemented, if there is a need to override this method
* * * @return/protected Globehttphandler Gethttphandler () {return null; /** * is used to provide interceptor, if additional interceptor can be provided to application implement this method * * @return/protected inte
Rceptor[] Getinterceptors () {return null; /** * Returns the context * * @return/* public static contexts GetContext () {return mapplication
;
}
}
Our Application:app.
public class App extends baseapplication{
private appcomponent appcomponent;
@Override public
void OnCreate () {
super.oncreate ();
Appcomponent = Daggerappcomponent.builder (). Clientmodule (Getclientmodule ()). Appmodule (Getappmodule ()). Servicemodule (Getservicemodule ()). build ();
@Override
protected String Getbaseurl () {return
API. Base_url;
}
Public Appcomponent getappcomponent () {return
appcomponent;
}
}
Here we have completed the initialization of the appcomponent. From this we can see that app we can get appcomponent through getappcomponent; logincomponent
@ActivityScope
@Component (modules = Loginmodule.class,dependencies = appcomponent.class) Public
interface logincomponent {public
void inject (mainactivity activity);
}
This component is the login's factory administrator, as can be seen from the diagram, it not only manages the loginmodule, but also relies on appcomponent, means that he can provide Application,apiservice,loginmodel, Logincontract.view (This is passed in through the construction method) to complete the loginpresenter injection. Of course we are injected through the @inject construction method. I don't understand. Please see several injection ways of Dagger2. Loginmodule
Note that it is a module in Dagger2, which is the point, as you can see from the diagram that it provides view and model, see the code, the initialization process is logincontract so that we passed through the construction method. And Apiservice is available with the last appcompoent. Then it completes the initialization of the Logincontract.model.
@Module public
class Loginmodule {
private logincontract.view View;
Public Loginmodule (Logincontract.view view) {
This.view = view;
}
@ActivityScope
@Provides
logincontract.view providercontract () {
return View;
}
@ActivityScope
@Provides
logincontract.model providermodel (apiservice service) {
return new Loginmodel (service);
}
Loginmodel
Note that it is our MVP model, we first analysis, in the Loginmodel we want to network request, the inevitable need for apiservice, and Apisevice, is not we in Servicemodule, is not already initialized well, take to use just fine. The code is as follows:
Basemodel
public class Basemodel {public
Basemodel () {
}
private Apiservice apiservice;
Public Basemodel (Apiservice apiservice) {
this.apiservice = Apiservice;
}
Public Apiservice Getapiservice () {return
apiservice;
}
}
Loginmodel
public class Loginmodel extends Basemodel implements Logincontract.model {public
Loginmodel (Apiservice service) { C1/>super (service);
}
@Override Public
observable<_user.loginresult> Login (string name, string password) {
_user User = New _ User ();
User.setusername (name);
User.setpassword (password);
Return Getapiservice (). Login (user). Compose (Rxsrxschedulers.<_user.loginresult>io_main ());
}
We need apiservice to make the networking request, so we're going to pass in a Apiservice object. We initialized this loginmodel in the Loginmodule.
-Loginpresenter
First, Basepresenter.
public class Basepresenter<m extends Imodel, V extends iview> implements Ipresenter {
protected final String TAG = This.getclass (). Getsimplename ();
protected M Mmodel;
protected V Mview;
Public Basepresenter () {
} public
Basepresenter (M model, V mview) {
This.mmodel = model;
This.mview = Mview;
OnStart ();
}
Public M Getmmodel () {return
mmodel;
}
Public v. Getmview () {return
mview;
}
Public Basepresenter (V rootview) {
this.mview = Rootview;
OnStart ();
}
public void OnStart () {
}
Loginpresenter
@ActivityScope public class Loginpresenter extends Basepresenter<logincontract.model,
logincontract.view> {@Inject public loginpresenter (Logincontract.model Model, Logincontract.view mview) {
Super (model, Mview); public void Login (string name, string password) {Getmmodel (). Login (Name,password). Subscribe (New Action1<
;_user.loginresult> () {@Override public void call (_user.loginresult loginresult) {
LOG.E (TAG, "call () called With:loginresult = [" + Loginresult + "]);
Getmview (). loginsucess ();
}, New Action1<throwable> () {@Override public void call (Throwable throwable) {
LOG.E (TAG, "call () called with:throwable = [" + Throwable + "]);
Getmview (). loginfailed ();
}
}); }
}
We see that we have completed the initialization of Loginpresenter through the @inject construction method. How the Loginpresenter is initialized. When the construction method is initialized with two parameters Logincontract.model and Logincontract.view, we know that the initialization of the two parameters in Loginmodule completes the Loginpresenter initialization.
Mainactivity
The target class is the destination we're going to inject.
First look at baseactivity.
Public abstract class Baseactiviy <p extends basepresenter> extends appcompatactivity{
@Inject
protected p mpresenter;
Private APP Application;
@Override
protected void onCreate (@Nullable Bundle savedinstancestate) {
super.oncreate (savedinstancestate) ;
Application = ((APP) getapplication ());
Setcontentview (Getcontentviewid ());
Componentinject (Application.getappcomponent ());//Dependency Injection
initdata ();
}
protected abstract void Componentinject (Appcomponent appcomponent);
The Componentinject (appcomponent appcomponent) method passed the appcomponent we needed;
Mainactivity
@Override
protected void Componentinject (Appcomponent appcomponent) {
daggerlogincomponent.builder (). Appcomponent (appcomponent). Loginmodule (New Loginmodule (This)). Builds (). inject (this);
The initialization of the logincomponent is done in this method and injected into the target class. Specific injection process
The first is the @Inject in the baseactivity.
Protected P mpresenter At this point, find the corresponding loginpresenter in Loginmodule, and if not, look for the corresponding construction method, and we don't have all the construction methods that have the @inject annotation added.
Find the construction method inside has two parameter logincontract.model,logincontract.view, then initialize these two objects also know Loginmodule
@Inject public
Loginpresenter (Logincontract.model model, Logincontract.view Mview) {
super (model, Mview);
}
In Loginmodule we see that Logincontract.view is passed through the construction method, and we are in the process of initializing logincomponent
Daggerlogincomponent.builder (). Appcomponent (appcomponent). Loginmodule (New Loginmodule (this)). Build (). Inject ( This