Android development framework --MVP2

Continued on an Android framework mode (1) entry on -MVP this one introduces a Demo MVP of the problems, and how to improve. On the other hand, I will introduce problems MVP implementation prone to elicit details MVP in noteworthy. Through this article, you will know how to make better use of MVP. (This article needs to have a certain foundation RxJava, if not skip watching the final few suggestions)

Optimizing the use of open source library
to improve the readability of asynchronous logic

In Android, multithreaded asynchronous code is usually unavoidable, for example in order to ensure the TextView View setText call in the main thread, the View layer have code

public class View extends Activity implements IView{
    @override
    public void setData(String data){  
        //保证对View的修改运行在主线程上
        runOnUiThread(new Runnable(){     //ugly
                    public void run(){
                        text.setText(data);
                    }
                }
            );
    }
}

Another example is the Model layer in order to avoid time-consuming operations in the main thread.

public class Model implements IModel{
    void getData(ICallback callback){
        execute(new Runnable(){     
            public void run(){             //ugly
                ... //这里是耗时操作
                callback.onResult("hello world");    //10 返回数据
            }
        };
    }
}

The above is the use of multi-threaded Java original wording written. Of course, such a simple code written in this way is no problem. However, when really being used in the project, you may want to consider several other issues: 
1. The error handling. Model data may come from unreliable data sources, such as network, then you have to add additional error handling code 
2. thread switch. For example, when a certain increase in APP function, certain A worker overwhelmed, the thread A set of distribution in certain tasks to other threads to execute. 
3. cancel the operation. After requesting data from the Model, but the Model return to the previous data, due to some change in the state of View, (such as the Home key Activity to the background), then the View does not need to receive these data, we need to stop these data to the View. 
When added to the existing codes of the logic, asynchronous code is bloated. 
So, in the MVP should be how to solve this problem?

1. The allocation of responsibilities to the full thread Presenter

Presenter chosen in the allocating thread, because the intermediary with Model View Presenter is, it is possible to extract each asynchronous logic from the code with the Model View, unified into a class (Presenter) to facilitate maintenance. So, on one of the Demo should be adjusted as follows:

//Model层
public class Model implements IModel{
    String getData(){         //不需要Callback,因为逻辑已经一到Presenter中
         ... //耗时操作
         return "hello world";    //删除异步逻辑,调整到Presenter中
    }
}
//View 层
public class View extends Activity implements IView{
    @override
    public void setData(String data){           
          text.setText(data);             //删除异步逻辑,调整到Presenter中
    }
} 
//Presenter 层
public class Presenter implements IPresenter{
     ...
    @override
    public void performOnClick(){
        execute(new Runnable(){     
                public void run(){             //ugly!!! 从Model中移来的异步代码
                    ... //这里是耗时操作
                    String data=model.getData();   //不需要Callback类,直接返回
                    String dataFromPresenter=data+" from presenter"; //8 加工数据
                    runOnUiThread(new Runnable(){     //ugly!!!从View中移来的异步代码
                            public void run(){
                                view.setData(dataFromPresenter);
                            }
                        }
                    );                     
                 }
            };
        }  

    }
}

It can be seen to move to the rear Presenter asynchronous logic, there are two major changes 
1. Model.Callback classes are no longer needed, directly back to the data obtained after the Model 
2.View, asynchronous code to the mobile Presenter Model layer, Presenter can control Model, View of execution threads 
to this, we have to move Presenter asynchronous logic control, this step is mainly to prepare for the next step RxJava, the purpose is to allow the flow-through RxJava not written off.

2. RxJava optimization of nested logic Presenter

Presenter with RxJava implementation code is relatively simple, understand RxJava can change the following code:

//Presenter 层
public class Presenter implements IPresenter{
     ...
    @override
    public void performOnClick(){
        //RxJava将原本栈式的逻辑改成流式的逻辑,按顺序略读即可,当然你也可以把生成Observable得代码移动到Model中。
        Observable
            .create(new OnSubscribe<String>(){
                public void call(Subscriber<? super String> subscriber){ //调用Model
                    String data=model.getData();
                    subscriber.onNext(data);
                    subscriber.onCompleted();
                }           
            })
            .subscribeOn(Schedulers.io())  //配置调用Model的线程
            .observeOn(AndroidSchedulers.mainThread()) //配置调用View的线程
            .subscribe(new Subscriber<String>(){   
                @Override
                public void onCompleted(){}
                @Override
                public void onError(Throwable throwable){}  //错误监听,当然在这里永远调用不到
                @Override
                public void onNect(String s){
                    view.setData(dataFromPresenter);  //调用View
                }

            });  

    }
}

Eliminate coupling

Despite the previous article, we have been in accordance with the Dependency Inversion principle to use IView, IPresenter, IModel easy to change the interface so that the layers MVP. But coupling problem still exists. We have to expand the analysis of the View layer, Presenter layer

//View层
private class ViewA implements IView{
    private IPresenter mPresenter=new PresenterA(this);// 1
    ...
}
//Presenter
private class PresenterA implments IPresenter{
    private IView mView;
    public PresenterA(IView view){
        mView=view;
    }
    ...
}

For at 1, we assume that one day NA PresenterA, to use PresenterB, we just need to put PresenterB, it seems very simple. 
However, in the hierarchy, each layer should be independent. Modify Presenter layer completely should not affect the View, but in this case, we have to modify the Presenter layer has no choice but to simultaneously change the View layer, the View layer with Presenter remain coupled, of course, Presenter with Model there is such a problem. 
The solution requires more than aid dependency injection tool, the current mainstream of dependency injection tool is Dagger2, maintained by Google. If you did not know Dagger2, you can look at this a popular open-source Android tools (1) -Dagger2 entry

maintain

For Android, the process even kill off where the Activity, Activity status is still retained, unless the user closes the subjective Activity. Presenter, Model Android itself is not available, we have to add extra code to them to ensure their success in the process of restarting the process of maintaining state.

Automatic restart

Activity Activity system will restart in the following cases: 
1. Activity in screen flip 
2. Activity long time (30 minutes) and then into the back of the Activity (2.3 or older) 
3. Insufficient memory, Activity was killed and then open the Activity 
in this case, restart the Activity will be a new instance and will use savedInstanceState restored to the state before the restart. 
Activity Android framework to provide the ability to go beyond the recovery process, and Activity and often are we in for MVP V. If we do not do something special at the same time the PM process, then restart when Activity, uncoordinated MVP three states. 
To illustrate this incongruity, a simple example: TextView initially zero, press the download button, turn a download Presenter thread layer, each second layer acquires download progress from the Model TextView and displayed, and then press the home button to the background, out of memory, Activity was killed after the restart Activity, Activity due savedInstanceState, the progress bar stays at a location process is killed, but found progress bar does not move. This is because restarting Activity, Presenter thread layer is not re-start.

Solve it yourself

In order to solve the above problems, we must put a running thread is bound to an id number, stored in savedInstanceState, the process will restart when the resurrection of the corresponding thread id number, I wrote in this general idea, the details involved in the recycling RxJava :( used to not write RxJava)

// general idea is to View incoming event corresponds to a type int Id, Id deposit equivalent to save this event, Id keep this process before death, restart the process by reading this Id restart this event.

public class Presenter implements IPresenter{
    //注册一些可能重启进程后可能被重启的线程 (id,Thread)
    private SparseArray<Function0<Subscription>> mRestartables=new SparseArray<>();
    //运行过的线程ID
    private List<Integer> mRunningIds=new ArraysList<>();
    //运行过的线程
    private SparseArray<Subscription> mReqeusts=new SparseArray<>();
    ...
    public void onCreate(Bundle savedInstanceState){
        //注册线程,绑定从DONWLOAD到线程的映射
        mRestartables.put(DOWNLOAD_ID,new Function0<Subscription>(){
            Subscription call(){
                return mModel
                        .getDownloadObservable()
                        .observeOn(AndroidSchedulers.mainThread())
                        .doOnSubscribe(()->)
                        .subscribe(new Action1<Integer>(){
                            public void call(Integer progress){
                                mView.setProgress();
                            }
                        });
            }
        });
        //Activity重启,可能有未完成线程可以在这里得到重启
        if (savedInstanceState==null){
            for (int id:savedInstanceState.getIntArrayList(KEY_RUNNINGS)){ 
                mRestartables.get(id).call();
                mRunningIds.add(id);
                mReqeusts.add(id);
            }
        }
    }
    public void onSaveInstanceState(Bundle outState){
        //保存未完成的线程,进程重启后能够自动重启这部分线程
        for (int id:mRunningIds){
            if (mRequests.get(id).isUnsubcribed()){
                mRunningIds.remove((Integer)id);
            }
        }
        outState.put(KEY_RUNNINGS,mRunningIds);
    }
    public void startDownload(){
        //有下载任务时候,启动线程,并保留线程索引
        Subscription downloadSubscription=mRestartables.get(DOWNLOAD_ID).call();
        mRequests.put(DOWNLOAD_ID,downloadSubscription);
    }
}

