MVPHulk Android MVP 快速集成方案


MVPHulk是一款採用當前最新技術RxJava+Rxlifecycle+Okhttp+Retrofit+Dagger封裝的一個MVP框架

主要功能

1.支持接口多狀態、單狀態返回值配置;支持服務器地址配置;攔截器相關配置等
2.支持support、AndroidX兩個版本
3.支持單Activity、列表Activity、單Fragment、列表Fragment、單DialogFragment,
結合BaseRecyclerViewAdapterHelper+SmartRefreshLayout的封裝,讓我們只需要關注業務邏輯的實現;對於複雜業務場景可根據基類進行擴展
4.支持接口狀態不同顯示不同狀態界面和可執行操作,支持自定義狀態界面,可集成(lottie)[https://github.com/airbnb/lottie-android]讓界面更酷炫
5.支持模版開發,MVPHulkTemplate讓開發者減少重複的操作,解放雙手

傳送門

1.MVPHulk上手文檔

2.MVPHulkTemplate使用文檔

3.MVPHulk源碼下載

簡單用例

1.設置依賴項

1.項目的build.gradle

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

2.app的build.gradle

在android下添加

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
   }
//1.0.0版本
api 'com.madreain:hulk:1.0.0'
//androidx版本
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"]

2.配置Application,繼承HulkApplication

⚠️注意:因爲涉及到的第三方庫比較多,dex的方法數量被限制在65535之內,這就是著名的64K(64*1024)事件,
需引入MultiDex來解決這個問題,創建好Application,記得在AndroidManifest.xml中修改application的name

記得修改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>

Glide的引用,需創建ids.xml

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

3.dagger2和mvp結合

app的build.gradle需引入相關dagger2庫,-項目配置介紹中已配置,此部分內容自行參考

1.)BuilderModule的創建(所有的activity、fragment都要在這裏進行註冊)(⚠️注意:我在Demo裏是放在了包名下面,我在項目開發中會使用到Template模版開發)

參考代碼


@Module
public abstract class BuilderModule {
    //所有的activity、fragment都要在這裏進行註冊

}

2.)Appcomponent的創建

參考代碼


@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, ApiModule.class, BuilderModule.class})
public interface Appcomponent extends IAppComponent {

    //HulkUnionApplication是繼承HulkApplication創建
    void inject(HulkUnionApplication mvpHulkApplication);

    Retrofit getRetrofit();

}

⚠️注意:DaggerAppComponent的生成make project一下就行

3.)以及注入初始化代碼。 app級別的當然在application裏面出初始化

參考代碼

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的生成make project一下就行
        appcomponent = DaggerAppcomponent.builder().apiModule(new com.madreain.hulk.application.ApiModule()).build();
    }
}

4.配置相關的HulkConfig

1.)在config.gradle中配置開啓日誌、開啓切換環境、BASEURL的相關參數

config.gradle的相關參數

參考代碼

OPEN_LOG = true
OPEN_CHANGE = true
BASE_URL = " http://www.mxnzp.com"
CODE_SUCCESS = "1"
//增刪改查返回網絡請求成功,因爲不支持集合,這裏採用,分割
CODELIST_SUCCESS = "1,0"

⚠️注意:優先級:CODE_SUCCESS>CODELIST_SUCCESS,針對項目只需要設置其中一個就行

app的build.gradle中定義這三個參數,android下的defaultConfig

參考代碼

//定義網絡請求成功返回碼 baseurl  日誌打印  切換環境  在代碼中BuildConfig.BASE_URL去使用
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()
android同級上創建上面的相關三個方法
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)+ "\""
}
設置完成後需rebuild或者clean一下

2.)網絡請求的相關攔截器

請求頭攔截

參考代碼

public class RequestHeaderInterceptor implements Interceptor {

    //統一請求頭的封裝根據自身項目添加
    @Override
    public Response 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();
        return chain.proceed(authorised);
    }
}
非正常態攔截(互踢的場景)

參考代碼

public class SessionInterceptor implements IReturnCodeErrorInterceptor {

    //和接口定義互踢的相關參數返回,然後在todo方法進行跳轉

    @Override
    public boolean intercept(String code) {
        //這裏的-100表示互踢的返回參數
        return "-100".equals(code);
    }

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

    }

}
消息攔截器

參考代碼

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

⚠️注意:可進行擴展設置多種攔截器,可參考OkHttp攔截器

3.)默認佔位圖、默認頭像佔位圖

