MVPHulk is an MVP framework wrapped with the latest technology RxJava+Rxlifecycle+Okhttp+Retrofit+Dagger

The main function

1. Interface multi-state and single-state return value configurations are supported. Support server address configuration; 2. Support and AndroidX versions 3. Support single Activity, list the Activity list, single fragments, fragments, single DialogFragment, combining BaseRecyclerViewAdapterHelper + SmartRefreshLayout encapsulation, Let’s just focus on the implementation of the business logic; For complex service scenarios, the interface can be extended based on the base class. 4. Different interface states can be displayed and operations can be performed. 5. Support template development, MVPHulkTemplate allows developers to reduce repeated operations, free hands

portal

1.MVPHulk hands-on documentation

2.MVPHulkTemplate uses documentation

3.MVPHulk source download

Simple use case

1. Set dependencies

1. Build. Gradle for the project

allprojects {
    repositories {
        maven { url "https://jitpack.io" }
        google()
        jcenter()
    }
}
Copy the code

2. The build of the app. Gradle

Add it under Android

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
   }
Copy the code
/ / 1.0.0 API'com. Madreain: hulk: 1.0.0'/ / androidx version of the API'com. Madreain: hulk: 1.0.0 - andx'

//dagger2
annotationProcessor rootProject.ext.dependencies["dagger2-compiler"]
annotationProcessor rootProject.ext.dependencies["dagger2-android-processor"]
//butterknife
api rootProject.ext.dependencies["butterknife"]
annotationProcessor rootProject.ext.dependencies["butterknife-compiler"]
//arouter
annotationProcessor rootProject.ext.dependencies["arouter-compiler"]
Copy the code

2. Configure Application to inherit HulkApplication

⚠ ️ note: Due to the large number of third-party libraries involved, the number of dex methods is limited to 65535, which is the famous 64K(64*1024) event. MultiDex is introduced to solve this problem. To create the Application, change the name of the Application in androidmanifest.xml

Remember to change the application style

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowBackground">@color/windowBackground</item>
        <item name="colorControlNormal">@color/windowBackground</item>
        <item name="colorControlActivated">@color/colorAccent</item>
    </style>
Copy the code

Glide reference, need to create ids.xml

<? xml version="1.0" encoding="utf-8"? > <resources> <item name="glide_tag" type="id" />
</resources>
Copy the code

3. Combine Dagger2 with MVP

Build. Gradle app needs to introduce dagger2 libraries. – This has been configured in the project configuration introduction, please refer to this part

1.)BuilderModule creation (all activities and fragments need to be registered here) (⚠️ note: I put the package name under the Demo, I will use the Template in the project development)

Reference code

@module public abstract Class BuilderModule {// All activities and fragments are registered here}Copy the code

2.) Appcomponent creation

Reference code

@Singleton @Component(modules = {AndroidSupportInjectionModule.class, ApiModule.class, Buildermodule. class}) public Interface Appcomponent extends IAppComponent {//HulkUnionApplication is an extension of HulkApplication void inject(HulkUnionApplication mvpHulkApplication); Retrofit getRetrofit(); }Copy the code

⚠️ Note: DaggerAppComponent is generated by a make project

3.) and inject initialization code. The app level of course is initialized in the application

Reference code

public class HulkUnionApplication extends HulkApplication {

    private static Appcomponent appcomponent;

    public static Appcomponent getAppcomponent() {
        return appcomponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        MultiDex.install(this);
    }

    @Override
    public void initHulkConfig() {/ / DaggerAppComponent generation make project once line appcomponent = DaggerAppComponent. Builder () apiModule (new com.madreain.hulk.application.ApiModule()).build(); }}Copy the code

4. Configure HulkConfig

1.) In config.gradle, set parameters for enabling logging, enabling switching environment, and BASEURL

Config. gradle parameters

Reference code

OPEN_LOG = true
OPEN_CHANGE = true
BASE_URL = " http://www.mxnzp.com"
CODE_SUCCESS = "1"CODELIST_SUCCESS ="1, 0"
Copy the code

⚠️ Note: priority: CODE_SUCCESS>CODELIST_SUCCESS, only one of these needs to be set for the project

These three parameters are defined in the build.gradle app, defaultConfig for Android

Reference code

// Define the network request success return code baseurl log print switch environment in the code buildConfig. BASE_URL to use buildConfigField"String"."CODE_SUCCESS", getCodeSuccess()
buildConfigField "String"."CODELIST_SUCCESS", getCodeListSuccess()
buildConfigField "String"."BASE_URL", getBaseUrl()
buildConfigField "boolean"."OPEN_LOG", getOpenLog()
buildConfigField "boolean"."OPEN_CHANGE", getOpenChange()
Copy the code
The android equivalent creates the related three methods above
def getOpenLog() {
    return "${OPEN_LOG}"
}

def getOpenChange() {
    return "${OPEN_CHANGE}"
}

