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

?? Sugar

image

最新版本1.0.1.4

需求:新項目只需5分鐘接入,之后直接開擼,不用關心網(wǎng)絡、圖片、模式、穩(wěn)定等問題,支持mvp一個activity對應多個presenter。
適用自己的才是最好的!

更新日志

  • 2019-06-14 更新 kotlin接入啦,還有一些好用的方法更新,后續(xù)更新文檔
  • 2019-05-29 最新,新鮮temeple出爐,請拉到最后看效果
  • 2019-05-31 更新 1.0.1.3升級到androidx
  • ...

To do

  • anko
  • kotlin-mvp temeple
  • kotlin 全面支持 ??
  • 圖片加載庫更換策略,或者為了方便使用直接用一套寫好的放入
  • 常用控件(刷新,標題等等)
  • more...

最新修改

  1. SugarHandleSubscriber 用于接口請求時候僅處理onNext(),因為在SugarRepository里面的customObservable(Observable observable)函數(shù)里面統(tǒng)一處理了錯誤異常抓取,一般情況下不需要再處理OnError當然可以重載函數(shù)的時候做處理
 .doOnError(throwable -> {
                    LogUtils.i("doOnError------" + throwable);
                    if (mIView != null) {
                        mIView.showLoadFailed();
                    }
                    if (rxErrorHandler != null){
                       //統(tǒng)一異常抓取
                       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狀態(tài)欄工具

ToastUtils 簡單實用toast

RxErroHandler rxjava異常獲取

RetrofitUrlManager retrofit動態(tài)綁定url

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

AndroidUtilCode 強大的工具庫

RxLifecycle 為rxjava而生你懂的

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

RxJava 不解釋

RxAndroid 不解釋

RxPermissions Android runtime permissions powered by RxJava2

Okhttp 不解釋

Gson 不解釋

Timber JakeWharton大神的日志打印工具

ARouter 阿里出的路由庫

lottie

...以及忘了加上的

使用效果

image

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

安裝和依賴

3種選擇

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

3、 allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }
implementation 'com.github.wobiancao:sugar:{version}'

統(tǒng)一配置

創(chuàng)建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("發(fā)生異常---" + t.getMessage());
            }
        };
    }

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

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

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

  • 創(chuàng)建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() {

    }

}

網(wǎng)絡請求統(tǒng)一配置

  • 問題:我們使用RetrofitUrlManager 解決了retorfit動態(tài)配置baseUrl的問題,但是每個域名或者說每個接口返回參數(shù)封裝等的可能不統(tǒng)一(這種情況一般不會出現(xiàn)在公司項目)比如我這個app要展示Gank.ioWanAndroid的界面,這樣就是兩個網(wǎng)絡請求封裝,使用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為需要增加的一個域名接口,統(tǒng)一配置apiService、請求函數(shù)、相應的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創(chuàng)建(之后會寫相應的Template ??已寫好)

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

WanContract

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

        void getWanArticleList(String index);
    }

    public interface IView extends BaseIView {
        /**
         * 綁定列表數(shù)據(jù)
         * @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層如何使用了,老規(guī)矩先看代碼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創(chuàng)建只需要注解即可,并且支持多個presenter

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

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

獲取presenter變量兩種方式

1、通過注解

@PresenterVariable
WanPresenter mPresenter;

2、通過getPresenter()函數(shù)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 簡單便捷 快速開發(fā)Android項目,集合流行框架封裝mvp + rxjava2 + retrofit2 + rxlifecycle2 + arouter...

mvp Template完成

  • 效果圖
image
image
  • 使用見圖解


    image

    image

使用步驟:

  1. 下載源碼目錄在 Sugar/SugarMvpTemplate
image
  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.
   
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。