近期在制作一个安卓的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中显示出来。
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层设置为静态,并且每次重启时重新绑定,销毁时解除绑定。
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层的那一次更新中创建的。
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秒后获取缓存。)
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层,有效降低了开发人员的开发难度。是好的。




