Sugar-快速開發安卓項目,流行框架封裝mvp + rxjava2 + retrofit2 + rxlifecycle2 + arouter...

🍯 Sugar

bintray

需求:新項目只需5分鐘接入,以後直接開擼,不用關心網絡、圖片、模式、穩定等問題,支持mvp一個activity對應多個presenter。 適用本身的纔是最好的!java


更新日誌

  • 2019-09-06 更新 1.0.1.7 圖片加載,ImageView擴展,fragment懶加載android

  • 2019-06-24 更新 1.0.1.5 anko ui註解初始化+自定義ConverterFactorygit

  • 2019-06-17 更新 kotlin-mvp temeplegithub

  • 2019-06-14 更新 kotlin接入express

  • 2019-06-05 更新 遊手好閒之使用MotionLayout實現高德地圖bottomSheets效果apache

  • 2019-05-31 更新 1.0.1.3升級到androidxjson

  • 2019-05-29 最新,新鮮temeple出爐,請拉到最後看效果api

  • ...緩存

最新修改

1.0.1.7

主要新增,圖片加載庫圖片加載,任意選擇加載引擎 打造統一的圖片加載框架,融合Glide(4.x),Fresco,不侵入業務代碼,一套API兼容兩種加載庫bash

@Override
    public AppSetting getAppSetting() {
        return getAppSettingBuilder()
                .isDebug(true)
                .build();
    }

    @Override
    public AppImageLoadSetting getImageLoadSetting() {
        return AppImageLoadSetting
                .builder()
                .imageLoaderConfig(getImageLoaderConfig())
                .isCrossFade(true)
                .errorholder(R.mipmap.ic_launcher_round)
                .placeholder(R.mipmap.ic_logo)
                .build();
    }
複製代碼

1.0.1.5

主要新增anko支持,經過註解解耦使用AnkoUi,不須要手動初始化,直接使用ankoUi

  • 頂部TYPE註解
@AnkoInject(ui = xxAnkoUi::class) 
複製代碼
  • 變量FIELD註解
@AnkoVariable
    internal var ui : xxAnkoUi? = null
複製代碼

而後可直接使用ui這個變量,以下:

  1. GankAnkoActivity
@AnkoInject(ui = GankActivityUi::class)
@Route(path = RouterPageContant.KT_ANKO)
@CreatePresenter(presenter = [GankPresenter::class])
class GankAnkoActivity : BaseAnkoActivity(), GankContract.IView {

    @PresenterVariable
    internal var mPresenter: GankPresenter? = null
    @AnkoVariable
    internal var ui : GankActivityUi? = null

    @SuppressLint("NewApi")
    override fun init(savedInstanceState: Bundle?) {
        setSupportActionBar(ui?.toolbar)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        supportActionBar?.title = "KtGankActivity"
    }

    override fun loadData() {
        mPresenter?.getFuliDataRepository("20", "1")
    }

    override fun bindData(data: MutableList<GirlsData>?) {
        val jsonStr = JSON.toJSONString(data)
        ui?.textInfo?.text = jsonStr
    }
    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when (item?.itemId) {
            android.R.id.home -> {
                finish()
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }

}
複製代碼
  1. GankAnkoFragment
@AnkoInject(ui = GankFragmentUi::class)
@CreatePresenter(presenter = [TestGankPresenter::class])
class GankAnkoFragment: BaseAnkoFragment(),  GankContract.IView {

    @PresenterVariable
    internal var mPresenter: TestGankPresenter? = null
    @AnkoVariable
    internal var ui : GankFragmentUi? = null

    override fun init(savedInstanceState: Bundle?) {

    }

    override fun initImmersionBar() {
        ImmersionBar.with(this)
                .transparentStatusBar()
                .fitsSystemWindows(true)
                .statusBarColor(R.color.colorPrimary)
                .statusBarDarkFont(true, 0.2f)
                .init()
    }



    override fun loadData() {
        mPresenter?.getFuliDataRepository("10", "1")
    }

    override fun bindData(data: MutableList<GirlsData>?) {
        val jsonStr = JSON.toJSONString(data)
        ui?.textInfo?.text = jsonStr
    }
}
複製代碼

1.0.1.4