def getBaseUrl() {
    return "\" " + String.valueOf(BASE_URL).trim() + "/ \" "
}

def getCodeSuccess() {
    return "\" " + String.valueOf(CODE_SUCCESS)+ "\" "
}

def getCodeListSuccess() {
    return "\" " + String.valueOf(CODELIST_SUCCESS)+ "\" "
}
Copy the code
After the configuration is complete, rebuild or clean it

2.) Associated interceptors for network requests

Request header interception

Reference code

Public class RequestHeaderInterceptor implements Interceptor {// Add @override public Response based on your project intercept(@NonNull Chain chain) throws IOException { Request request = chain.request(); Request authorised; Headers headers = new Headers.Builder() .add("test"."test")
                .build();
        authorised = request.newBuilder().headers(headers).build();
        returnchain.proceed(authorised); }}Copy the code
Abnormal state interception (kick scenario)

Reference code

Public class SessionInterceptor implements IReturnCodeErrorInterceptor {/ / and the related parameters in the interface definition and kick back, Override public Boolean Intercept (String code) {Override public Boolean Intercept (String code) {Override public Boolean Intercept (String code)return "100".equals(code);
    }

    @Override
    public void todo(IView iView, String returnCode, String msg) {

    }

}
Copy the code
Message interceptor

Reference code

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
Copy the code

⚠️ Note: You can set up a variety of interceptors for extension, see OkHttp interceptor

3.) Default placeholder, default avatar placeholder

The HulkApplication configuration item is complete, here also set Glide, ARouter, SmartRefreshLayout, complete code reference below

public class HulkUnionApplication extends HulkApplication {

    private static Appcomponent appcomponent;

    public static Appcomponent getAppcomponent() {
        return appcomponent;
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }

    @Override
    public void initHulkConfig() {/ / DaggerAppComponent generation make project once line appcomponent = DaggerAppComponent. Builder () apiModule (new com.madreain.hulk.application.ApiModule()).build(); HttpLoggingInterceptor logging = new Httploggingtor (); logging.setLevel(HttpLoggingInterceptor.Level.BODY); Build ().setApplication(this).setretSuccess (buildconfig.code_success .setRetSuccessList(BuildConfig.CODELIST_SUCCESS) .setBaseUrl(BuildConfig.BASE_URL) .setChangeBaseUrl(BuildConfig.OPEN_CHANGE) .setOpenLog(BuildConfig.OPEN_LOG) .addOkHttpInterceptor(new RequestHeaderInterceptor())// RequestHeaderInterceptor.addokhttpinterceptor (buildconfig.open_log, Md.addretcodeinterceptor (new SessionInterceptor())//returnSetGlidePlaceHolder (new ColorDrawable(color.parsecolor ());"#ffffff"))) / / the default placeholder figure - color. SetGlideHeaderPlaceHolder (getResources (). GetDrawable (R.m ipmap. Ic_launcher)) / / the default image placeholder figure .setRetrofit(appcomponent.getRetrofit()) .build(); appcomponent.inject(this); // Application context utils.init (this); //Glide sets the tag viewTarget. setTagId(R.i.D.glide_tag); //logDay switch initLog(); //ARouter initArouter(); } private voidinitArouter() {// initialize ARouter. Init (this); } private voidinitLog() {// Test the environmentif(BuildConfig.OPEN_LOG) { ARouter.openLog(); // Prints the log arouter.openDebug (); // Enable debug mode (if running in InstantRun mode, debug must be enabled! Online version needs to be closed, otherwise there are security risks)}} / / static code can prevent memory leaks static {/ / enable vector figure compatible AppCompatDelegate. SetCompatVectorFromResourcesEnabled (true); / / set the global Header builder SmartRefreshLayout. SetDefaultRefreshHeaderCreator (newDefaultRefreshHeaderCreator() { @Override public RefreshHeader createRefreshHeader(Context context, RefreshLayout layout) { layout.setPrimaryColorsId(R.color.colorPrimary, android.R.color.white); // Set the theme color globallyreturnnew ClassicsHeader(context); //.setTimeFormat(new DynamicTimeFormat("Update at %s")); // Specify classic Header, default is Bezier radar Header}}); / / set the global Footer builder SmartRefreshLayout. SetDefaultRefreshFooterCreator (newDefaultRefreshFooterCreator() {@override public RefreshFooter createRefreshFooter(Context Context, RefreshLayout layout) { The default is BallPulseFooterreturnnew ClassicsFooter(context).setDrawableSize(20); }}); }}Copy the code

5. Create the ApiService interface (in Demo, it is placed under the module/ API of the package name, because the code will be generated with Template, so it is recommended to use ApiService name)

public interface ApiService {

    @GET("api/address/list")
    Flowable<BaseRes<List<CityListListData>>> getCityList();

}
Copy the code

The ARouterUri class is created and stored in the path of the ARouter.ARouterUri is the key used for data transfer

public class ARouterUri {

    public static final String MainActivity = "/mvphulk/ui/MainActivity";

}

Copy the code

Next, we’ll really get into the business development phase

7. Use HulkTemplate to generate corresponding single Activity, single Fragment, single DialogFragment, ListActivity, and ListFragment

MVPHulkTemplate Usage Guide

8. Remember to register the files generated in step 6 in BuilderModule

@Module
public abstract class BuilderModule {

    @ContributesAndroidInjector(modules = MainModule.class)
    abstract MainActivity mainActivity();

    @ContributesAndroidInjector(modules = HomeModule.class)
    abstract HomeFragment homeFragment();

}
Copy the code

9.ARouter redirects routes and transmits parameters

Refer to the official ARouter user Guide

The Activity needs to set a route

@Route(path = ARouterUri.CityListActivity)
Copy the code

10. Request interface

1.) Add the permission related to the request interface

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Copy the code

2.) Add parameters related to the object

Keep in mind that @keep keeps classes and elements from being confused

@Keep
public class CityListListData {

