Una breve discusión sobre el modelo MVP en Android.

Insertar descripción de la imagen aquí

Es el feriado del Dragon Boat Festival y está lloviendo, así que estoy escribiendo un blog en casa, este blog explicará la aplicación del modelo MVP en Android.

Este artículo explicará el modelo MVP desde los siguientes aspectos:

\ 1. Introducción al MVP

\ 2. ¿Por qué utilizar el modelo MVP?

\ 3. Ejemplo de patrón MVP 4. Problema de pérdida de memoria en MVP 1. Introducción a MVP:

A medida que la funcionalidad de la tecnología de creación de UI aumenta día a día, la capa de UI también cumple cada vez con más responsabilidades. Para subdividir mejor las funciones de Ver (Ver) y Modelo (Modelo), dejar que Ver se centre en el procesamiento de la visualización de datos y la interacción con los usuarios, y dejar que Modelo solo se ocupe del procesamiento de datos, entró en el modo MVP (Modelo-Vista-Presentador). ser.

Generalmente hay cuatro elementos en el modelo MVP:

(1) Vista: responsable de dibujar elementos de la interfaz de usuario e interactuar con los usuarios (reflejado como Actividad en Android);

(2) ViewInterface: la interfaz que debe implementar View. View interactúa con Presenter a través de la interfaz View para reducir el acoplamiento y facilitar las pruebas unitarias;

(3) Modelo: responsable de almacenar, recuperar y manipular datos (a veces también implementa una interfaz de modelo para reducir el acoplamiento);

(4) Presentador: como vínculo intermedio entre la interacción Vista y Modelo, maneja la lógica responsable de interactuar con los usuarios.

\ 2. ¿Por qué utilizar el modelo MVP?

imagen

En el desarrollo de Android, la Actividad no es un Controlador en el modelo MVC estándar. Su responsabilidad principal es cargar el diseño de la aplicación e inicializar la interfaz de usuario, aceptar y procesar las solicitudes de operación del usuario y luego responder. A medida que la complejidad de la interfaz y su lógica continúa aumentando, las responsabilidades de la clase Actividad continúan aumentando, lo que hace que se vuelva grande e inflada. Cuando trasladamos el procesamiento lógico complejo a otra clase (Presneter), la Actividad es en realidad la Vista en el modelo MVP, es responsable de inicializar los elementos de la UI y establecer la asociación entre los elementos de la UI y el Presentador (Listener y similares). , y al mismo tiempo él mismo También manejará alguna lógica simple (Presentador manejará la lógica compleja).

Además, piense en cómo probó la lógica del código al desarrollar aplicaciones de Android. ¿Necesita implementar la aplicación en el emulador de Android o en un dispositivo real cada vez y luego probarla simulando las operaciones del usuario? Sin embargo, debido a las características de la plataforma Android, cada implementación consume mucho tiempo, lo que conduce directamente a una reducción en la eficiencia del desarrollo. En el modelo MVP, el Presentador que maneja lógica compleja interactúa con la Vista (Actividad) a través de la interfaz ¿Qué significa esto? Muestra que podemos implementar esta interfaz a través de una clase personalizada para simular el comportamiento de Actividad para realizar pruebas unitarias del Presentador, ahorrando mucho tiempo de implementación y prueba.

\ 3. Ejemplo de patrón MVP Bien, después de comprender aproximadamente los conceptos básicos del patrón MVP, usaremos el patrón MVP para escribir un pequeño ejemplo.

La estructura del paquete se muestra en la siguiente figura: Visualización de efectos:

imagen

imagen

Comencemos con los pasos del modo MVP:

1) Cree la clase de interfaz de la vista y defina métodos abstractos según el negocio.

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

2) Crear la clase de interfaz del modelo y definir métodos abstractos según el negocio.

Defina un método para cargar datos y también configure un oyente para completar la carga. El método abstracto completo se configura en el oyente para la devolución de llamada una vez completada la carga.

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

3) Cree la clase de implementación del modelo e implemente los métodos abstractos. La clase de usuario se crea en el paquete bean según las necesidades.

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);
		}
	}
	
}

Después de cargar los datos, vuelva a llamar el método completo en el oyente.

4) Cree el presente, pase la clase de implementación de la vista en el constructor, agregue la clase de implementación del modelo en nuevo y cree una carga de método para realizar el puente de comunicación entre la vista y el modelo.

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);
					
				}
			});
		}
		
	}

En Load, primero llame a mUserView.showLoading () para mostrar el progreso de la carga y luego llame a mUserModel.loadUser para cargar los datos. Se debe implementar el método completo de Listener. La lógica es usar la vista para mostrar los datos en la interfaz. y el modelo finalmente volverá a llamar al oyente Método completo, los datos se muestran en la interfaz.

5) MainActivity se usa obviamente para mostrar datos. Hay una vista de lista en él. Cree dos archivos de diseño relacionados Activity_main.xml y item_user.xml. Deje que MainActivity implemente la interfaz IUserView e implemente dos métodos abstractos para crear un adaptador para la vista de lista. Reescribir el constructor, use viewHolder, reutilice convertView para optimizarlo y finalmente cree el Presentador y llame a su método de carga para completar la carga de toda la lógica.

