Android の MVP モデルに関する簡単な説明

ここに画像の説明を挿入します

端午節の休日で雨が降っているので、家でブログを書いています。このブログでは、Android での MVP モデルのアプリケーションについて説明します。

この記事では、MVP モデルを次の側面から説明します。

\1. MVP の紹介

\2. MVP モードを使用する理由

\3. MVP パターンの例 4. MVP におけるメモリ リークの問題 1. MVP の概要:

UI 作成テクノロジーがますます強力になるにつれて、UI レイヤーが実行する責任もますます大きくなります。View と Model の機能をより細分化するために、View はデータの視覚化とユーザーとの対話の処理に集中し、Model はデータ処理のみを処理できるようにするために、MVC の概念に基づいた MVP (Model-View) (プレゼンター) モードが登場しました。いる。

通常、MVP モデルには 4 つの要素があります。

(1) ビュー: UI 要素の描画とユーザーとの対話を担当します (Android ではアクティビティとして反映されます)。

(2) ViewInterface: View によって実装される必要があるインターフェイス。View は、結合を軽減し単体テストを容易にするために、View インターフェイスを介して Presenter と対話します。

(3) モデル: データの保存、取得、操作を担当します (結合を減らすためにモデル インターフェイスを実装する場合もあります)。

(4) プレゼンター: ビューとモデルの対話間の中間リンクとして、ユーザーとの対話を担当するロジックを処理します。

\2. MVP モードを使用する理由

画像

Android 開発では、アクティビティは標準の MVC モデルのコントローラーではなく、アプリケーションのレイアウトをロードしてユーザー インターフェイスを初期化し、ユーザーからの操作リクエストを受け入れて処理し、応答することが主な役割です。インターフェイスとそのロジックが複雑になるにつれて、Activity クラスの責任も増大し続け、クラスが大きくなり肥大化してきます。複雑なロジック処理を別のクラス (Presneter) に移すと、アクティビティは実際には MVP モデルの View となり、UI 要素の初期化、UI 要素とプレゼンター (リスナーなど) との関連付けを確立します。 、同時にそれ自体もいくつかの単純なロジックを処理します (複雑なロジックはプレゼンターによって処理されます)。

さらに、Android アプリケーションを開発するときにコード ロジックをどのように単体テストしたかを思い出してください。アプリケーションを毎回 Android エミュレーターまたは実デバイスにデプロイし、ユーザーの操作をシミュレートしてテストする必要がありますか? しかし、Androidプラットフォームの特性上、毎回のデプロイに多くの時間がかかり、開発効率の低下に直結します。MVP モデルでは、複雑なロジックを処理するプレゼンターがインターフェイスを介してビュー (アクティビティ) と対話しますが、これは何を意味しますか? これは、カスタム クラスを介してこのインターフェイスを実装し、アクティビティの動作をシミュレートしてプレゼンターを単体テストし、展開とテストの時間を大幅に節約できることを示しています。

\3. MVP パターンの例 さて、MVP パターンの基本概念を大まかに理解した後、MVP パターンを使用して小さな例を書いていきます。

パッケージの構造は次の図に示されています。 エフェクト表示:

画像

画像

MVP モードの手順から始めましょう。

1) ビューのインターフェースクラスを作成し、業務に応じた抽象メソッドを定義する

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

2) モデルのインターフェースクラスを作成し、業務に応じた抽象メソッドを定義する

データをロードするためのメソッドを定義し、ロード完了用のリスナーを設定し、ロード完了後のコールバック用のリスナーに抽象メソッドcompleteを設定します。

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

3) モデルの実装クラスを作成し、抽象メソッドを実装します 必要に応じて Bean パッケージ内にユーザー クラスを作成します。

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

データをロードした後、リスナーで完全なメソッドをコールバックします。

4) presentを作成し、コンストラクタにviewの実装クラスを渡し、newにmodelの実装クラスを追加し、viewとmodel間の通信ブリッジを実現するメソッドloadを作成します。

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

Load では、最初に mUserView.showLoading() を呼び出して読み込みの進行状況を表示し、次に mUserModel.loadUser を呼び出してデータを読み込みます。Listener の完全なメソッドを実装する必要があります。ロジックは、ビューを使用してデータをインターフェイスに表示することです。そしてモデルは最終的にリスナーをコールバックし、メソッドが完了すると、データがインターフェイスに表示されます。

5) MainActivity は明らかにデータを表示するために使用されます。その中にリストビューがあります。2 つの関連するレイアウト ファイル activity_main.xml と item_user.xml を作成します。MainActivity に IUserView インターフェイスを実装させ、リストビューのアダプターを作成する 2 つの抽象メソッドを実装させます。コンストラクターを作成し、viewHolder を使用し、convertView を再利用して最適化します。最後に Presenter を作成し、その load メソッドを呼び出してすべてのロジックの読み込みを完了します。

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

