Android中基于MVP构架的Socket实例
MVP构架简介
Android中的MVP构架主要就是对原先MVC中Controller层臃肿的代码进行提取抽象,实际上就是将Activity当中的UI逻辑,业务逻辑与数据进行隔离,因此分为了三个层次;
-
View: 视图层,在View层中只负责对数据的展示,在Android开发中通常将Activity或者Fragment作为View层;
-
Model: 数据层,负责对数据的存取操作,例如对数据库的读写,网络的数据的请求等;
-
Presenter:连接View层与Model层的桥梁并对业务逻辑进行处理,在这层中从Model层获得所需要的数据,进行一些适当的处理后返回View层进行显示。
实例介绍
本项目的Socket作为长连接来使用,因此socket放在基类model层中,在基类model层中进行socket的收发消息操作,View层作为数据的展示与用户获取数据的操作接口,presenter层作为View层与Model层的中转,并且进行一些业务逻辑操作与线程切换。
流程序列图:
项目结构:
|----包名
| |----mvp
| | |----base
| | | BaseModel Model基类
| | | IBaseDelegate 简化Presenter在Activity的实现
| | | IBasePresenter Presenter基类
| | | IBaseView View基类
| | |----model 业务逻辑和bean
| | | xxxModel
| | | xxxBean
| | |----presenter 连接View和Model的桥梁
| | | xxxPresenter
| | |----view UI界面提炼出来的接口
| | | xxxView
| |----ui
| | |----activity 具体Activity
| | | BaseActivity Activity基类
| | | BaseMVPActivity MVP Activity基类
| | | xxxActivity
| | |----fragment 具体Fragment
| | | BaseFragment Fragment基类
| | | xxxFragment
实例分析
mvp基类构建
基类view,在这里抽象出所有activity的通用接口
public interface IBaseView {
/**
* 显示加载
*/
void showLoading();
/**
* 完成加载
*/
void dismiss();
}
presenter基类,连接Model和View,定义一个限定view为IbaseView子类的泛型,抽象出绑定和解绑接口
public interface IBasePresenter<V extends IBaseView> {
/**绑定接口*/
void attachView(V view);
/**释放接口*/
void detachView();
}
基类model,使用泛型方便子类与IBasePresenter的子类绑定
public abstract class BaseModel<SubP> {
protected SubP mPresenter;
public BaseModel(SubP presenter) {
this.mPresenter = presenter;
}
}
基类代理接口,抽象出创建和获取presenter方法,使persenter和activity进行绑定,泛型P为IBasePresenter的子类,由于IBasePresenter的泛型V限定为IbaseView的子类,因此IBaseDelegate也需要定义一个限定V为IbaseView子类的泛型来传入IBasePresenter中
public interface IBaseDelegate<V extends IBaseView, P extends IBasePresenter<V>> {
/**初始化presenter*/
@NonNull
P createPresenter();
/**获取presenter*/
@NonNull
P getPresenter();
}
Activity基类,在这里实现通用方法
public abstract class BaseActivity extends AppCompatActivity {
protected void startActivity(Class<?> clz) {
Intent intent = new Intent(this, clz);
startActivity(intent);
}
}
MVPActivity基类,实现presenter对象的创建,获取和解绑
public abstract class BaseMVPActivity<V extends IBaseView, P extends IBasePresenter<V>> extends
BaseActivity implements IBaseDelegate<V, P> {
protected P mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mPresenter == null) {
mPresenter = createPresenter();
}
}
@NonNull
@Override
public P getPresenter() {
return mPresenter;
}
@Override
protected void onDestroy() {
//解除绑定
if (mPresenter != null) {
mPresenter.detachView();
}
super.onDestroy();
}
}
mvp构架功能实现
定义功能模块的view的接口
public interface IMainView extends IBaseView {
/**
* 显示数据
*/
void showData(String s);
/**
* 显示通知
*/
void showMessage(String msg);
}
定义功能模块的presenter,在presenter的构造方法中新建model层
public class MainPresenter implements IBasePresenter<IMainView> {
private IMainView mView;
private MainModel mModel;
public MainPresenter(IMainView mView) {
attachView(mView);
this.mModel = new MainModel(this);
}
@Override
public void attachView(IMainView view) {
this.mView = view;
}
@Override
public void detachView() {
this.mView = null;
}
public void showData(String s) {
if (mView == null) {
return;
}
mView.dismiss();
mView.showData(s);
}
public void showMessage(String msg) {
if (mView == null) {
return;
}
mView.showMessage(msg);
}
public <T> void sendData(T data) {
mModel.sendData(data);
}
public void stopSocket() {
mModel.stopSocket();
}
}
定义功能模块的model,在构造方法中创建封装好的socket对象,并放入线程池中运行
封装好的SocketClientThread介绍可以看这篇文章http://blog.csdn.net/lj402159806/article/details/79371526
关于线程池的介绍可以看这篇文章http://blog.csdn.net/lj402159806/article/details/79369692
public class MainModel extends BaseModel<MainPresenter> implements SocketClientResponseInterface {
private static final String TAG = MainModel.class.getSimpleName();
private SocketClientThread socketClientThread;
public MainModel(MainPresenter presenter) {
super(presenter);
socketClientThread = new SocketClientThread("socketClientThread", this);
ThreadPoolUtil.getInstance().addExecuteTask(socketClientThread);
}
@Override
public void onSocketConnect() {
mPresenter.showData("连接成功");
}
@Override
public void onSocketReceive(Object socketResult, int code) {
mPresenter.showData("收到消息 , data: " + socketResult + " , code: " + code);
}
@Override
public void onSocketDisable(String msg, int code) {
mPresenter.showMessage("连接断开 , msg: " + msg + " , code: " + code);
}
public <T> void sendData(T data) {
//convert to string or serialize object
String s = (String) data;
if (TextUtils.isEmpty(s)) {
mPresenter.showMessage("消息不能为空");
return;
}
if (socketClientThread != null) {
socketClientThread.addRequest(s);
}
}
public void stopSocket() {
//一定要在子线程内执行关闭socket等IO操作
ThreadPoolUtil.getInstance().addExecuteTask(() -> {
socketClientThread.setReConnect(false);
socketClientThread.stopThread();
});
ThreadPoolUtil.getInstance().shutdown();
}
}
Fragment基类,定义回调Activity的接口
public abstract class BaseFragment extends Fragment {
/**
* Activity 供Fragment回调的接口
*/
public interface IMainCallBack {
<T> void sendData(T data);
}
}
view层逻辑在Fragment中实现,这里功能仅为点击按钮回调activity
public class TestFragment extends BaseFragment {
private static final String KEY_MESSAGE = "message";
Unbinder unbinder;
@BindView(R.id.btn_send)
Button mBtnSend;
private String msg;
private IMainCallBack activityCallBack;
public static TestFragment newInstance(String message) {
TestFragment fragment = new TestFragment();
Bundle args = new Bundle();
args.putString(KEY_MESSAGE, message);
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
activityCallBack = (IMainCallBack) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement activityCallBack");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
msg = (String) getArguments().getSerializable(KEY_MESSAGE);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_text, container, false);
unbinder = ButterKnife.bind(this, view);
initView();
return view;
}
private void initView() {
String text = mBtnSend.getText() + msg;
mBtnSend.setText(text);
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@OnClick({R.id.btn_send})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn_send:
activityCallBack.sendData(msg);
break;
default:
break;
}
}
}
MainActivity作为多个fragment的容器,对各个fragment提供统一的mvp接口
public class MainActivity extends BaseMVPActivity<IMainView, MainPresenter> implements
IMainView, BaseFragment.IMainCallBack {
private static final String TAG = MainActivity.class.getSimpleName();
@BindView(R.id.bottom_navigation_bar)
BottomNavigationBar mBottomNavigationBar;
private TestFragment fragment1;
private TestFragment fragment2;
private TestFragment fragment3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(MainActivity.this);
initView();
}
private void initView() {
fragment1 = TestFragment.newInstance("123");
fragment2 = TestFragment.newInstance("321");
fragment3 = TestFragment.newInstance("123321");
mBottomNavigationBar.setTabSelectedListener(new BottomNavigationBar.SimpleOnTabSelectedListener() {
@Override
public void onTabSelected(int position) {
setScrollableText(position);
}
});
mBottomNavigationBar
.addItem(new BottomNavigationItem(R.mipmap.ic_location_on_white_24dp, "Nearby").setActiveColorResource(R.color.orange))
.addItem(new BottomNavigationItem(R.mipmap.ic_find_replace_white_24dp, "Find").setActiveColorResource(R.color.teal))
.addItem(new BottomNavigationItem(R.mipmap.ic_favorite_white_24dp, "Categories").setActiveColorResource(R.color.blue))
.initialise();
setScrollableText(0);
}
@NonNull
@Override
public MainPresenter createPresenter() {
return new MainPresenter(this);
}
@Override
protected void onDestroy() {
getPresenter().sendData("bye");
Log.i(TAG, "onDestroy: sendData");
getPresenter().stopSocket();
Log.i(TAG, "onDestroy: stopSocket");
super.onDestroy();
}
@Override
public void showData(String s) {
Log.i(TAG, "showData: " + s);
runOnUiThread(() -> Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show());
}
@Override
public void showMessage(String msg) {
Log.i(TAG, "showMessage: " + msg);
//runOnUiThread(() -> Toast.makeText(this, msg, Toast.LENGTH_SHORT).show());
}
@Override
public void showLoading() {
}
@Override
public void dismiss() {
}
@Override
public <T>void sendData(T data) {
getPresenter().sendData(data);
}
private void setScrollableText(int position) {
switch (position) {
case 0:
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_frag_container, fragment1).commitAllowingStateLoss();
break;
case 1:
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_frag_container, fragment2).commitAllowingStateLoss();
break;
case 2:
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_frag_container, fragment3).commitAllowingStateLoss();
break;
default:
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_frag_container, fragment1).commitAllowingStateLoss();
break;
}
}
}
由于将mainActivity作为fragment的mvp容器会导致mainActivity的mvp接口过多,因此已更新为由fragment自己实现mvp功能,mainModel仅作为socket的转发端
首先抽象出一个BaseSocketModel类
/**
* Created by gavinandre on 18-1-7.
*/
public abstract class BaseSocketModel<SubP> extends BaseModel<SubP> implements SocketClientResponseInterface {
private static final String TAG = BaseSocketModel.class.getSimpleName();
private static SocketClient socketClient;
public BaseSocketModel(SubP presenter) {
super(presenter);
if (socketClient == null) {
socketClient = new SocketClient(this);
}
}
public <T> void sendData(T data) {
if (socketClient != null) {
socketClient.sendData(data);
}
}
public void stopSocket() {
if (socketClient != null) {
socketClient.stopSocket();
socketClient = null;
}
}
@Override
public void onSocketConnect() {
}
@Override
public void onSocketReceive(Object socketResult, int code) {
}
@Override
public void onSocketDisable(String msg, int code) {
}
}
mainModel继承BaseSocketModel
/**
* Created by gavinandre on 18-1-7.
*/
public class MainModel extends BaseSocketModel<MainPresenter> {
private static final String TAG = MainModel.class.getSimpleName();
public MainModel(MainPresenter presenter) {
super(presenter);
}
@Override
public void onSocketConnect() {
//if (mPresenter != null) {
// mPresenter.showData("连接成功");
//}
EventBus.getDefault().post("连接成功");
}
@Override
public void onSocketReceive(Object socketResult, int code) {
//if (mPresenter != null) {
// mPresenter.showData("收到消息 , data: " + socketResult + " , code: " + code);
//}
//发送消息给childModel
EventBus.getDefault().post("收到消息 , data: " + socketResult + " , code: " + code);
}
@Override
public void onSocketDisable(String msg, int code) {
//if (mPresenter != null) {
// mPresenter.showMessage("连接断开 , msg: " + msg + " , code: " + code);
//}
EventBus.getDefault().post("连接断开 , msg: " + msg + " , code: " + code);
}
@Override
public void detachModel() {
stopSocket();
super.detachModel();
}
}
在mainModel中使用EventBus将消息转发到fragment的model中,更多细节请参考完整demo
Socket通信完整实例(心跳包,客户端断线重连,服务端超时断开客户端连接)分析
https://blog.csdn.net/lj402159806/article/details/79371526
完整demo:
https://github.com/GavinAndre/MVPSocket
参考:
http://blog.csdn.net/jiujiedexiaoming/article/details/54927617