  1. SugarHandleSubscriber 用於接口請求時候僅處理onNext(),由於在SugarRepository裏面的customObservable(Observable observable)函數裏面統一處理了錯誤異常抓取,通常狀況下不須要再處理OnError固然能夠重載函數的時候作處理
.doOnError(throwable -> {
                    LogUtils.i("doOnError------" + throwable);
                    if (mIView != null) {
                        mIView.showLoadFailed();
                    }
                    if (rxErrorHandler != null){
                       //統一異常抓取
                       rxErrorHandler.getHandlerFactory().handleError((Throwable) throwable);
                    }

                });
複製代碼
  1. 使用SugarHandleSubscriber
  • java :
@Override
    public void getFuliDataRepository(String size, String index) {

        mModel.getFuliDataRepository(size, index)
                .subscribe(new SugarHandleSubscriber<List<GirlsData>>() {
                    @Override
                    public void onNext(List<GirlsData> girlsData) {
                        mView.bindData(girlsData);
                    }
                });
    }
複製代碼
  • kotlin
mModel.getFuliDataRepository(size, index)
                .subscribe({
                    mView.bindData(it)
                })
複製代碼
  1. 1.0.1.4版本以後 Presenter不支持在Activity/Fragment後寫泛型,只支持註解
  • java:
@CreatePresenter(presenter = GankPresenter.class)
public class GankActivity extends BaseActivity implements GankContract.IView{
    @PresenterVariable
    GankPresenter mPresenter;
}
複製代碼
  • kotlin
@CreatePresenter(presenter = [GankPresenter::class])
class KtGankActivity : BaseActivity(), GankContract.IView{
    @PresenterVariable
    internal var mPresenter: GankPresenter? = null
}
複製代碼

實用到的庫(排名不分前後)

Retrofit你懂的

ImmersionBar狀態欄工具

ToastUtils 簡單實用toast

RxErroHandler rxjava異常獲取

RetrofitUrlManager retrofit動態綁定url

EasyMvp個簡單強大且靈活的MVP框架

AndroidUtilCode 強大的工具庫

RxLifecycle 爲rxjava而生你懂的

Gloading 深度解耦Android App中全局加載中、加載失敗及空數據視圖

RxJava 不解釋

RxAndroid 不解釋

RxPermissions Android runtime permissions powered by RxJava2

Okhttp 不解釋

Gson 不解釋

Timber JakeWharton大神的日誌打印工具

ARouter 阿里出的路由庫

lottie

...以及忘了加上的

使用效果

圖片有壓縮,能夠下載demo apk進行體驗 demo-debug.apk

安裝和依賴

3種選擇

一、 git clone https://github.com/wobiancao/sugar.git
    implementation project(':sugarlibrary')
    
二、 implementation 'com.wobiancao:sugarlibrary:{version}'

三、 allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
implementation 'com.github.wobiancao:sugar:{version}'
複製代碼

統一配置

建立DemoConfigure 繼承於SugarConfigure 重載相關方法便可:

public class DemoConfigure extends SugarConfigure {


    public DemoConfigure(Application application) {
        super(application);
    }

    @Override
    public ResponseErrorListener getErrorResponse() {
        return new ResponseErrorListener() {
            @Override
            public void handleResponseError(Context context, Throwable t) {
                LogUtils.i("捕獲異常---" + t.getMessage());
                ToastUtils.show("發生異常---" + t.getMessage());
            }
        };
    }

    @Override
    public int getStatusColor() {
        return R.color.colorPrimary;
    }

    @Override
    public AppHttpSetting getHttpSetting() {
        return AppHttpSetting
                .builder()
                .with(mApplication)
                //設置初始的baseUrl host
                .setBaseUrl(Gank.HOST)
                //動態修改baseUrl 具體看https://github.com/JessYanCoding/RetrofitUrlManager
                .putDomain(Wan.DOMAN, Wan.HOST)
                //是否打印網絡請求日誌 默認否
                .setHttpLog(true)
                //百度Stetho便可 網絡監測等 默認否
                .setHttpMoniter(true)
                //設置緩存時間 默認60s
                .setCacheMaxTime(65)
                //設置鏈接超時 默認20s
                .connectTimeout(20)
                //設置讀取超時 默認20s
                .readTimeout(20)
                //設置寫入超時 默認20s
                .writeTimeout(20)
                //請求header
                .addHeaderInterceptor(getHeader())
                //添加請求明文公共參數
                .addCustomHeaderInterceptor(getCustomHeader())
                //token過時等請求成功處理 通常不須要處理
//                .addExceptionInterceptor(getExceptionInterceptor())
                //其它攔截
//                .addInterceptor(xx)
//                .addNetworkInterceptor(xxx)
//                配置本身的緩存
//                .cache(xx)
                //甚至另外寫一套本身的okhttp builder 也行
//                .setOkHttpBuilder(xxx)
                .build();
    }

