很早之前寫過一篇關(guān)于MVP模式的文章,大家都知道MVP模式相對(duì)于MVC來說更加的解耦鉴扫,當(dāng)然了現(xiàn)在還有比較熱的MVVM模式等帐要,每一種新的架構(gòu)模式的產(chǎn)生則表示之前的模式在某種程度上并不能很好的滿足項(xiàng)目的需要,對(duì)于MVVM模式目前還沒有接觸蒲肋,表示以后會(huì)抽時(shí)間研究乒省。這里為什么會(huì)再次寫關(guān)于MVP模式相關(guān)的文章呢巧颈?首先,之前文章是在CSDN上寫的袖扛,這里表示懶得直接搬過來砸泛,所以做個(gè)復(fù)習(xí)十籍;其次,之前的寫法都是針對(duì)單個(gè)P來實(shí)現(xiàn)的唇礁,比如我們?cè)谝粋€(gè)activity中同時(shí)實(shí)現(xiàn)登錄和注冊(cè)勾栗,那么這個(gè)activity就需要對(duì)應(yīng)兩個(gè)presenter,那么按照之前的實(shí)現(xiàn)方式是不支持的盏筐,所以這里作為一個(gè)完善围俘。
在此之前還是先來復(fù)習(xí)下之前寫的MVP模式吧,還是用干貨集中營的api琢融,再次表示感謝大家都知道P層是作為V層和M層之間的橋梁界牡,也就是說,當(dāng)我們進(jìn)行網(wǎng)絡(luò)請(qǐng)求的操作是在M層做的漾抬,然后將請(qǐng)求得到的結(jié)果回調(diào)到P層宿亡,最后在P層再將結(jié)果回調(diào)到V層。那么會(huì)不會(huì)出現(xiàn)這樣的問題纳令,當(dāng)網(wǎng)絡(luò)請(qǐng)求結(jié)果還沒回調(diào)回來時(shí)我們的V層(activity)由于某些原因被銷毀了她混,但是P層還持有V層的引用,導(dǎo)致activity不能被gc回收泊碑,也就出現(xiàn)了內(nèi)存泄漏,當(dāng)結(jié)果回來時(shí)P層將拿到的結(jié)果再給V層毯欣,這樣也有可能會(huì)導(dǎo)致應(yīng)用閃退等馒过。所以我們應(yīng)該讓P層也擁有V層同樣的生命周期,這里我們簡單定義下presenter的基類BasePresenter
public abstract class BasePresenter<V> {
private V mvpView;
protected void attachView(V mvpView){
this.mvpView = mvpView;
}
protected void detachView(){
mvpView = null;
}
protected V getMvpView(){
return mvpView;
}
}
你也可以定義一個(gè)view的基類酗钞,比如請(qǐng)求加載框的顯示與隱藏腹忽,數(shù)據(jù)加載成功或者失敗的顯示以及一些通用的toast提示等等,這里就不再贅述
接下來我們定義下activity的基類
public abstract class BaseMvpActivity<V, P extends BasePresenter<V>> extends AppCompatActivity{
private P presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//布局
setContentView(getLayoutId());
//獲取presenter
presenter = createPresenter();
if (presenter != null){
presenter.attachView((V) this);
}
//初始化等操作
initView(savedInstanceState);
//findViewById
findViewById();
//設(shè)置監(jiān)聽事件
setListener();
//setViews
setViews();
//請(qǐng)求數(shù)據(jù)
setRequestDatas();
}
protected abstract int getLayoutId();
protected abstract P createPresenter();
protected void findViewById(){}
protected abstract void initView(Bundle savedInstanceState);
protected void setListener(){}
protected void setViews(){}
protected void setRequestDatas(){}
protected P getPresenter(){
return presenter;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null){
presenter.detachView();
}
}
}
為了方便管理各層之間定義的接口砚作,接下來我們需要定義一個(gè)契約類來統(tǒng)一管理
public class MZContract {
//view interface
public interface MZViewI{
void onSuccess(List<MZBean> lists);
void onFailed(String msg);
}
//presenter interface
public interface MZPresenterI{
void setMZDatas(Context context, String category, int count, int size);
}
//model
public interface MZModelI{
void requestDatas(Context context, String category, int count, int size, ResultStatuI resultStatu);
}
public interface ResultStatuI{
void onSuccess(List<MZBean> lists);
void onFailed(String msg);
}
}
看下我們進(jìn)行網(wǎng)絡(luò)請(qǐng)求的model層的處理
public class InfoModel implements MZContract.MZModelI{
@Override
public void requestDatas(Context context, String category, int count, int size, final MZContract.ResultStatuI resultStatu) {
RxRetrofitManger.getInstance().getService()
.getMZInfo(category, count, size)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new RxObserver<BaseBean<List<MZBean>>>(context, false) {
@Override
public void onSuccess(BaseBean<List<MZBean>> response) {
resultStatu.onSuccess(response.getResults());
}
@Override
public void onFailed(BaseBean<List<MZBean>> response) {
resultStatu.onFailed("獲取數(shù)據(jù)失敗");
}
});
}
}
代碼中簡單封裝了RxJava+OkHttp+Retrofit實(shí)現(xiàn)的網(wǎng)絡(luò)請(qǐng)求框架窘奏,感興趣的可以自己查看下代碼。
接著我們?cè)賮砜聪潞蚼odel交互的presenter的具體實(shí)現(xiàn)類
public class MZPresenter extends BasePresenter<MZContract.MZViewI> implements MZContract.MZPresenterI{
private InfoModel model;
public MZPresenter(){
model = new InfoModel();
}
@Override
public void setMZDatas(Context context, String category, int count, int size) {
if (getMvpView() != null){
model.requestDatas(context, category, count, size, new MZContract.ResultStatuI() {
@Override
public void onSuccess(List<MZBean> lists) {
if (getMvpView() != null){
getMvpView().onSuccess(lists);
}
}
@Override
public void onFailed(String msg) {
if (getMvpView() != null){
getMvpView().onFailed(msg);
}
}
});
}
}
}
可以看出葫录,我們?cè)谶M(jìn)行網(wǎng)絡(luò)請(qǐng)求以及結(jié)果回調(diào)時(shí)都進(jìn)行了判斷着裹,如果view不存在了就不再進(jìn)行后續(xù)操作。其實(shí)你還可以定義個(gè)方法去取消網(wǎng)絡(luò)請(qǐng)求等等米同,這里示例只是用來簡單解釋說明骇扇,如需更加詳細(xì)、完善的想法面粮,可自行添加少孝。
接著我們?cè)賮砜聪聉層和p層是如何交互的
public class MainActivity extends BaseMvpActivity<MZContract.MZViewI, MZPresenter> implements MZContract.MZViewI{
private RecyclerView recyclerView;
private MZListsAdapter mzAdapter;
private List<MZBean> lists;
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected MZPresenter createPresenter() {
return new MZPresenter();
}
@Override
protected void initView(Bundle savedInstanceState) {
}
@Override
protected void findViewById() {
recyclerView = findViewById(R.id.mz_recycler_view);
}
@Override
protected void setViews() {
lists = new ArrayList<>();
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(gridLayoutManager);
mzAdapter = new MZListsAdapter(this, lists);
recyclerView.setAdapter(mzAdapter);
}
@Override
protected void setRequestDatas() {
getPresenter().setMZDatas(this,"福利", 10, 1);
}
@Override
public void onSuccess(List<MZBean> lists) {
if (lists != null && lists.size() > 0){
mzAdapter.setDatas(lists);
}
}
@Override
public void onFailed(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
我們?cè)赩層通過createPresenter()進(jìn)行和P層的綁定,對(duì)于解除綁定我們放在了base基類中去操作了熬苍,同時(shí)在解綁時(shí)我們也可以再增加個(gè)取消網(wǎng)絡(luò)請(qǐng)求的操作等稍走。
從上面的案例可以看到并不能滿足一個(gè)V對(duì)應(yīng)多個(gè)P的情況,所以,接下來我們就通過注解的方式實(shí)現(xiàn)這種需求婿脸,關(guān)于注解我們可以看下這位大牛的文章粱胜,講解的很清楚深入理解Java注解類型(@Annotation),還要感謝L_Xian提供的通過注解的方式實(shí)現(xiàn)一個(gè)V對(duì)應(yīng)多個(gè)P的案例盖淡,之前也學(xué)習(xí)過注解的一些相關(guān)知識(shí)年柠,剛好通過這個(gè)案例加以實(shí)踐。首先我們先看下修改之后的View層都發(fā)生了哪些變化
@CreatePresenter(presenters = {MZPresenter.class, ArticlePresenter.class})
public class MainActivity extends BaseMvpActivity implements MZContract.MZViewI, View.OnClickListener{
private LinearLayout llTitle;
private RecyclerView recyclerView;
private MZListsAdapter mzAdapter;
private List<MZBean> lists;
private ArticleListsAdapter articleAdapter;
private List<MZBean> articleLists;
private boolean isShowArticle;
@PresenterFiles
MZPresenter presenter;
@PresenterFiles
ArticlePresenter articlePresenter;
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
/*@Override
protected MZPresenter createPresenter() {
return new MZPresenter();
}*/
@Override
protected void initView(Bundle savedInstanceState) {
}
@Override
protected void findViewById() {
llTitle = findViewById(R.id.ll_title);
recyclerView = findViewById(R.id.mz_recycler_view);
}
@Override
protected void setViews() {
lists = new ArrayList<>();
articleLists = new ArrayList<>();
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(gridLayoutManager);
mzAdapter = new MZListsAdapter(this, lists);
recyclerView.setAdapter(mzAdapter);
}
@Override
protected void setListener() {
llTitle.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.ll_title:
getArticleDatas();
break;
}
}
private void getArticleDatas() {
isShowArticle = true;
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
articleAdapter = new ArticleListsAdapter(this, articleLists);
recyclerView.setAdapter(articleAdapter);
//請(qǐng)求
articlePresenter.setMZDatas(this, "Android", 10, 1);
}
@Override
protected void setRequestDatas() {
presenter.setMZDatas(this,"福利", 10, 1);
//getPresenter().setMZDatas(this,"福利", 10, 1);
}
@Override
public void onSuccess(List<MZBean> lists) {
if (lists != null && lists.size() > 0){
if (isShowArticle){
articleAdapter.setArticleDatas(lists);
}else {
mzAdapter.setDatas(lists);
}
}
}
@Override
public void onFailed(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
由于獲取妹紙字段和獲取文章字段一樣褪迟,所以為了偷懶在很多地方都共用了冗恨,這不重要,重要的是理解注解的實(shí)現(xiàn)方式味赃。從上面代碼中可以看出掀抹,之前我們是通過createPresenter()方法去new出我們要和view層進(jìn)行綁定的presenter實(shí)例對(duì)象,現(xiàn)在我們是通過@CreatePresenter(presenters = {MZPresenter.class, ArticlePresenter.class})注解的方式將我們的presenter傳過去心俗,我們會(huì)在注解處理器中對(duì)傳進(jìn)來的這些presenter進(jìn)行實(shí)例化傲武,這種使用方式和使用阿里開源的ARouter使用類似,其次是我們發(fā)現(xiàn)多了兩個(gè)加了@PresenterFiles注解的成員變量城榛,這個(gè)@PresenterFiles注解和Android系統(tǒng)自帶的@Test注解類似揪利,是一個(gè)標(biāo)記注解,我們會(huì)在注解處理器中通過反射先找到所有的成員變量字段狠持,然后判斷每個(gè)字段上是否存在@PresenterFiles注解疟位,存在的話就通過key找到存儲(chǔ)的相對(duì)應(yīng)的value值,這個(gè)value值也就是前面?zhèn)鬟M(jìn)去的presenter實(shí)例對(duì)象(前面我們說了@CreatePresenter注解喘垂,其作用就是去實(shí)例化傳進(jìn)去的這些presenter甜刻,通過map去進(jìn)行以類名為key,實(shí)例化對(duì)象為value的存儲(chǔ))正勒,所以要注意下這個(gè)key必須要保持一致得院,因?yàn)槲覀兌际怯妙惷鳛閗ey。
接下來我們就來看下這兩個(gè)注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatePresenter {
Class<?>[] presenters();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PresenterFiles {
}
以及處理這倆注解的處理器
/**
* 運(yùn)行時(shí)注解處理器
*/
public class PresenterCreator {
private Activity activity;
private Fragment fragment;
private Class<?> mClass;
private HashMap<String, BasePresenter> map = new HashMap<>();
public static PresenterCreator init(Activity activity){
return new PresenterCreator(activity, null);
}
public static PresenterCreator init(Fragment fragment){
return new PresenterCreator(null, fragment);
}
private PresenterCreator(Activity activity, Fragment fragment){
if (activity != null){
mClass = activity.getClass();
dealPresenterAnnotationOfType(activity);
dealPresenterOfField(activity);
}
if (fragment != null){
mClass = fragment.getClass();
dealPresenterAnnotationOfType(fragment);
dealPresenterOfField(fragment);
}
}
private <P extends BasePresenter> void dealPresenterAnnotationOfType(Object view){
CreatePresenter createPresenter = mClass.getAnnotation(CreatePresenter.class);
if (createPresenter != null){
Class<P>[] presenterClass = (Class<P>[]) createPresenter.presenters();
for (Class<P> clazz : presenterClass){
try {
P presenter = clazz.newInstance();
if (presenter != null){
map.put(clazz.getCanonicalName(), presenter);
//P和V綁定
presenter.attachView(view);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
private <P extends BasePresenter> void dealPresenterOfField(Object view){
//首先通過反射先獲取所有成員字段
Field[] fields = mClass.getDeclaredFields();
for (Field field : fields){
//再獲取每一個(gè)字段上面的注解
Annotation[] anns = field.getDeclaredAnnotations();
if (anns.length < 1){//如果此字段上沒有注解章贞,則會(huì)返回一個(gè)長度為0的數(shù)組
continue;
}
//因?yàn)橐粋€(gè)字段上可能會(huì)有多個(gè)注解
if (anns[0] instanceof PresenterFiles){
P presenter = (P) map.get(field.getType().getCanonicalName());
if (presenter != null){
try {
//給字段賦值
field.setAccessible(true);
field.set(view, presenter);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
public void detachView(){
for (Map.Entry<String, BasePresenter> entry : map.entrySet()){
BasePresenter presenter = entry.getValue();
if (presenter != null){
presenter.detachView();
}
}
}
}
然后我們?cè)赽aseactivity基類中去啟動(dòng)這個(gè)注解處理器
public abstract class BaseMvpActivity extends AppCompatActivity{
//private P presenter;
private PresenterCreator presenterCreator;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//布局
setContentView(getLayoutId());
//獲取presenter
presenterCreator = PresenterCreator.init(this);
/* presenter = createPresenter();
if (presenter != null){
presenter.attachView((V) this);
}*/
//初始化等操作
initView(savedInstanceState);
//findViewById
findViewById();
//設(shè)置監(jiān)聽事件
setListener();
//setViews
setViews();
//請(qǐng)求數(shù)據(jù)
setRequestDatas();
}
protected abstract int getLayoutId();
//protected abstract P createPresenter();
protected void findViewById(){}
protected abstract void initView(Bundle savedInstanceState);
protected void setListener(){}
protected void setViews(){}
protected void setRequestDatas(){}
/*protected P getPresenter(){
return presenter;
}*/
@Override
protected void onDestroy() {
super.onDestroy();
if (presenterCreator != null){
presenterCreator.detachView();
}
/*if (presenter != null){
presenter.detachView();
}*/
}
}
看到這里你可以能有些疑慮祥绞,這種方式會(huì)導(dǎo)致性能損耗吧?是的鸭限,因?yàn)槲覀兌x的這些注解@Retention(RetentionPolicy.RUNTIME)可以看出是運(yùn)行時(shí)注解就谜,期間通過反射進(jìn)行一系列的操作必然會(huì)導(dǎo)致性能上有所損耗,那應(yīng)該怎么辦呢里覆?相信大家都用過黃油刀ButterKnife丧荐,他們有個(gè)@BindView注解,點(diǎn)擊進(jìn)去查看下這個(gè)注解定義的是@Retention(CLASS)喧枷,也就是編譯時(shí)注解虹统,導(dǎo)致可能會(huì)在編譯期時(shí)間會(huì)稍微長些弓坞,但是在運(yùn)行時(shí)是不會(huì)有性能上的損耗的,那么他們是怎么做的呢车荔?以后我會(huì)抽個(gè)時(shí)間研究下渡冻,等研究明白了再對(duì)此方式進(jìn)行優(yōu)化吧,最后引用zejian_博客中的關(guān)于@Retention中的三個(gè)可取值進(jìn)行解釋說明
@Retention用來約束注解的生命周期忧便,分別有三個(gè)值族吻,源碼級(jí)別(source),類文件級(jí)別(class)或者運(yùn)行時(shí)級(jí)別(runtime)珠增,其含有如下:
SOURCE:注解將被編譯器丟棄(該類型的注解信息只會(huì)保留在源碼里超歌,源碼經(jīng)過編譯后,注解信息會(huì)被丟棄蒂教,不會(huì)保留在編譯好的class文件里)
CLASS:注解在class文件中可用巍举,但會(huì)被VM丟棄(該類型的注解信息會(huì)保留在源碼里和class文件里,在執(zhí)行的時(shí)候凝垛,不會(huì)加載到虛擬機(jī)中)懊悯,請(qǐng)注意,當(dāng)注解未定義Retention值時(shí)梦皮,默認(rèn)值是CLASS炭分,如Java內(nèi)置注解,@Override剑肯、@Deprecated捧毛、@SuppressWarnning等
RUNTIME:注解信息將在運(yùn)行期(JVM)也保留,因此可以通過反射機(jī)制讀取注解的信息(源碼退子、class文件和執(zhí)行的時(shí)候都有注解的信息),如SpringMvc中的@Controller型将、@Autowired寂祥、@RequestMapping等。