Talking about the MVP mode in Android

Insert image description here

It’s the Dragon Boat Festival holiday and it’s raining, so I’m blogging at home. This blog will explain the application of the MVP model in Android.

This article will explain the MVP model from the following aspects:

\1. Introduction to MVP

\2. Why use MVP mode

\3. MVP pattern example 4. Memory leak problem in MVP 1. MVP introduction:

As UI creation technology becomes more and more powerful, the UI layer also performs more and more responsibilities. In order to better subdivide the functions of View and Model, let View focus on processing data visualization and interaction with users, while letting Model only deal with data processing, MVP (Model-View) based on the MVC concept -Presenter) mode came into being.

There are usually 4 elements in the MVP model:

(1) View: Responsible for drawing UI elements and interacting with users (reflected as Activity in Android);

(2) ViewInterface: The interface that needs to be implemented by View. View interacts with Presenter through View interface to reduce coupling and facilitate unit testing;

(3) Model: responsible for storing, retrieving, and manipulating data (sometimes also implementing a Model interface to reduce coupling);

(4) Presenter: As the intermediate link between View and Model interaction, it handles the logic responsible for interacting with users.

\2. Why use MVP mode

img

In Android development, Activity is not a Controller in the standard MVC model. Its primary responsibility is to load the layout of the application and initialize the user interface, accept and process operation requests from the user, and then respond. As the complexity of the interface and its logic continues to increase, the responsibilities of the Activity class continue to increase, causing it to become huge and bloated. When we move the complex logic processing to another class (Presneter), the Activity is actually the View in the MVP model. It is responsible for initializing the UI elements, establishing the association between the UI elements and the Presenter (Listener and the like), and at the same time itself It will also handle some simple logic (complex logic is handled by the Presenter).

In addition, think back to how you unit tested the code logic when developing Android applications? Do you need to deploy the application to the Android emulator or a real device every time, and then test it by simulating user operations? However, due to the characteristics of the Android platform, each deployment consumes a lot of time, which directly leads to a reduction in development efficiency. In the MVP model, the Presenter that handles complex logic interacts with the View (Activity) through the interface. What does this mean? It shows that we can implement this interface through a custom class to simulate the behavior of Activity to unit test the Presenter, saving a lot of deployment and testing time.

\3. MVP pattern example Okay, after roughly understanding the basic concepts of the MVP pattern, we will use the MVP pattern to write a small example.

The structure of the package is shown in the figure below: Effect display:

img

img

Let's start to explain the steps of the mvp mode:

1) Create the interface class of the view and define abstract methods according to the business

<span style="font-size:18px;">public interface IUserView {
    
    
	//显示进度条
	void showLoading();
	//展示用户数据
	void showUser(List<User> users);
	
}</span>

2) Create the interface class of the model and define abstract methods according to the business

Define a method for loading data, and also set up a listener for loading completion. The abstract method complete is set in the listener for callback after the loading is completed.

public interface IUserModel {
    
    
	//加载用户信息的方法
	void loadUser(UserLoadListenner listener);
	//加载完成的回调
	interface UserLoadListenner{
    
    
		void complete(List<User> users);
	}
}

3) Create the implementation class of the model and implement the abstract methods. The user class is created in the bean package according to the needs.

public class UserModelImpl implements IUserModel{
    
    

	@Override
	public void loadUser(UserLoadListenner listener) {
    
    
		//模拟加载本地数据
		List<User> users = new ArrayList<User>();
		users.add(new User("姚明", "我很高", R.drawable.ic_launcher));
		users.add(new User("科比", "怒砍81分", R.drawable.ic_launcher));
		users.add(new User("詹姆斯", "我是宇宙第一", R.drawable.ic_launcher));
		users.add(new User("库里", "三分我最强", R.drawable.ic_launcher));
		users.add(new User("杜兰特", "千年老二", R.drawable.ic_launcher));
		if(listener != null){
    
    
			listener.complete(users);
		}
	}
	
}

After loading the data, call back the complete method in the listener.

4) Create present, pass in the implementation class of view in the constructor, and add the implementation class of model in new, and create a method load to realize the communication bridge between view and model.

public class Presenter1 {
    
    
	//view
	IUserView mUserView;
	//model
	IUserModel mUserModel = new UserModelImpl();
	//ͨ通过构造函数传入view
	public Presenter1(IUserView mUserView) {
    
    
		super();
		this.mUserView = mUserView;
	}
//加载数据
	public void load() {
    
    
		//加载进度条
		mUserView.showLoading();
		//model进行数据获取
		if(mUserModel != null){
    
    
			mUserModel.loadUser(new UserLoadListenner() {
    
    
				
				@Override
				public void complete(List<User> users) {
    
    
					// 数据加载完后进行回调,交给view进行展示
					mUserView.showUser(users);
					
				}
			});
		}
		
	}

In Load, first call mUserView.showLoading() to display the loading progress, and then call mUserModel.loadUser to load data. The complete method of Listener must be implemented. The logic is to use the view to display the data to the interface, and the model will finally call back the listener. Complete method, the data is displayed on the interface.

5) MainActivity is obviously used to display data. There is a listview in it. Create two related layout files activity_main.xml and item_user.xml. Let MainActivity implement the IUserView interface and implement two abstract methods to create an adapter for the listview. Rewrite the constructor, use viewHolder, reuse convertView to optimize it, and finally create the Presenter and call its load method to complete loading all logic.