    @Override
    public IToastStyle getToastStyle() {
        return new ToastStyle();
    }
}

複製代碼
  • 建立DemoApplication繼承於 LibApplication < S extends SugarConfigure > 重寫initConfigure()初始化配置便可,詳見DemoApplication
public class DemoApplication extends LibApplication<DemoConfigure> {

    @Override
    protected void initConfigure() {
        mConfigure = new DemoConfigure(this);
    }

    @Override
    protected void init() {

    }

}

複製代碼

網絡請求統一配置

  • 問題:咱們使用RetrofitUrlManager 解決了retorfit動態配置baseUrl的問題,可是每一個域名或者說每一個接口返回參數封裝等的可能不統一(這種狀況通常不會出如今公司項目)好比我這個app要展現Gank.ioWanAndroid的界面,這樣就是兩個網絡請求封裝,使用sugar能夠快速解決此類問題;
  • 使用以前先看源碼SugarRepository
/**
 * @author wobiancao
 * @date 2019/5/20
 * desc :
 */
public class SugarRepository {
    /**
     * 0 沒loading 1 dialog形式  2page形式
     */
    protected final static int LOADING_TYPE_NULL = 0;
    /**
     * 0 沒loading 1 dialog形式  2page形式
     */
    protected final static int LOADING_TYPE_DIALOG = 1;
    /**
     * 0 沒loading 1 dialog形式  2page形式
     */
    protected final static int LOADING_TYPE_PAGE = 2;
    protected BaseIView mIView;

    public SugarRepository(BaseIView IView) {
        mIView = IView;
    }

    protected Observable addObservable(Observable observable) {
        if (mIView == null) {
            return null;
        }
        return customObservable(observable);
    }

    protected Observable addObservable(Observable observable, int loadingType) {
        if (mIView == null) {
            return null;
        }
        return customObservable(observable)
                .doOnSubscribe(disposable -> {
                    if (loadingType > 0) {
                        if (loadingType == LOADING_TYPE_DIALOG) {
                            mIView.showDialogLoading();
                        } else {
                            mIView.showLoading();
                        }
                    }
                });
    }

    private Observable customObservable(Observable observable) {
        return observable
                .compose(mIView.getProvider().bindToLifecycle())
                .retryWhen(new RetryWithDelay(2, 2))
                .subscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .doFinally(() -> {
                    if (mIView != null) {
                        mIView.hideDialogLoading();
                    }
                })
                .doOnNext(o -> {
                    LogUtils.e("doOnNext------" + o);
                    if (mIView != null) {
                        mIView.showLoadSuccess();
                    }
                })
                .doOnError(throwable -> {
                    LogUtils.e("doOnError------" + throwable);
                    if (mIView != null) {
                        mIView.showLoadFailed();
                    }
                });
    }
}
複製代碼
  • addObservable(Observable observable)不會使用任何loading效果, addObservable(Observable observable, int loadingType) loadingType : 0 沒loading 、1 dialog形式 、2 page形式

  • Repository首先有個契約類,RepositoryContract xxxModel爲須要增長的一個域名接口,統一配置apiService、請求函數、相應的transformer

/**
* @author wobiancao
* @date 2019-05-21
* desc :
*/
public class RepositoryContract {

   /**
    * gank.io
    */
   public interface GankModel  {
       Gank getService();
       /**
        * Transformer 須要處理api返回值包裝的加上便可
        * @param <T>
        * @return
        */
       <T> ObservableTransformer<GirlsResult<T>, T> gankTransformer();

       Observable<List<GirlsData>> getFuliDataRepository(String size, String index);
   }

   /**
    * wanandroid
    */
   public interface WanModel{
       Wan getService();
       /**
        * Transformer 須要處理api返回值包裝的加上便可
        * @param <T>
        * @return
        */
       <T> ObservableTransformer<WanResult<T>, T> wanTransformer();


       Observable<WanData> getWanArticleList(String index);
   }
}
複製代碼
/**
* @author wobiancao
* @date 2019/5/20
* desc :
*/
public class GankRepository extends SugarRepository implements RepositoryContract.GankModel {


   public GankRepository(BaseIView IView) {
       super(IView);
   }

   @Override
   public Gank getService() {
       return AppHttpClient.getInstance().initService(Gank.class);
   }


