近期在制作一个安卓的Launcher,其中AppList页面需要读取另一个资源包的文件,而且采用了安卓官方的日夜切换会在切换时recreate当前的Activity,如果快速进行操作会导致该App的Fragment没有及时被回收,双倍吃内存从而OOM。
后来有一个朋友建议我试试MVP模式开发。于是就有了这篇文章。来记录一下开发过程中的突发奇想。
我也不懂,刚学,有问题就是你对。
先引用一下MVP的说明:
所谓MVP(Model-View-Presenter)模式。是将APP的结构分为三层:
作者:Jude95
view – UI显示层
提供UI交互
在presenter的控制下修改UI。
将业务事件交由presenter处理。
注意. View层不存储数据,不与Model层交互。
presenter – 逻辑处理层
对UI的各种业务事件进行相应处理。也许是与Model层交互,也许自己进行一些计算,也许控制后台Task,Servic
对各种订阅事件进行响应,修改UI。
临时存储页面相关数据。
注意. Presenter内不出现View引用。
model – 数据层
从网络,数据库,文件,传感器,第三方等数据源读写数据。
对外部的数据类型进行解析转换为APP内部数据交由上层处理。
对数据的临时存储,管理,协调上层数据请求。
将复杂的功能分割为各层内的小问题。各层内功能单一。这样易于功能修改拓展与Debug。
解耦的设计,独立的模块,更有利于分工开发与测试。
链接:https://www.jianshu.com/p/ed2aa9546c2c
来源:简书
另外,为了方便管理接口,我们还需要一个合约来对接口进行约定。下面开始开发:
1.创建合约类(MainContract)
合约是一个接口文件,用来管理、设计各个层之间的接口。
在这个类中,规定了Model\View\Presenter应该实现哪些功能。这次测试APP的作用是获取AppList并且在前端的ListView中显示出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public interface MainContract { interface Model{ List<ResolveInfo> app_list(PackageManager pm); } interface View{ void updateApps(List<ResolveInfo> applist); void showToast(int info); } interface Presenter{ void getApps(); void bindView(MainActivity mainActivity); void setNum(int initNum); } } |
2.View类
在安卓开发中,我把Activity作为View类进行处理。该类不再处理逻辑而是仅更新显示内容。
MainActivity中实现了一些方法,包括了显示和初始化。在这里会有一个initNum用于计算重置次数,每当MainActivity被重置就会给initNum加一,当V层初始化P层时会将该数值传递到P层,这样就知道P层是在哪一次被初始化的。
在这里将P层设置为静态,并且每次重启时重新绑定,销毁时解除绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
public class MainActivity extends AppCompatActivity implements MainContract.View { private static MainPresenter mainPresenter; private ListView appsListView; private MainActivity that; private int initNum = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); that = this; initView(savedInstanceState); } private void initView(Bundle savedInstanceState) { try { initNum = savedInstanceState.getInt("rec"); }catch (Exception e){ } initNum +=1; if(mainPresenter == null){ mainPresenter = new MainPresenter(); mainPresenter.setNum(initNum); } mainPresenter.bindView(this); appsListView = findViewById(R.id.AppsListView); Button button =findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { that.recreate(); } }); } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("rec",initNum); } @Override protected void onResume() { super.onResume(); mainPresenter.getApps(); } @Override public void updateApps(List<ResolveInfo> applist) { appsListView.setAdapter(new CommonAdapter<ResolveInfo>(this,R.layout.adapter_apps_item,applist) { @Override protected void convert(ViewHolder viewHolder, ResolveInfo item, int position) { viewHolder.setText(R.id.title, String.valueOf(item.loadLabel(that.getPackageManager()))); viewHolder.setText(R.id.packagename,item.activityInfo.name); viewHolder.setImageDrawable(R.id.icon,item.loadIcon(that.getPackageManager())); } }); } @Override protected void onDestroy() { super.onDestroy(); mainPresenter.bindView(null); } public void showToast(int intdd) { Toast.makeText(that,""+intdd,Toast.LENGTH_SHORT).show(); } } |
3.Presenter类
P层负责初始化并获取M层信息并且回调V层函数将其更新到页面上。通过阅读代码可以看出每次更新应用列表时P层都会返回自己的初始化ID,这样就可以确定使用的P层是在V层的那一次更新中创建的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class MainPresenter implements MainContract.Presenter { private MainModel model; private MainActivity view; private int Intdd=0; public MainPresenter() { this.model = new MainModel(); } @Override public void getApps() { PackageManager pm = view.getPackageManager(); List<ResolveInfo> list = model.app_list(pm); if(list!=null){ view.updateApps(list); view.showToast(Intdd); } } @Override public void bindView(MainActivity mainActivity) { this.view = mainActivity; } public void setNum(int initNum) { Intdd = initNum; } } |
4.Model类
M层负责获取app列表并提供列表给P层,在M层我们对数据进行了一次缓存,并在数据获取部分增加了5秒的等待,这样我们就可以知道系统是否有效的读取了缓存(当缓存存在时直接返回,减少系统调用次数。当缓存不存在时就会等待5秒后获取缓存。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class MainModel implements MainContract.Model { private List<ResolveInfo> all_apps_list = null; @Override public List<ResolveInfo> app_list(PackageManager pm) { if(pm==null)return null; if(all_apps_list!=null) return all_apps_list; SystemClock.sleep(5000); Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null); resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> resolveinfoList = pm.queryIntentActivities(resolveIntent, 0); all_apps_list = resolveinfoList; return resolveinfoList; } } |
5.运行
通过运行上述代码,可以发现P层仅在V层第一次被创建时初始化,之后recreateV层不再重新初始化P层。M层在P层初始化时被初始化,因为后续没有重新初始化P层所以M层也得以保留,数据可以稳定的从缓存中读取而无需每次都重新获取。
但是这样的代码还有一个问题,就是每次程序运行的时候依旧需要通过5秒等待来初始化。所以我们可以将列表持久化到文件中去,每次启动时先从文件中快速读取到列表,同时异步在后台更新该文件并在获取成功后更新list。这样就可以做到不仅启动迅速,而且不会导致严重卡顿,节约了内存。
另外,出于MVP设计的优势,作出上述更改的过程完全不需要修改P层和V层,有效降低了开发人员的开发难度。是好的。