MVPArms 基於 MVP 開發的 Android App 通用架構,集成了許多開源項目(如Dagger2、RxJava、Retrofit ...),使您的開發更快更容易。
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.InflateException;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.jess.arms.base.delegate.IActivity;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.CacheType;
import com.jess.arms.integration.lifecycle.ActivityLifecycleable;
import com.jess.arms.mvp.IPresenter;
import com.jess.arms.utils.ArmsUtils;
import com.trello.rxlifecycle2.android.ActivityEvent;
import javax.inject.Inject;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;
import static com.jess.arms.utils.ThirdViewUtil.convertAutoView;
public abstract class BaseActivity<P extends IPresenter> extends AppCompatActivity implements IActivity, ActivityLifecycleable {
protected final String TAG = this.getClass().getSimpleName();
private final BehaviorSubject<ActivityEvent> mLifecycleSubject = BehaviorSubject.create();
@Inject
@Nullable
protected P mPresenter;
private Cache<String, Object> mCache;
private Unbinder mUnbinder;
@NonNull
@Override
public synchronized Cache<String, Object> provideCache() {
if (mCache == null) {
//noinspection unchecked
mCache = ArmsUtils.obtainAppComponentFromContext(this).cacheFactory().build(CacheType.ACTIVITY_CACHE);
}
return mCache;
}
@NonNull
@Override
public final Subject<ActivityEvent> provideLifecycleSubject() {
return mLifecycleSubject;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
View view = convertAutoView(name, context, attrs);
return view == null ? super.onCreateView(name, context, attrs) : view;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
int layoutResID = initView(savedInstanceState);
if (layoutResID != 0) {
setContentView(layoutResID);
mUnbinder = ButterKnife.bind(this);
}
} catch (Exception e) {
if (e instanceof InflateException) {
throw e;
}
e.printStackTrace();
}
initData(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) {
mUnbinder.unbind();
}
this.mUnbinder = null;
if (mPresenter != null) {
mPresenter.onDestroy();
}
this.mPresenter = null;
}
@Override
public boolean useEventBus() {
return true;
}
@Override
public boolean useFragment() {
return true;
}
}
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.jess.arms.base.delegate.IFragment;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.CacheType;
import com.jess.arms.integration.lifecycle.FragmentLifecycleable;
import com.jess.arms.mvp.IPresenter;
import com.jess.arms.utils.ArmsUtils;
import com.trello.rxlifecycle2.android.FragmentEvent;
import javax.inject.Inject;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;
public abstract class BaseFragment<P extends IPresenter> extends Fragment implements IFragment, FragmentLifecycleable {
protected final String TAG = this.getClass().getSimpleName();
private final BehaviorSubject<FragmentEvent> mLifecycleSubject = BehaviorSubject.create();
protected Context mContext;
@Inject
@Nullable
protected P mPresenter;
private Cache<String, Object> mCache;
@NonNull
@Override
public synchronized Cache<String, Object> provideCache() {
if (mCache == null) {
mCache = ArmsUtils.obtainAppComponentFromContext(getActivity()).cacheFactory().build(CacheType.FRAGMENT_CACHE);
}
return mCache;
}
@NonNull
@Override
public final Subject<FragmentEvent> provideLifecycleSubject() {
return mLifecycleSubject;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return initView(inflater, container, savedInstanceState);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDestroy();
}
this.mPresenter = null;
}
@Override
public void onDetach() {
super.onDetach();
mContext = null;
}
@Override
public boolean useEventBus() {
return true;
}
}
import android.app.Service;
import android.view.View;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
public class BasePresenter<M extends IModel, V extends IView> implements IPresenter, LifecycleObserver {
protected final String TAG = this.getClass().getSimpleName();
protected M mModel;
protected V mRootView;
public BasePresenter(M model, V rootView) {
this.mModel = model;
this.mRootView = rootView;
onStart();
}
public BasePresenter(V rootView) {
this.mRootView = rootView;
onStart();
}
public BasePresenter() {
onStart();
}
@Override
public void onStart() {
if (mRootView != null && mRootView instanceof LifecycleOwner) {
((LifecycleOwner) mRootView).getLifecycle().addObserver(this);
if (mModel != null && mModel instanceof LifecycleObserver) {
((LifecycleOwner) mRootView).getLifecycle().addObserver((LifecycleObserver) mModel);
}
}
}
@Override
public void onDestroy() {
if (mModel != null) {
mModel.onDestroy();
}
this.mModel = null;
this.mRootView = null;
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestroy(LifecycleOwner owner) {
owner.getLifecycle().removeObserver(this);
}
}
/*
* Copyright 2017 JessYan
*
* 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.
*/
package com.yi.androidarchitecturemvp.jessyancoding_mvparms.demo.contract;
import android.app.Activity;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.base.presenter.IModel;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.base.presenter.IView;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.demo.model.entity.User;
import java.util.List;
import io.reactivex.Observable;
import me.jessyan.mvparms.demo.mvp.model.entity.User;
public interface UserContract {
interface View extends IView {
void startLoadMore();
void endLoadMore();
Activity getActivity();
//申请权限
RxPermissions getRxPermissions();
}
interface Model extends IModel {
Observable<List<User>> getUsers(int lastIdQueried, boolean update);
}
}
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.OnLifecycleEvent;
import com.jess.arms.di.scope.ActivityScope;
import com.jess.arms.integration.IRepositoryManager;
import com.jess.arms.mvp.BaseModel;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.functions.Function;
import io.rx_cache2.DynamicKey;
import io.rx_cache2.EvictDynamicKey;
import me.jessyan.mvparms.demo.mvp.contract.UserContract;
import me.jessyan.mvparms.demo.mvp.model.api.cache.CommonCache;
import me.jessyan.mvparms.demo.mvp.model.api.service.UserService;
import me.jessyan.mvparms.demo.mvp.model.entity.User;
import timber.log.Timber;
@ActivityScope
public class UserModel extends BaseModel implements UserContract.Model {
public static final int USERS_PER_PAGE = 10;
@Inject
public UserModel(IRepositoryManager repositoryManager) {
super(repositoryManager);
}
@Override
public Observable<List<User>> getUsers(int lastIdQueried, boolean update) {
return Observable.just(mRepositoryManager
.obtainRetrofitService(UserService.class)
.getUsers(lastIdQueried, USERS_PER_PAGE))
.flatMap((Function<Observable<List<User>>, ObservableSource<List<User>>>) listObservable -> mRepositoryManager.obtainCacheService(CommonCache.class)
.getUsers(listObservable
, new DynamicKey(lastIdQueried)
, new EvictDynamicKey(update))
.map(listReply -> listReply.getData()));
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause() {
Timber.d("Release Resource");
}
}
import android.app.Application;
import androidx.core.app.ComponentActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.recyclerview.widget.RecyclerView;
import com.jess.arms.di.scope.ActivityScope;
import com.jess.arms.integration.AppManager;
import com.jess.arms.mvp.BasePresenter;
import com.jess.arms.utils.PermissionUtil;
import com.jess.arms.utils.RxLifecycleUtils;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.base.presenter.BasePresenter;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.demo.contract.UserContract;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import me.jessyan.mvparms.demo.mvp.contract.UserContract;
import me.jessyan.mvparms.demo.mvp.model.entity.User;
import me.jessyan.rxerrorhandler.core.RxErrorHandler;
import me.jessyan.rxerrorhandler.handler.ErrorHandleSubscriber;
import me.jessyan.rxerrorhandler.handler.RetryWithDelay;
@ActivityScope
public class UserPresenter extends BasePresenter<UserContract.Model, UserContract.View> {
@Inject
RxErrorHandler mErrorHandler;
@Inject
AppManager mAppManager;
@Inject
Application mApplication;
@Inject
List<User> mUsers;
@Inject
RecyclerView.Adapter mAdapter;
private int lastUserId = 1;
private boolean isFirst = true;
private int preEndIndex;
@Inject
public UserPresenter(UserContract.Model model, UserContract.View rootView) {
super(model, rootView);
}
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate() {
requestUsers(true);
}
public void requestUsers(final boolean pullToRefresh) {
PermissionUtil.externalStorage(new PermissionUtil.RequestPermission() {
@Override
public void onRequestPermissionSuccess() {
//request permission success, do something.
requestFromModel(pullToRefresh);
}
@Override
public void onRequestPermissionFailure(List<String> permissions) {
mRootView.showMessage("Request permissions failure");
mRootView.hideLoading();
}
@Override
public void onRequestPermissionFailureWithAskNeverAgain(List<String> permissions) {
mRootView.showMessage("Need to go to the settings");
mRootView.hideLoading();
}
}, mRootView.getRxPermissions(), mErrorHandler);
}
private void requestFromModel(boolean pullToRefresh) {
if (pullToRefresh) {
lastUserId = 1;
}
boolean isEvictCache = pullToRefresh;
if (pullToRefresh && isFirst) {
isFirst = false;
isEvictCache = false;
}
mModel.getUsers(lastUserId, isEvictCache)
.subscribeOn(Schedulers.io())
.retryWhen(new RetryWithDelay(3, 2))
.doOnSubscribe(disposable -> {
if (pullToRefresh) {
mRootView.showLoading();
} else {
mRootView.startLoadMore();
}
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doFinally(() -> {
if (pullToRefresh) {
mRootView.hideLoading();
} else {
mRootView.endLoadMore();
}
})
.compose(RxLifecycleUtils.bindToLifecycle(mRootView))
.subscribe(new ErrorHandleSubscriber<List<User>>(mErrorHandler) {
@Override
public void onNext(List<User> users) {
lastUserId = users.get(users.size() - 1).getId();
if (pullToRefresh) {
mUsers.clear();
}
preEndIndex = mUsers.size();
mUsers.addAll(users);
if (pullToRefresh) {
mAdapter.notifyDataSetChanged();
} else {
mAdapter.notifyItemRangeInserted(preEndIndex, users.size());
}
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
this.mAdapter = null;
this.mUsers = null;
this.mErrorHandler = null;
this.mAppManager = null;
this.mApplication = null;
}
}
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.jess.arms.base.BaseActivity;
import com.jess.arms.base.DefaultAdapter;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.utils.ArmsUtils;
import com.paginate.Paginate;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.base.activity.BaseActivity;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.demo.contract.UserContract;
import com.yi.androidarchitecturemvp.jessyancoding_mvparms.demo.presenter.UserPresenter;
import javax.inject.Inject;
import butterknife.BindView;
import me.jessyan.mvparms.demo.R;
import me.jessyan.mvparms.demo.di.component.DaggerUserComponent;
import me.jessyan.mvparms.demo.mvp.contract.UserContract;
import me.jessyan.mvparms.demo.mvp.presenter.UserPresenter;
import timber.log.Timber;
import static com.jess.arms.utils.Preconditions.checkNotNull;
public class UserActivity extends BaseActivity<UserPresenter> implements UserContract.View, SwipeRefreshLayout.OnRefreshListener {
@BindView(R.id.recyclerView)
RecyclerView mRecyclerView;
@BindView(R.id.swipeRefreshLayout)
SwipeRefreshLayout mSwipeRefreshLayout;
@Inject
RxPermissions mRxPermissions;
@Inject
RecyclerView.LayoutManager mLayoutManager;
@Inject
RecyclerView.Adapter mAdapter;
private Paginate mPaginate;
private boolean isLoadingMore;
@Override
public void setupActivityComponent(@NonNull AppComponent appComponent) {
DaggerUserComponent
.builder()
.appComponent(appComponent)
.view(this)
.build()
.inject(this);
}
@Override
public int initView(@Nullable Bundle savedInstanceState) {
return R.layout.activity_user;
}
@Override
public void initData(@Nullable Bundle savedInstanceState) {
initRecyclerView();
mRecyclerView.setAdapter(mAdapter);
initPaginate();
}
@Override
public void onRefresh() {
mPresenter.requestUsers(true);
}
private void initRecyclerView() {
mSwipeRefreshLayout.setOnRefreshListener(this);
ArmsUtils.configRecyclerView(mRecyclerView, mLayoutManager);
}
@Override
public void showLoading() {
Timber.tag(TAG).w("showLoading");
mSwipeRefreshLayout.setRefreshing(true);
}
@Override
public void hideLoading() {
Timber.tag(TAG).w("hideLoading");
mSwipeRefreshLayout.setRefreshing(false);
}
@Override
public void showMessage(@NonNull String message) {
checkNotNull(message);
ArmsUtils.snackbarText(message);
}
@Override
public void launchActivity(@NonNull Intent intent) {
checkNotNull(intent);
ArmsUtils.startActivity(intent);
}
@Override
public void killMyself() {
finish();
}
@Override
public void startLoadMore() {
isLoadingMore = true;
}
@Override
public void endLoadMore() {
isLoadingMore = false;
}
@Override
public Activity getActivity() {
return this;
}
@Override
public RxPermissions getRxPermissions() {
return mRxPermissions;
}
private void initPaginate() {
if (mPaginate == null) {
Paginate.Callbacks callbacks = new Paginate.Callbacks() {
@Override
public void onLoadMore() {
mPresenter.requestUsers(false);
}
@Override
public boolean isLoading() {
return isLoadingMore;
}
@Override
public boolean hasLoadedAllItems() {
return false;
}
};
mPaginate = Paginate.with(mRecyclerView, callbacks)
.setLoadingTriggerThreshold(0)
.build();
mPaginate.setHasMoreDataToLoad(false);
}
}
@Override
protected void onDestroy() {
DefaultAdapter.releaseAllHolder(mRecyclerView);
super.onDestroy();
this.mRxPermissions = null;
this.mPaginate = null;
}
}
分 3 大部分
com.squareup.retrofit2:retrofit
com.squareup.retrofit2:converter-gson
com.squareup.retrofit2:adapter-rxjava
com.squareup.retrofit2:adapter-rxjava2
com.squareup.okhttp3:okhttp
com.squareup.okhttp3:okhttp
com.squareup.okhttp:okhttp-urlconnection
com.github.bumptech.glide:glide
com.github.bumptech.glide:compiler
com.github.bumptech.glide:okhttp3-integration
com.squareup.picasso:picasso
com.zhy:autolayout
com.jakewharton:butterknife
com.jakewharton:butterknife-compiler
com.contrarywind:Android-PickerView
com.github.chrisbanes.photoview:library
com.daimajia.numberprogressbar:library
com.nineoldandroids:library
com.github.markomilos:paginate
com.alibaba.android:vlayout
me.jessyan:autosize
io.reactivex:rxandroid
io.reactivex:rxjava
com.trello:rxlifecycle
com.trello:rxlifecycle-components
com.github.VictorAlbertos.RxCache:runtime
com.github.VictorAlbertos.Jolyglot:gson
com.jakewharton.rxbinding:rxbinding-recyclerview-v7
com.tbruyelle.rxpermissions:rxpermissions
me.jessyan:rxerrorhandler
io.reactivex.rxjava2:rxandroid
io.reactivex.rxjava2:rxjava
com.trello.rxlifecycle2:rxlifecycle
com.trello.rxlifecycle2:rxlifecycle-android
com.trello.rxlifecycle2:rxlifecycle-components
com.github.VictorAlbertos.RxCache:runtime
com.github.tbruyelle:rxpermissions
me.jessyan:rxerrorhandler
com.google.dagger:dagger
com.google.dagger:dagger-android
com.google.dagger:dagger-android-support
com.google.dagger:dagger-compiler
com.google.dagger:dagger-android-processor
org.simple:androideventbus
org.greenrobot:eventbus
com.squareup:otto
com.google.code.gson:gson
com.android.support:multidex
javax.annotation:jsr250-api
com.alibaba:arouter-api
com.alibaba:arouter-compiler
me.jessyan:progressmanager
me.jessyan:retrofit-url-manager
me.jessyan:lifecyclemodel
junit:junit
androidx.test.runner.AndroidJUnitRunner
com.android.support.test:runner
com.android.support.test.espresso:espresso-core
com.android.support.test.espresso:espresso-contrib
com.android.support.test.espresso:espresso-intents
org.mockito:mockito-core
com.jakewharton.timber:timber
com.orhanobut:logger
com.squareup.leakcanary:leakcanary-android
com.squareup.leakcanary:leakcanary-android-no-op
com.umeng.analytics:analytics