   @Override
   public <T> ObservableTransformer<GirlsResult<T>, T> gankTransformer() {

       return upstream -> upstream
               .flatMap((Function<GirlsResult<T>, ObservableSource<T>>) tGirlsResult -> {
                   if (tGirlsResult == null) {
                       return Observable.error(new HttpException("返回值爲null"));
                   }
                   if (!tGirlsResult.error) {
                       return Observable.just(tGirlsResult.results);
                   } else {
                       return Observable.error(new HttpException("接口異常"));
                   }
               });

   }

   @Override
   public Observable<List<GirlsData>> getFuliDataRepository(String size, String index) {
       return addObservable(getService()
               .getFuliData(size, index)
               .compose(gankTransformer()), LOADING_TYPE_PAGE);
   }

}

複製代碼

最後mvp建立(以後會寫相應的Template ⬅️已寫好)

  • =。=假的,接下來看代碼

WanContract

/**
 * @author wobiancao
 * @date 2019-05-21
 * desc :
 */
public class WanContract {
    public interface PView{

        void getWanArticleList(String index);
    }

    public interface IView extends BaseIView {
        /**
         * 綁定列表數據
         * @param data
         */
        void bindData(WanData data);
    }
}
複製代碼

WanPresenter

/**
 * @author wobiancao
 * @date 2019-05-21
 * desc :
 */
public class WanPresenter extends BasePresenter<WanContract.IView, WanRepository> implements WanContract.PView {

    @Override
    protected void initRepository() {
        mModel = new WanRepository(mView);
    }

    @Override
    public void getWanArticleList(String index) {
        mModel.getWanArticleList(index)
                .subscribe(new ErrorHandleSubscriber<WanData>(rxErrorHandler) {
                    @Override
                    public void onNext(WanData wanData) {
                        mView.bindData(wanData);
                    }

                });
    }


}

複製代碼
  • 就是這麼簡單,最後就是在view層如何使用了,老規矩先看代碼WanActivity
/**
 * @author wobiancao
 * @date 2019-05-21
 * desc :
 */
@CreatePresenter(presenter = WanPresenter.class)
public class WanActivity extends BaseActivity<WanPresenter> implements WanContract.IView {
    @PresenterVariable
    WanPresenter mPresenter;
    TextView mInfoView;
    Toolbar mToolbar;
    @Override
    protected int getContentView() {
        return R.layout.gank_activity_list;
    }

    @Override
    public void init(Bundle savedInstanceState) {
        mInfoView = findViewById(R.id.tv_info);
        mToolbar = findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setTitle("WanAndroid");
        }
    }

    @Override
    public void loadData() {
        mPresenter.getWanArticleList("1");
    }

    @Override
    public void bindData(WanData data) {
        String jsonStr = new Gson().toJson(data);
        mInfoView.setText(jsonStr);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case android.R.id.home:
                finish();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}
複製代碼

是的Presenter建立只須要註解便可,而且支持多個presenter

這裏要萬分感謝EasyMvp一個簡單強大且靈活的MVP框架

  • 首先,單個presenter
@CreatePresenter(presenter = WanPresenter.class)
public class WanActivity extends BaseActivity<WanPresenter> implements WanContract.IView 
複製代碼

獲取presenter變量兩種方式

一、經過註解

@PresenterVariable
WanPresenter mPresenter;
複製代碼

二、經過getPresenter()函數1.0.1.4以後不支持

xxActivity extends BaseActivity<xxPresenter>...

xxPresenter getPresenter()
複製代碼
  • 多個prenenter 就只有經過註解得到變量了
@CreatePresenter(presenter = {xxPresenter1.class, xxPresenter2.class})
xxActivity extends BaseActivity...

@PresenterVariable
xxPresenter1 mPresenter1;

@PresenterVariable
xxPresenter2 mPresenter2;
複製代碼

本庫github地址 sugar 簡單便捷 快速開發Android項目,集合流行框架封裝mvp + rxjava2 + retrofit2 + rxlifecycle2 + arouter...

mvp Template完成

  • 效果圖

  • 使用見圖解

使用步驟:

  1. 下載源碼目錄在 Sugar/SugarMvpTemplate

  1. 把兩個文件夾放入{Android Studio installation dir}\plugins\android\lib\templates\activities\路徑下

  2. 重啓Android studio便可使用

About me

License

Copyright 2019, wobiancao       
  
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at 
 
       http://www.apache.org/licenses/LICENSE-2.0 

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   
複製代碼
相關文章
相關標籤/搜索