<pre name="code" class="java">public class MainActivity extends ActionBarActivity implements IUserView  {
    
    

    private ListView mListView;
	
	

	@Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.lv);      
        new Presenter1(this).load();
        
    }
    
	
	public void showUser(List<User> users) {
    
    
		//显示所有用户列表
		mListView.setAdapter(new UserAdapter(this,users));
	}

	@Override
	public void showLoading() {
    
    
		
		Toast.makeText(this, "正在拼命加载中", Toast.LENGTH_SHORT).show();
	}
}

adapter:

public class UserAdapter extends BaseAdapter {
    
    

	private Context context;
	private List<User> users;

	public UserAdapter(Context context, List<User> users) {
    
    
		this.context = context;
		this.users = users;
	}

	@Override
	public int getCount() {
    
    
		// TODO Auto-generated method stub
		return users.size();
	}

	@Override
	public Object getItem(int position) {
    
    
		// TODO Auto-generated method stub
		return users.get(position);
	}

	@Override
	public long getItemId(int position) {
    
    
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
    
    
		LayoutInflater inflater = LayoutInflater.from(context);
		ViewHolder viewHolder = null;
		//convertView
		if (convertView == null) {
    
    
			convertView = inflater.inflate(R.layout.item_user, null);
			viewHolder = new ViewHolder();
			viewHolder.image = (ImageView) convertView
					.findViewById(R.id.iv_user);
			viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);
			viewHolder.content = (TextView) convertView
					.findViewById(R.id.tv_content);
			convertView.setTag(viewHolder);
		}else{
    
    
			viewHolder = (ViewHolder) convertView.getTag();
		}
		
		viewHolder.image.setImageResource(users.get(position).getPicid());
		viewHolder.name.setText(users.get(position).getName());
		viewHolder.content.setText(users.get(position).getContent());
		return convertView;
	}

	private static class ViewHolder {
    
    
		ImageView image;
		TextView name;
		TextView content;
	}

}

In this way, our small example is finished, and the effect is as follows:

img

Experience the advantages of the MVP model:

a) Suppose we no longer obtain user data locally, but instead obtain it from the network. We only need to re-write a model implementation class, create a new present, and replace it in MainActivity. This can be solved. Let's simulate this situation. , I found that it is very convenient to modify. It is recommended to use the MVP mode for the main interface, which complies with the opening and closing principle very well.

b) Suppose I don’t want to use listview to display data and want to switch to gridview. There is no need to modify the original code. I only need to create a new Activity to implement the view and implement the interface methods. At the same time, use gridview and create a corresponding adapter. This is consistent. The open-close principle does not modify the source code, but makes scalable modifications. View and model are decoupled. You can find that there is no shadow of model in the activities we write, only presenter.

public class GridActivity extends MvpBaseActivity<IUserView, GridPresenter> implements IUserView{
    
    
	 private GridView mGridView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
    
    
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_grid);
		mGridView = (GridView) findViewById(R.id.gv);
		mPresenter.load();
	}

	@Override
	public void showLoading() {
    
    
		// TODO Auto-generated method stub
		Toast.makeText(this, "正在拼命加载中", Toast.LENGTH_SHORT).show();
	}

	@Override
	public void showUser(List<User> users) {
    
    
		// TODO Auto-generated method stub
		mGridView.setAdapter(new UserAdapter(this,users));
	}

	@Override
	protected GridPresenter createPresenter() {
    
    
		// TODO Auto-generated method stub
		
		return new GridPresenter();
	}

}
public class Presenter2 {
    
    
	//view
	IUserView mUserView;
	//model
	IUserModel mUserModel = new UserModelImpl2();
	//ͨ通过构造函数传入view
	public Presenter2(IUserView mUserView) {
    
    
		super();
		this.mUserView = mUserView;
	}
	//加载数据
	public void load() {
    
    
		//加载进度条
		mUserView.showLoading();
		//model进行数据获取
		if(mUserModel != null){
    
    
			mUserModel.loadUser(new UserLoadListenner() {
    
    
				
				@Override
				public void complete(List<User> users) {
    
    
					// 数据加载完后进行回调,交给view进行展示
					mUserView.showUser(users);
					
				}
			});
		}
		
	}
	
}