アダプタ:

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

}

このようにして、小さな例が完成し、その効果は次のようになります。

画像

MVP モデルの利点を体験してください。

a) ユーザー データをローカルで取得するのではなく、ネットワークから取得するとします。モデル実装クラスを書き直して、新しいプレゼントを作成し、MainActivity で置き換えるだけで済みます。これは解決できます。これをシミュレーションしてみましょう。変更するのに非常に便利であることがわかりました。メイン インターフェイスには、開閉の原則によく準拠する MVP モードを使用することをお勧めします。

b) データの表示にリストビューを使用せず、グリッドビューに切り替えたいとします。元のコードを変更する必要はありません。ビューを実装し、インターフェイス メソッドを実装するための新しいアクティビティを作成するだけで済みます。同時に、グリッドビューを使用し、対応するアダプターを作成します。これは一貫しています。オープン/クローズの原則により、ソース コードは変更されませんが、スケーラブルな変更が行われます。ビューとモデルは分離されており、私たちが作成したアクティビティにはモデルの影はなく、プレゼンターのみが存在することがわかります。

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) MVP のメモリリークの問題

以前に作成した 2 つのアクティビティには、新しいものと既存のものという共通点があることがわかり、コードを抽出してコードの再利用性を向上させました。

各 Activity には Presenter の種類が多数あるため、BaseActivitty では Presenter も BasePresenter に抽出する必要があります。MVP では Presenter がビューへの参照を保持するため、BasePresenter でジェネリックが使用されます。

public abstract class BasePresenter<T> {
    
    
	
}

BaseActivity では、Presenter の具体的なタイプの決定はサブクラスに委ねられています。Presenter を生成するメソッドのみが提供されています。ここではジェネリックが多用されているため、注意が必要です。

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

メモリ リーク分析: モデルを追加し、ネットワークにデータのロードを要求します。この時点では、メモリ不足によりアクティビティが GC によってリサイクルされていると想定されますが、ネットワークのロードは完了しておらず、プレゼンターはまだ存在しており、アクティビティへの参照を保持します。ネットワーク データの読み込みが完了すると、プレゼンター アクティビティがデータの表示に使用されます。このときにアクティビティがリサイクルされていると、メモリ リークが発生してエラーが報告されます。解決策は、ビューがリサイクルされるときに、プレゼンターがビューの関連付けを解除する必要があることです。

プレゼンターがビューから関連付けを解除するため、関連付けと関連付け解除のロジックは、プレゼンター内の弱い参照でビューをラップすることである必要があります。その理由は、弱い参照を使用すると、GC がスキャンするときにすぐにリサイクルされるからです。したがって、BasePresenter に次の変更を加えます。

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

作成および関連付けを解除するメソッド:

アソシエーション ロジック: 弱い参照を作成し、ビューをラップします。

関連付け解除のロジック: 弱い参照が空でない場合は、弱い参照をクリアし、空に設定し、完全に解放します。

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

他のクラスが弱参照からビューを取得するためのメソッドを公開する

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

GridPresenter は BasePresenter を継承し、オブジェクトの抽象メソッドを実装します。

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

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

oncreate メソッドでビューを関連付け、onDestroy メソッドで関連付けをクリアすると、メモリ リークに関するすべてのロジックが完了します。MVP パターンの分析はこれで終了です。さらに多くのアプリケーションを使用するには、全員がそれをプロジェクトに適用する必要があります。モデルを適用して継続的に要約を作成します。

最後に、Alibaba の上級アーキテクトによって書かれた一連の「Android の 8 つのモジュールに関する高度な資料」を共有したいと思います。これは、Android 開発を体系的かつ効率的に習得できるように、乱雑で散在し断片化した知識を体系的に整理するのに役立ちます。 . さまざまな知識のポイント。

記事の内容が多く、スペースが限られているため、情報は PDF ドキュメントにまとめられています。「Android の 8 つのモジュールに関する高度な資料」の完全なドキュメントが必要な場合は、WeChat を追加すると無料で入手できます。

「Android 8 つのモジュールに関する注意事項」

ここに画像の説明を挿入します

私たちが普段読んでいる断片的な内容と比べて、このノートの知識ポイントはより体系的で理解しやすく記憶しやすく、知識体系に従って厳密に整理されています。

1. ソースコード解析集

ここに画像の説明を挿入します

2. オープンソースフレームワークのコレクション

ここに画像の説明を挿入します

同時に、chatGPT に基づく WeChat グループ チャット ロボットがここで構築され、誰もが 24 時間体制で難しい技術的な質問に答えます

写真

おすすめ

転載: blog.csdn.net/huahaiyi/article/details/132753101