 After more than a relatively cumbersome process, thread gets restarted, but there is another problem: 
Activity flip the screen for a long time back when re-entering the Activity, Activity will reCreate, it will open up a new thread manually re not used before thread, because they can not get a reference to the last thread and start running. This will lead to serious problems: on the one hand, if the old thread before you is not closed, that old thread holds the old View causes a memory leak; on the other hand, even before the thread can be turned, the thread is not constantly reboot need, a waste of resources. 
So, I recommend Presenter will make a RetainFragment, the following forms:

//Presenter变成Retained Fragment,你将不需要再View中显式调用Presenter.onCreate();
public class Presenter extends Fragment implment IPresenter{
    ...
    public onCreate(Bundlet bundle){
        setRetainInstance(true);
    }
}
public class View extends Activity implmetns IView{
    private IPresenter mPresenter=new Presenter(this); 
    public void onCreate(Bundle b){
        super.onCreate(b);
        //添加Presenter,将不需要再View中显式调用Presenter.onCreate()
        getFragmentManager().beginTransaction().put((Fragment)mPresenter,"Presenter").commit();
    }
}

After such a setting is changed (turn screen, etc.) will not cause the thread to restart.

Quick Solution

Yes, I have to admit that the above-mentioned solution is troublesome, a framework urgent need to solve this problem occurs. A foreigner has written this framework that I did not see anything wrong place (there before threading through the bug I mentioned the name of a foreigner issue has been resolved). This framework is called Nucleus, I also use and recommend it to everyone.

Other details of the recommendations

1. Design View interface when the need to consider this interface, just to View interface to transfer data and not to let the Presenter to control, but also pay attention Presenter only get data from the Model, for example:

public interface IView{
     void startSomeActivity(); //错,Presenter不能控制View,只能设置数据
     void showTextView(); //错,Presenter不能控制View,只能设置数据
     void setActivityPrepareData(DownloadInfo info);//对
     void setViewAdapterData(List<Data> listData);//对
     void showToast(int resId);//错  ResourceId不是从Model层中获取,View选取哪个ResourceId来显示是View自己的责任
}

2.Presenter to decouple the two by two MV, best not to have other responsibilities 
a) organize and save the logical thread 
b) obtaining from Model Model layer POJO POJO or substantially transformed into data element View layer, before and after the two attention a POJO must be different. Model View layer is not used directly POJO

3. Name the interface layers prevent blur. for example

public interface IPresenter{
    void init(); //错 模糊不清,View层根本不知道应该什么时候调用init()
    void onCreate(); //对, View层知道在onCreate的时候调用Presenter的onCreate()方法
}

--------------------- 
Author: ancient bell 
Source: CSDN 
Original: https: //blog.csdn.net/duo2005duo/article/details/50778321 
copyright notice : This article is a blogger original article, reproduced, please attach Bowen link!

Guess you like

Origin blog.csdn.net/qq_27981847/article/details/92585364