4) Memory leak problem in MVP

We found that the two activities we wrote before have something in common, that is, they are both new and present. We extracted the code to improve the reusability of the code.

There are many types of Presenters in each Activity, so in BaseActivitty, the Presenter also needs to be extracted into BasePresenter. In MVP, Presenter holds a reference to the view, so Generics are used in BasePresenter.

public abstract class BasePresenter<T> {
    
    
	
}

In BaseActivitty, the specific type of Presenter is left to the subclass to determine. We only provide a method to generate Presenter. Generics are used many times here, so you need to pay attention.

public abstract class MvpBaseActivity<V,T extends BasePresenter<V>> extends ActionBarActivity {
    
    
	protected T mPresenter;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
    
    
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		//创建presenter
		mPresenter = createPresenter();
		//内存泄露
		//关联View
		mPresenter.attachView((V) this);
	}

	protected abstract  T createPresenter();
	
}

Memory leak analysis: Add the Model and request the network to load data. At this time, it is assumed that the Activity is recycled by the GC due to insufficient memory, but the network loading has not been completed, then the Presenter still exists and holds a reference to the Activity. When the network data loading is completed, the Presenter Activity will be used for data display, and if the Activity has been recycled at this time, a memory leak will occur and an error will be reported. So the solution is: when the view is recycled, the Presenter should disassociate it.

Since the Presenter disassociates from the view, the logic of association and disassociation must be to wrap the view with a weak reference in the Presenter. The reason is that using a weak reference, when the GC scans it, it will be recycled immediately. So modify BasePresenter as follows:

public abstract class BasePresenter<T> {
    
    
	//当内存不足,释放内存
	protected WeakReference<T> mViewReference;

How to create and disassociate:

Association logic: create weak references and wrap the view

Logic of disassociation: Determine, if the weak reference is not empty, clear the weak reference, set it to empty, and release it completely

//进行关联
	public void attachView(T view) {
    
    
		mViewReference = new WeakReference<T>(view);
	}
	//解除关联
	public void detachView() {
    
    
		if(mViewReference != null){
    
    
			mViewReference.clear();
			mViewReference = null;
		}
	}

Expose a method for other classes to retrieve views from weak references

protected T getView() {
    
    
		
		return mViewReference.get();
		
	}

GridPresenter inherits BasePresenter and implements object abstract methods.

public class GridPresenter extends BasePresenter<IUserView>{
    
    
	//view
	//IUserView mUserView;
	//model
	IUserModel mUserModel = new UserModelImpl();

	/*public GridPresenter(IUserView mUserView) {
		super();
		this.mUserView = mUserView;
	}*/
	//加载数据
	public void load() {
    
    
		//加载进度条
		//mUserView.showLoading();
		getView().showLoading();
		//model进行数据获取
		if(mUserModel != null){
    
    
			mUserModel.loadUser(new UserLoadListenner() {
    
    
				
				@Override
				public void complete(List<User> users) {
    
    
					// 数据加载完后进行回调,交给view进行展示
					//mUserView.showUser(users);
					getView().showUser(users);
					
				}
			});
		}
		
	}
	
}

Then modify BaseActivity:

public abstract class MvpBaseActivity<V,T extends BasePresenter<V>> extends ActionBarActivity {
    
    
	protected T mPresenter;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
    
    
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		//创建presenter
		mPresenter = createPresenter();
		//内存泄露
		//关联View
		mPresenter.attachView((V) this);
	}

	protected abstract  T createPresenter();
	@Override
	protected void onDestroy() {
    
    
		// TODO Auto-generated method stub
		super.onDestroy();
		mPresenter.detachView();
	}
}

Associate the view in the oncreate method, clear the association in the onDestroy method, and all the logic about memory leaks is completed. Well, the analysis of the MVP pattern ends here. For more applications, everyone needs to apply it in the project. The model is used and continuously summarized.

Finally, I would like to share with you a set of "Advanced Materials on the Eight Modules of Android" written by Alibaba's senior architects to help you systematically organize messy, scattered, and fragmented knowledge, so that you can master Android development systematically and efficiently. Various knowledge points.

Due to the large content of the article and the limited space, the information has been organized into PDF documents. If you need the complete document of "Advanced Materials on Eight Modules of Android", you can add WeChat to get it for free!

"Android Eight Modules Advanced Notes"

Insert image description here

Compared with the fragmented content we usually read, the knowledge points in this note are more systematic, easier to understand and remember, and are strictly arranged according to the knowledge system.

1. Source code analysis collection

Insert image description here

2. Collection of open source frameworks

Insert image description here

At the same time, a WeChat group chat robot based on chatGPT has been built here to answer difficult technical questions for everyone 24 hours a day .

picture

Guess you like

Origin blog.csdn.net/huahaiyi/article/details/132753101