MVP从更早的MVC架构演变过来的,与MVC有一定的相似:
(1)MVC的缺点:在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载页面布局和初始化用户界面,接受并处理来自用户的操作请求。随着界面及逻辑的复杂度不断提升,Activity类的职能不断增加,以致变得庞大臃肿。
(2)MVP通常有以下几个部分组成:
- View:负责绘制和更新UI,与用户进行交互(在Android中体现为Activity或Fragment)。
- Model:负责存储,检索,操作数据或与后台通信,简单理解就是获取数据的地方(有时也实现一个Model Interface来降低耦合)。
- Presenter:作为View与Model的中间纽带,通知model获取数据,model获取数据完之后,通知View更新界面。
- View interface:需要View实现的借口,View通过View interface与Presenter进行交互,降低耦和,方便进行单元测试。
(3)MCV—MVP:当我们将Activity复杂的逻辑处理移至另一个类(Presenter)中时,Activity实际上就是MVP模式中的View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂逻辑交由Presenter处理)。
MVP的Presenter是架构的控制者,承担了大量的逻辑操作,因此APP中引入MVP的原因,是为了将此前在Activity中包含的大量逻辑操作放到控制层,避免Activity的臃肿。
下面通过一个获取电话号码归属地的例子来说明(网络请求部分使用Retrofit结合Rxjava):
电话号码归属地API地址:
http://api.k780.com:88/?app=phone.get&phone=18926502506&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json
成功后返回一段json数据如下:
{“success”:”1”,”result”:{“status”:”ALREADY_ATT”,”phone”:”18926502506”,”area”:”0755”,”postno”:”518000”,”att”:”中国,广东,深圳”,”ctype”:”中国电信189卡”,”par”:”1892650”,”prefix”:”189”,”operators”:”中国电信”,”style_simcall”:”中国,广东,深圳”,”style_citynm”:”中华人民共和国,广东省,深圳市”}}
号码不存在时返回一段json数据如下:
{“success”:”1”,”result”:{“status”:”NOT_ATT”,”phone”:”1892652506”,”msg”:”暂无相关归属地信息”}}
代码如下:
MainActivity:
public class FirstActivity extends AppCompatActivity implements IBaseView , View.OnClickListener
{
private EditText input;
private Button search;
private TextView show;
private UserPresenter userPresenter;
private ProgressDialog progressDialog;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
initView();
}
private void initView()
{
input = (EditText) findViewById(R.id.number);
search = (Button) findViewById(R.id.search);
show = (TextView) findViewById(R.id.show);
userPresenter = new UserPresenter(this);
search.setOnClickListener(this);
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Loading");
AppContextUtil.init(FirstActivity.this);
}
@Override
public String getNumber()
{
return input.getText().toString().trim();
}
@Override
public void setSearchResult(String result)
{
show.setText(result);
}
@Override
public void showProgress()
{
progressDialog.show();
}
@Override
public void hideProgress()
{
progressDialog.dismiss();
}
@Override
public void onClick(View view)
{
switch (view.getId())
{
case R.id.search:
userPresenter.loadData(getNumber());
break;
}
}
}
View Interface:
public interface IBaseView<T>
{
String getNumber();
void setSearchResult(String result);
void showProgress();
void hideProgress();
}
Presenter类:
public class UserPresenter
{
private IBaseView iBaseView;
private RequestModel requestModel;
public UserPresenter(IBaseView iBaseView)
{
this.iBaseView = iBaseView;
requestModel = RequestModel.getInstance();
}
public void loadData(String number)
{
iBaseView.showProgress();
requestModel.getPhoneAlt(number, new Subscriber<Result>()
{
@Override
public void onCompleted()
{
iBaseView.hideProgress();
}
@Override
public void onError(Throwable e)
{
iBaseView.hideProgress();
if (NetUtil.isNetworkConnected())
{
Toast.makeText(AppContextUtil.getInstance() , e.getMessage() , Toast.LENGTH_SHORT).show();
}
else
{
iBaseView.setSearchResult("请检查您的网络是否连接!!!");
}
}
@Override
public void onNext(Result result)
{
if (result.getStatus().equals("NOT_ATT"))
{
iBaseView.setSearchResult("请输入正确的手机号码");
}
else
{
iBaseView.setSearchResult(result.toString());
}
}
});
}
}
Retrofit网络请求Service接口:
public interface NumberService
{
@GET("/")
Observable<NumberEntity<Result>> getPhoneNumber(@Query("app") String app ,
@Query("phone") String phone ,
@Query("appkey") String appKey ,
@Query("sign") String sign ,
@Query("format") String format);
}
RequestModel类:
public class RequestModel
{
private static final String BASE_URL = "http://api.k780.com:88";
private static final String APP = "phone.get";
private static final String APP_KEY = "10003";
private static final String SIGN = "b59bc3ef6191eb9f747dd4e83c99f2a4";
private static final String FORMAT = "json";
private static final int DEFAULT_TIMEOUT = 5;
private Retrofit retrofit;
private NumberService numberService;
private RequestModel()
{
//手动创建一个OkHttpClient并设置超时
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.connectTimeout(DEFAULT_TIMEOUT , TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(clientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
numberService = retrofit.create(NumberService.class);
}
private static class SingletonHolder
{
private static final RequestModel INSTANCE = new RequestModel();
}
public static RequestModel getInstance()
{
return SingletonHolder.INSTANCE;
}
public void getPhoneAlt(String number , Subscriber<Result> subscriber)
{
numberService.getPhoneNumber(APP , number , APP_KEY , SIGN , FORMAT)
.map(new NumberResultFunc<Result>())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
private class NumberResultFunc<T> implements Func1<NumberEntity<T> , T>
{
@Override
public T call(NumberEntity<T> numberEntity)
{
return numberEntity.getResult();
}
}
}
实体类比较简单,这里不再贴出,大家可以根据返回的json数据自行编写。最后贴出两个工具类:分别用于获取网络连接状态和获取Context对象。
public class NetUtil
{
private NetUtil()
{
}
public static boolean isNetworkConnected()
{
if (AppContextUtil.getInstance() != null)
{
ConnectivityManager connectivityManager = (ConnectivityManager) AppContextUtil.getInstance()
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null)
{
return networkInfo.isAvailable();
}
}
return false;
}
}
public class AppContextUtil
{
private static Context sContext;
private AppContextUtil()
{
}
public static void init(Context context)
{
sContext = context;
}
public static Context getInstance()
{
if (sContext == null)
{
throw new NullPointerException("the Context is null , please init AppContextUtil in Activity first.");
}
return sContext;
}
}