前一段时间撸了好久的webview和前端的代码,停下来时间感觉要学点什么东西,在此和大家分享下我对mvp设计模式的理解,可能很多小伙伴项目中也会用到。先上代码,后面附上个人总结。
View层
/**
* Created by qiang.lin,view的接口,主要做一些获取用户输入的操作,和界面显示toast,页面跳转。
*/
public interface LoginViewInterface {
void showMsg(String msg);
String getUser();
String getPwd();
void startIntent();
}
public class LoginActivity extends BaseActivity implements View.OnClickListener, LoginViewInterface {
@InjectView(R.id.name)
EditText name;
@InjectView(R.id.pwd)
EditText pwd;
@InjectView(R.id.skip_register)
TextView skipRegister;
@InjectView(R.id.forget_pwd)
TextView forgetPwd;
@InjectView(R.id.login)
Button login;
private LoginPreInterface loginPreInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
MyApplication.getInstance().getActivityManager().addActivity(this);
ButterKnife.inject(this);
initView();
initData();
}
@Override
protected void initData() {
}
@Override
protected void initView() {
loginPreInterface=new LoginPreImpl(this);
login.setOnClickListener(this);
forgetPwd.setOnClickListener(this);
skipRegister.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.skip_register:
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
break;
case R.id.login:
loginPreInterface.login();
break;
case R.id.forget_pwd:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
MyApplication.getInstance().getActivityManager().finishActivity(this);
Log.i("登录界面销毁", "----");
}
@Override
public void showMsg(String msg) {
Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show();
}
@Override
public String getUser() {
return name.getText().toString();
}
@Override
public String getPwd() {
return pwd.getText().toString();
}
@Override
public void startIntent() {
Intent intent=new Intent(LoginActivity.this,MainActivity.class);
startActivity(intent);
}
在activity里面实现View的接口,主要做一些用户界面操作,我用了butterknife这个插件,这个大家可以去上网百度,快速实例化控件,并且对每个Activity进行管理。
butterknift使用:
添加依赖 compile ‘com.jakewharton:butterknife:6.1.0’
下载bufferknife就行,在setting里面可以直接下载。
P层
import example.com.mvpdesign.view.interfaces.LoginViewInterface;
/**
* Created by qiang.lin ,登录功能接口
*/
public interface LoginPreInterface {
void login();
}
/**
* Created by qiang.lin 方法回调,成功还是失败
*/
public interface OnLoginListener {
void onFail();
void onSucceed();
}
/**
* Created by qiang.lin,P层的实现类
*/
public class LoginPreImpl implements LoginPreInterface,OnLoginListener {
private LoginViewInterface loginViewInterface;
private LoginModel loginModel;
public LoginPreImpl(LoginViewInterface loginViewInterface) {
this.loginViewInterface = loginViewInterface;
loginModel = new LoginModelImpl(this);
}
@Override
public void login() {
String pwd = loginViewInterface.getPwd();
String name = loginViewInterface.getUser();
if(pwd.isEmpty()||name.isEmpty()){
loginViewInterface.showMsg("用户信息不全");
return;
}
loginModel.login(name, pwd);
}
@Override
public void onFail() {
loginViewInterface.showMsg("用户名或密码错误");
}
@Override
public void onSucceed() {
loginViewInterface.startIntent();
loginViewInterface.showMsg("登录成功");
}
}
Model层
/**
* Created by qiang.lin,实体类
*/
public class Entity<T> {
private int code;
private T msg;
public Entity(int code, T msg) {
super();
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public T getMsg() {
return msg;
}
public void setMsg(T msg) {
this.msg = msg;
}
}
/**
* Created by qiang.lin m层的功能接口
*/
public interface LoginModel {
void login(String name,String pwd);
}
这里开始就要用到网络请求框架和Rxjava。
添加依赖
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.google.code.gson:gson:2.8.1'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'io.reactivex.rxjava2:rxjava:2.1.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
/**
* Created by qiang.lin
*/
public class LoginModelImpl implements LoginModel {
private OnLoginListener onLoginListener;
public LoginModelImpl(OnLoginListener onLoginListener) {
this.onLoginListener = onLoginListener;
}
@Override
public void login(String name, String pwd) {
RxJavaClient.login(name, pwd, new Observer<Entity>() {
private Disposable disposable;
private int i;
@Override
public void onSubscribe(@NonNull Disposable d) {
//这里可以判断中断观察者接受数据源,被观察者还是会继续发送剩下数据。
disposable = d;
}
@Override
public void onNext(@NonNull Entity entity) {
//网络端发来的请求,disposable会让观察者停止接受数据源,但是被观察者还是会继续发送
//返回的数据在entity里面
// i++;
// if (i == 6) {
// disposable.dispose();
// Log.d("----", "isDisposed : " + disposable.isDisposed());
// }
boolean flag = isMainThread();
Log.i("----------------是否在主线程上", flag + "");
//根据网络请求的返回码判断调用成功方法还是失败方法。
onLoginListener.onSucceed();
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("rxjava错误日志", e + "");
onLoginListener.onFail();
}
@Override
public void onComplete() {
Log.i("-------onComplete", "");
}
});
}
//判断是否在主线程上
public boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
}
在这里要搭建网络请求框架,我就直接上代码了,直接用。
/**
* Created by qiang.lin
* 这里要创建一个方法,在retrofitservice也要创建一个方法
*/
//遇到耗时操作的时候可能要做修改,图片、文件、缓存
public class RxJavaClient {
//创建实例调用
private static final RetrofitService retrofitService = RetrofitClient.getClient();
//POST请求
public static void login(String name, String pwd, Observer<Entity> observer) {
setSubscribe(retrofitService.login(name, pwd), observer);
}
//公共方法
public static <T> void setSubscribe(Observable<T> observable, Observer<T> observer) {
observable.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.newThread())//子线程访问网络
.observeOn(AndroidSchedulers.mainThread())//回调到主线程
.subscribe(observer);
}
}
/**
* Created by qiang.lin
*/
public interface RetrofitService {
//把原来的retrofit的Call回调换成了rxjava的被观察者,跟网络连接就是被观察者
@Headers("Content-Type:application/json;charset=UTF-8")
@FormUrlEncoded
@POST("android/login")
Observable<Entity> login(@Field("name") String name, @Field("pwd") String pwd);
}
/**
* Created by qiang.lin
*/
//单例模式
public class RetrofitClient {
private static RetrofitService retrofitService;
private static final String TAG=RetrofitClient.class.getSimpleName();
public static RetrofitService getClient() {
if (retrofitService == null) {
OkHttpClient okClient = new OkHttpClient();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(MyApplication.getInstance().getCacheDir(), cacheSize);//缓存的目录
OkHttpClient.Builder builder = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)//设置超时时间
.readTimeout(10, TimeUnit.SECONDS)//设置读取超时时间
.writeTimeout(10, TimeUnit.SECONDS)//设置写入超时时间
.addInterceptor(interceptor).addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
// 进行重定向等操作
//Interceptor的典型使用场景,就是对request和response的Headers进行编辑
Response response=chain.proceed(chain.request());
Request request = chain.request();
long t1 = System.nanoTime();
Log.d(TAG,String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
long t2 = System.nanoTime();
Log.d(TAG, String.format("Received response for %s in %.1fms%n%sconnection=%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers(), chain.connection()));
Log.i("------------response",response.toString());
return response;
}
})
.cache(cache);
okClient = builder.build();
Retrofit client = new Retrofit.Builder()
//地址
.baseUrl(Contants.WEB_URL)
.client(okClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//将Callable接口转换成Observable接口
.build();
retrofitService = client.create(RetrofitService.class);
}
return retrofitService;
}
}
到这里一个综合案例就完成了,我基本关键点都有注释,大家可以看看。
总结:1、V层:抽象出获取用户输入和页面更新的接口,实例化P层对象,调用P层的方法,持有
的是P层接口的引用,防止gc回收的时候发现Activity仍持有引用而无法回收,导致oom
2、P层:抽象出具体功能的接口,如Login(interface),然后再Impl方法里面进行实现,
构造器里面传递过来的时候V层的接口class,通过这个来调用V层进行更新。
P层实例化M层的接口,然后调用M层从网络端获取的数据,整合V层过来的数据进行逻辑操作,
操作完成后调用通过接口实例调用V层的更新方法。
3、M层:和mvc里面的M层差不多,调用网络服务,获取数据,把网络服务抽象出来写成工具类。
4、 Android MVP设计模式比较多的采用了多态,通过接口的暴露,让P层持有两个层级的数据,降低了M层和V层的耦合,逻辑操作和视图更新分离,利于单元测试,但是需要些很多个接口类。
做单元测试的时候,可以写一个P层的测试类,这样来测试,因为主要的逻辑操作都在P层,减少了Activity的代码量。
5、真的是接口爆炸,我写了好多的接口,可能到项目大了它的优点就出来了。
以上仅代表博主本人的个人见解,如果不恰当的地方,可留言纠正。