<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();
	}
}

adaptador:

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;
	}

}

De esta forma termina nuestro pequeño ejemplo y el efecto es el siguiente:

imagen

Experimente las ventajas del modelo MVP:

a) Supongamos que ya no obtenemos datos de usuario localmente, sino que los obtenemos de la red. Solo necesitamos reescribir una clase de implementación del modelo, crear un nuevo presente y reemplazarlo en MainActivity. Esto se puede resolver. Simulemos esto situación., Descubrí que es muy conveniente modificarlo. Se recomienda utilizar el modo MVP para la interfaz principal, que cumple muy bien con el principio de apertura y cierre.

b) Supongamos que no quiero usar la vista de lista para mostrar datos y quiero cambiar a la vista de cuadrícula. No hay necesidad de modificar el código original. Solo necesito crear una nueva Actividad para implementar la vista e implementar los métodos de la interfaz. Al mismo tiempo, use gridview y cree el adaptador correspondiente, lo cual es consistente: el principio de apertura y cierre no modifica el código fuente, pero realiza modificaciones escalables. La vista y el modelo están desacoplados. Puede encontrar que no hay sombra de modelo en las actividades que escribimos, solo presentador.

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) Problema de pérdida de memoria en MVP

Descubrimos que las dos actividades que escribimos antes tienen algo en común, es decir, son nuevas y presentes. Extrajimos el código para mejorar la reutilización del código.

Hay muchos tipos de presentadores en cada actividad, por lo que en BaseActivitty, el presentador también debe extraerse en BasePresenter. En MVP, el presentador tiene una referencia a la vista, por lo que los genéricos se usan en BasePresenter.

public abstract class BasePresenter<T> {
    
    
	
}

En BaseActivitty, el tipo específico de Presenter se deja a la subclase para que lo determine. Solo proporcionamos un método para generar Presenter. Los genéricos se usan muchas veces aquí, por lo que debe prestar atención.

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();
	
}

Análisis de pérdida de memoria: agregue el modelo y solicite a la red que cargue datos. En este momento, se supone que el GC recicla la actividad debido a una memoria insuficiente, pero la carga de la red no se ha completado, entonces el Presentador todavía existe y contiene una referencia a la Actividad. Cuando se completa la carga de datos de la red, la Actividad del Presentador se utilizará para la visualización de datos, y si la Actividad se ha reciclado en este momento, se producirá una pérdida de memoria y se informará un error. La solución es: cuando la vista se recicla, el presentador debe disociarla.

Dado que el Presentador se disocia de la vista, la lógica de asociación y disociación debe ser envolver la vista con una referencia débil en el Presentador, la razón es que al usar una referencia débil, cuando el GC la escanee, se reciclará inmediatamente. Entonces haga las siguientes modificaciones a BasePresenter:

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

Métodos para crear y disociar:

Lógica de asociación: cree referencias débiles y ajuste la vista

Lógica de disociación: determine, si la referencia débil no está vacía, borre la referencia débil, configúrela como vacía y libérela por completo.

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

Exponer un método para que otras clases recuperen vistas de referencias débiles

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

GridPresenter hereda BasePresenter e implementa métodos abstractos de objetos.

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);
					
				}
			});
		}
		
	}
	
}

Luego modifique 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();
	}
}

Asocie la vista en el método oncreate, borre la asociación en el método onDestroy y toda la lógica sobre las pérdidas de memoria se completará. Bueno, el análisis del patrón MVP termina aquí. Para más aplicaciones, todos deben aplicarlo en el proyecto. Aplicar el modelo y realizar resúmenes continuos.

Finalmente, me gustaría compartir con ustedes un conjunto de "Materiales avanzados sobre los ocho módulos de Android" escritos por los arquitectos senior de Alibaba para ayudarlo a organizar sistemáticamente el conocimiento desordenado, disperso y fragmentado, para que pueda dominar el desarrollo de Android de manera sistemática y eficiente. Varios puntos de conocimiento.

Debido al gran contenido del artículo y al espacio limitado, la información se ha organizado en documentos PDF. Si necesita el documento completo de "Materiales avanzados sobre ocho módulos de Android", puede agregar WeChat para obtenerlo gratis.

"Notas avanzadas sobre los ocho módulos de Android"

Insertar descripción de la imagen aquí

En comparación con el contenido fragmentado que leemos habitualmente, los puntos de conocimiento de esta nota son más sistemáticos, más fáciles de entender y recordar y están estrictamente organizados de acuerdo con el sistema de conocimiento.

1. Colección de análisis de código fuente.

Insertar descripción de la imagen aquí

2. Colección de marcos de código abierto.

Insertar descripción de la imagen aquí

Al mismo tiempo, se ha creado aquí un robot de chat grupal WeChat basado en chatGPT para responder preguntas técnicas difíciles para todos las 24 horas del día .

imagen

Supongo que te gusta

Origin blog.csdn.net/huahaiyi/article/details/132753101
Recomendado
Clasificación