到此爲止,繼承HulkApplication的配置項就完成了,這裏還設置了Glide、ARouter、SmartRefreshLayout,完整代碼參考如下

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的生成make project一下就行
        appcomponent = DaggerAppcomponent.builder().apiModule(new com.madreain.hulk.application.ApiModule()).build();
        //消息攔截器
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        //配置項
        HulkConfig.builder()
                .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())//請求頭攔截器
                .addOkHttpInterceptor(BuildConfig.OPEN_LOG, logging)//okhttp請求日誌開關+消息攔截器.md
                .addRetCodeInterceptor(new SessionInterceptor())// returnCode非正常態攔截器
                .setGlidePlaceHolder(new ColorDrawable(Color.parseColor("#ffffff")))//默認佔位圖--顏色
                .setGlideHeaderPlaceHolder(getResources().getDrawable(R.mipmap.ic_launcher))//默認頭像佔位圖
                .setRetrofit(appcomponent.getRetrofit())
                .build();
        appcomponent.inject(this);
        //application 上下文
        Utils.init(this);
        //Glide設置tag
        ViewTarget.setTagId(R.id.glide_tag);
        //log日子開關
        initLog();
        //ARouter
        initArouter();
    }

    private void initArouter() {
        //ARouter的初始化
        ARouter.init(this);
    }

    private void initLog() {
        //測試環境
        if (BuildConfig.OPEN_LOG) {
            ARouter.openLog();     // 打印日誌
            ARouter.openDebug();   // 開啓調試模式(如果在InstantRun模式下運行,必須開啓調試模式!線上版本需要關閉,否則有安全風險)
        }
    }

    //static 代碼段可以防止內存泄露
    static {
        //啓用矢量圖兼容
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        //設置全局的Header構建器
        SmartRefreshLayout.setDefaultRefreshHeaderCreator(new DefaultRefreshHeaderCreator() {
            @Override
            public RefreshHeader createRefreshHeader(Context context, RefreshLayout layout) {
                layout.setPrimaryColorsId(R.color.colorPrimary, android.R.color.white);//全局設置主題顏色
                return new ClassicsHeader(context);//.setTimeFormat(new DynamicTimeFormat("更新於 %s"));//指定爲經典Header,默認是 貝塞爾雷達Header
            }
        });
        //設置全局的Footer構建器
        SmartRefreshLayout.setDefaultRefreshFooterCreator(new DefaultRefreshFooterCreator() {
            @Override
            public RefreshFooter createRefreshFooter(Context context, RefreshLayout layout) {
                //指定爲經典Footer,默認是 BallPulseFooter
                return new ClassicsFooter(context).setDrawableSize(20);
            }
        });
    }
}

5.ApiService接口創建(Demo中是放在包名下的module/api下,因爲會結合Template去生成代碼,因此建議也使用ApiService命名)

public interface ApiService {

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

}

6.ARouterUri類創建,ARouter的路徑存放(Demo中是放在包名下的consts下,因爲會結合Template去生成代碼,因此建議也使用ARouterUri命名),ARouterKey用於數據傳遞的key

public class ARouterUri {

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

}

接下來,我們要真正的進入業務開發階段了

7.利用HulkTemplate生成對應單Activity、單Fragment、單DialogFragment、ListActivity、ListFragment

MVPHulkTemplate使用指南

8.第6步生成的記得在BuilderModule進行註冊

@Module
public abstract class BuilderModule {

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

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

}

9.ARouter進行路由跳轉、及其參數傳遞

參考官方文檔ARouter使用指南

Activity需設置路由

@Route(path = ARouterUri.CityListActivity)

10.請求接口

1.)添加請求接口相關權限

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

2.)補充對象的相關參數

參考如下,這裏需注意@Keep的作用,就是保持哪些類或者哪些元素不被混淆

@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;
    }
}

3.)ApiService配置相關接口

參考如下

public interface ApiService {

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

}

⚠️注意:項目中,還是建議GET、HEAD、POST、DELETE、PUT、DELETE,遵循restful風格

4.)繼承自BaseModel的對應Model增加接口調用的方法

參考如下

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

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

    @Override
    public Flowable<BaseRes<List<CityListListData>>> loadListDatas(int pageNo) {
        return apiService.getCityList();//接口調用 apiService.xxx
    }
}

5.)繼承自BasePresenter的對應Presenter放開相對應的方法

參考如下

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);
                    }

                });
    }
}

⚠️注意:RSubscriberList(適用於List界面,可刷新加載更多)、RSubscriber(適用於單界面)

Transformer中retrofit(BaseRes類中的泛型 -即有result)、retrofitBaseRes(接口響應只有BaseRes,內部的泛型爲null)

6.)繼承自BaseAdapter的Adapter設置相關數據展示

參考如下


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());
    }
}


7.)繼承自BaseListActivity的Activity類的設置

設置是否可刷新加載,默認都可以


    setLoadMoreEnable(true);
    setRefreshEnable(true);
    

Activity類完整代碼參考如下

@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展示能刷新能加載更多");
        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() {
        return smartRefreshLayout;
    }

}

接口調用到數據顯示的代碼參考CityList城市

到此爲止,程序可以跑起來,想展示的界面也能展示出來了

詳細相關使用說明參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章