    private String code;
    private String name;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) { this.name = name; }}Copy the code

3.)ApiService Configures related interfaces

Refer to the following

public interface ApiService {

    @GET("api/address/list")
    Flowable<BaseRes<List<CityListListData>>> getCityList();

}
Copy the code

⚠️ Note: In the project, GET, HEAD, POST, DELETE, PUT, and DELETE are recommended to follow the restful style

4.) The corresponding Model inherited from BaseModel adds the method called by the interface

Refer to the following

public class CityListModel extends BaseModel<ApiService> implements CityListContract.Model {

    @Inject
    public CityListModel() {
        super();
    }

    @Override
    public Flowable<BaseRes<List<CityListListData>>> loadListDatas(int pageNo) {
        returnapiService.getCityList(); // Interface call apiService. XXX}}Copy the code

5.) Inherits the BasePresenter corresponding Presenter release corresponding method

Refer to the following

public class CityListPresenter extends BasePresenter<CityListModel, CityListContract.View> {

    @Inject
    CityListPresenter() {

    }

    @Override
    public void onStart() {
        loadPageListData(1);
    }

    @Override
    public void onDestroy() {
        model.onDestroy();
    }

    public void loadPageListData(final int pageNum) {
        model.loadListDatas(pageNum)
                .compose(Transformer.retrofit(view))
                .subscribeWith(new RSubscriberList<List<CityListListData>>(view, pageNum) {
                    @Override
                    public void _onNext(List<CityListListData> datas) {
                        view.showListData(datas, pageNum);
                    }

                    @Override
                    public void retry() { loadPageListData(pageNum); }}); }}Copy the code

⚠️ Note: RSubscriberList (for List interface, more can be refreshed and loaded), RSubscriber (for single interface)

Transformer retrofit (BaseRes), retrofitBaseRes (interface response only BaseRes, internal generic null)

6.) Adapter Settings related data display inherited from BaseAdapter

Refer to the following


public class CityListAdapter<V extends IView> extends BaseAdapter<CityListListData, CityListActivity> {

    @Inject
    public CityListAdapter() { super(R.layout.item_activity_city_list, new ArrayList<>()); } @Override protected void convert(HulkViewHolder helper, CityListListData data) { helper.setText(R.id.tv, data.getName()); }}Copy the code

7.) The Activity class Settings inherited from BaseListActivity

Set whether or not refresh loading is possible, either by default


    setLoadMoreEnable(true);
    setRefreshEnable(true);
    
Copy the code

The complete code for the Activity class is shown below

@Route(path = ARouterUri.CityListActivity)
public class CityListActivity extends BaseListActivity<CityListPresenter, CityListAdapter<CityListActivity>, CityListListData> implements CityListContract.View {

    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.smartRefreshLayout)
    SmartRefreshLayout smartRefreshLayout;
    @BindView(R.id.recyclerView)
    RecyclerView recyclerView;

    @Override
    public int getLayoutId() {
        return R.layout.activity_city_list;
    }


    @Override
    public void _init(Bundle savedInstanceState) {
        setSupportActionBarWithBack(toolbar);
        toolbar.setTitle("List shows can refresh can load more");
        presenter.onStart();
    }

    @Override
    public void loadPageListData(int pageNo) {
        presenter.loadPageListData(pageNo);
    }

    @Override
    public SmartRefreshLayout getSmartRefreshLayout() {
        return smartRefreshLayout;
    }


    @Override
    public RecyclerView getRecyclerView() {
        return recyclerView;
    }

    @Override
    public RecyclerView.LayoutManager getLayoutManager() {
        return new LinearLayoutManager(this);
    }

    @Override
    public View getReplaceView() {
        returnsmartRefreshLayout; }}Copy the code

Interface calls to data display code reference CityList city

At this point, the program is running and the desired interface is displayed

Refer to related instructions for details