Recently, there is a new project to do. When building the framework, I combed the next MVP mode by the way. Here is a record. Welcome to make corrections.
Project address GitHub
First, dependence
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.yanzhenjie:permission:1.0.5'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.5'
//Converter Factory's String dependency package
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
//Gson dependency package of ConverterFactory
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
//Rex dependency package of CallAdapterFactory
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
2. ApiServer, defining some interfaces
public interface ApiServer {
@POST("shopping_login.htm")
Observable<String> LoginByRx(@Field("username") String username, @Field("password") String password);
}
3. ApiRetrofit, initialize Retrofit, OkHttpClient and add interceptor
public class ApiRetrofit {
public final String BASE_SERVER_URL = "https://wawa-api.vchangyi.com/";
private static ApiRetrofit apiRetrofit;
private Retrofit retrofit;
private OkHttpClient client;
private ApiServer apiServer;
private String TAG = "ApiRetrofit";
/**
* Request access to quest
* response Interceptor
*/
private Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
Response response = chain.proceed(chain.request());
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
MediaType mediaType = response.body().contentType();
String content = response.body().string();
Log.e(TAG, "----------Request Start----------------");
Log.e(TAG, "| " + request.toString() + request.headers().toString());
Log.e(TAG, "| Response:" + content);
Log.e(TAG, "----------Request End:" + duration + "Millisecond----------");
return response.newBuilder()
.body(ResponseBody.create(mediaType, content))
.build();
}
};
public ApiRetrofit() {
client = new OkHttpClient.Builder()
//Add log interceptor
.addInterceptor(interceptor)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
//Support for RxJava2
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
apiServer = retrofit.create(ApiServer.class);
}
public static ApiRetrofit getInstance() {
if (apiRetrofit == null) {
synchronized (Object.class) {
if (apiRetrofit == null) {
apiRetrofit = new ApiRetrofit();
}
}
}
return apiRetrofit;
}
public ApiServer getApiService() {
return apiServer;
}
}
IV. BASIC CLASS
1.BaseView, which defines common interfaces
public interface BaseView {
/**
* Display dialog
*/
void showLoading();
/**
* Hide dialog
*/
void hideLoading();
/**
* Display error message
*
* @param msg
*/
void showError(String msg);
/**
* Error code
*/
void onErrorCode(BaseModel model);
}
2.BaseModel
public class BaseModel<T> implements Serializable {
private int errcode;
private String errmsg;
private T result;
public int getErrcode() {
return errcode;
}
public void setErrcode(int errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
}
3.BasePresenter
public class BasePresenter<V extends BaseView> {
private CompositeDisposable compositeDisposable;
public V baseView;
protected ApiServer apiServer = ApiRetrofit.getInstance().getApiService();
public BasePresenter(V baseView) {
this.baseView = baseView;
}
/**
* Unbinding
*/
public void detachView() {
baseView = null;
removeDisposable();
}
/**
* Return to view
*
* @return
*/
public V getBaseView() {
return baseView;
}
public void addDisposable(Observable<?> observable, BaseObserver observer) {
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
}
compositeDisposable.add(observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer));
}
public void removeDisposable() {
if (compositeDisposable != null) {
compositeDisposable.dispose();
}
}
}
4.BaseObserver, which deals with common errors
public abstract class BaseObserver<T> extends DisposableObserver<T> {
protected BaseView view;
/**
* Failure to parse data
*/
public static final int PARSE_ERROR = 1001;
/**
* Network issues
*/
public static final int BAD_NETWORK = 1002;
/**
* Connection error
*/
public static final int CONNECT_ERROR = 1003;
/**
* connection timed out
*/
public static final int CONNECT_TIMEOUT = 1004;
public BaseObserver(BaseView view) {
this.view = view;
}
@Override
protected void onStart() {
if (view != null) {
view.showLoading();
}
}
@Override
public void onNext(T o) {
try {
BaseModel model = (BaseModel) o;
if (model.getErrcode() == 0) {
onSuccess(o);
} else {
if (view != null) {
view.onErrorCode(model);
}
}
} catch (Exception e) {
e.printStackTrace();
onError(e.toString());
}
}
@Override
public void onError(Throwable e) {
if (view != null) {
view.hideLoading();
}
if (e instanceof HttpException) {
// HTTP error
onException(BAD_NETWORK);
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) {
// Connection error
onException(CONNECT_ERROR);
} else if (e instanceof InterruptedIOException) {
// connection timed out
onException(CONNECT_TIMEOUT);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
// Parsing error
onException(PARSE_ERROR);
} else {
if (e != null) {
onError(e.toString());
} else {
onError("unknown error");
}
}
}
private void onException(int unknownError) {
switch (unknownError) {
case CONNECT_ERROR:
onError("Connection error");
break;
case CONNECT_TIMEOUT:
onError("connection timed out");
break;
case BAD_NETWORK:
onError("Network issues");
break;
case PARSE_ERROR:
onError("Failure to parse data");
break;
default:
break;
}
}
@Override
public void onComplete() {
if (view != null) {
view.hideLoading();
}
}
public abstract void onSuccess(T o);
public abstract void onError(String msg);
}
5.BaseActivity, which implements BaseView, handles the life cycle of Presenter.
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {
public Context context;
private ProgressDialog dialog;
public Toast toast;
protected P presenter;
protected abstract P createPresenter();
protected abstract int getLayoutId();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
setContentView(getLayoutId());
presenter = createPresenter();
initView();
initData();
}
public void initData() {
}
public void initView() {
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.detachView();
}
}
/**
* @param s
*/
public void showtoast(String s) {
if (toast == null) {
toast = Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG);
}
toast.show();
}
private void closeLoadingDialog() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
private void showLoadingDialog() {
if (dialog == null) {
dialog = new ProgressDialog(context);
}
dialog.setCancelable(false);
dialog.show();
}
@Override
public void showLoading() {
showLoadingDialog();
}
@Override
public void hideLoading() {
closeLoadingDialog();
}
@Override
public void showError(String msg) {
showtoast(msg);
}
@Override
public void onErrorCode(BaseModel model) {
}
@Override
public void showLoadingFileDialog() {
showFileDialog();
}
@Override
public void hideLoadingFileDialog() {
hideFileDialog();
}
@Override
public void onProgress(long totalSize, long downSize) {
if (dialog != null) {
dialog.setProgress((int) (downSize * 100 / totalSize));
}
}
}
Fifth, as mentioned above, the framework has been built. Let's see how to use it in practical projects.
Let's simulate landing.
1. Define LoginView to inherit BaseView
public interface LoginView extends BaseView {
void onLoginSucc();
}
2. Define LoginPresenter to inherit from BasePresenter
public class LoginPresenter extends BasePresenter<LoginView> {
public LoginPresenter(LoginView baseView) {
super(baseView);
}
public void login(String name, String pwd) {
addDisposable(apiServer.LoginByRx(name, pwd), new BaseObserver(baseView) {
@Override
public void onSuccess(Object o) {
baseView.onLoginSucc();
}
@Override
public void onError(String msg) {
baseView.showError(msg);
}
});
}
}
3. Using LoginActivity
public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView {
private AutoCompleteTextView mEmailView;
private EditText mPasswordView;
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(this);
}
@Override
protected int getLayoutId() {
return R.layout.activity_login;
}
@Override
public void initView() {
mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
mPasswordView = (EditText) findViewById(R.id.password);
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//Sample code, sample interface
presenter.login(mEmailView.getText().toString(), mPasswordView.getText().toString());
}
});
}
@Override
public void onLoginSucc() {
//Login Succ
showtoast("Successful login");
}
}
Summary: The above is the implementation of MVP. There are many frameworks for MVP, just for yourself.
Your acceptance is the motivation for me to keep updating my blog. If it's useful, please give me a compliment. Thank you.