Fork me on GitHub

初探Android MVP项目编写

刚到公司开始工作,项目组的大佬把项目代码扔给我,让我去看。不过公司的项目采用的是MVP模式,而且比较不同于网上查到的MVP模式结构,比较复杂,所以在这里对MVP模式做一下笔记。

什么是MVP模式

MVP模式是model(模型)-view(视图)-presenter(主导器)的结合使用。model负责业务逻辑与数据处理,view负责视图部分的显示,presenter则扮演逻辑控制处理的角色,起着控制各业务流程的作用。MVP与MVC最不同的一点是model与view之间不存在直接的交互关系,在这两者之间隔着的是presenter层,负责view与model之间的数据的间接交互。

大概示意图如下:

view指的是activity和fragment类等,presenter起到桥梁的作用,model则包括service,dao和entity,其中service不指服务,指的是处理逻辑业务的类,dao负责数据处理,entity则是一些实体类。model通过callback将获取到的数据返回到presenter,presenter再通过callback返回到view。相比MVC,view承担了更少的逻辑处理与数据存取操作,只负责自己的数据与视图加载就可以了。presenter则更多的负责逻辑业务判断,仅判断,具体业务要怎么实现,还是要交给model去处理。

在写第一个Demo的过程中,我的最大感受是,层次分明搞得很清楚,各模块做什么都很清晰,但是,callback作为回执,在各部分的引用我还不大清晰。所以我打算照着第一个Demo,摸清楚callback在MVP中什么时候适合被引用。

Demo前言

我不太喜欢Android的sqlite,所以就不做从sqlite存取的Demo了。这里我就做一个获取本地所有音乐的Demo吧。先贴出Demo的项目结构图。

项目结构没有完全按照公司的项目来,公司的项目比这个更复杂一些(扶额),当然,层次也更清晰。不过这次的目的,做到层次分明就行了。

除去布局xml文件不说,我整理一下接口或类的编写顺序:

entity->view接口->presenter接口->service接口->callback抽象类->dao接口->presenter实现类->service实现类->dao实现类->view实现类(即activity类)

其实编写没有严格意义上的顺序,这只是按照我的编写习惯整理出来的顺序。而且在编写过程中,每个类都会多多少少要回去修改一下内容,无需准确按照以上顺序编写。

代码内容

entity

entity实体类自不用多说,比如音乐实体类,包括歌名、歌手、专辑信息等。

MusicEntity:

public class MusicEntity implements Serializable {

    int id;
    String song;
    String singer;
    String album;

    public MusicEntity(int id, String song, String singer, String album) {
        this.id = id;
        this.song = song;
        this.singer = singer;
        this.album = album;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSong() {
        return song;
    }

    public void setSong(String song) {
        this.song = song;
    }

    public String getSinger() {
        return singer;
    }

    public void setSinger(String singer) {
        this.singer = singer;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }
}

view接口

view接口中负责写view中加载数据和显示界面的方法。这里加载数据会用到实体类。

MainView:

public interface MainView {

    /**
     * 加载歌曲数据
     * @param list
     */

    void loadData(List<MusicEntity> list);

}

presenter

presenter作为view与model交互的枢纽,负责view层上传的数据和model层返回的数据的逻辑判断与处理。这里的处理不包括具体怎么去实现,只是负责指路,它只指明这个业务去model哪里处理,不负责处理业务的具体内容。这样一来,减轻了activity类的逻辑处理负担,分离了视图逻辑和业务逻辑,降低了视图与业务之间的耦合。

IMainPresenter:

public interface IMainPresenter {

    /**
     * 加载音乐数据
     */
    void loadData();

    void attachView(MainView view);
}

MainPresenterImpl:

public class MainPresenterImpl implements IMainPresenter {

    private Context mContext;
    private MainView mView;
    private IMainService mService;

    public MainPresenterImpl(Context context) {
        mContext=context;
        mService=new MainServiceImpl(context);
    }

    @Override
    public void attachView(MainView view) {
        this.mView=view;
    }

    /**
     * 加载音乐数据
     */

    @Override
    public void loadData() {
        mService.getAllMusicData(new MainCallBack() {
            @Override
            public void success(List list) {
                super.success(list);
                mView.loadData(list);
            }

            @Override
            public void fail() {
                super.fail();
            }
        });
    }

}

callback

callback负责信息的回传,从model传回presenter,并做一个简单的逻辑判断处理方法。callback主要用在service和presenter,负责这两者之间的数据传递。

MainCallBack:

public abstract class MainCallBack<MusicEntity> {

    public void success(){}

    public void success(List<MusicEntity> list){}

    public void fail(){}

    public void fail(List<MusicEntity> list){}

}

service

这个service不是服务,只是挂了个service的牌子,实际上只是业务处理,应该是utils。我只是顺着公司的项目挂了个一样的牌子。

IMainService:

public interface IMainService {

    /**
     * 默认获取所有音乐数据
     */
    void getAllMusicData(MainCallBack callBack);

}

MainServiceImpl:

public class MainServiceImpl implements IMainService {

    private Context mContext;
    private final IMusicDao mDao;

    public MainServiceImpl(Context context) {
        mContext=context;
        mDao=new MusicDaoImpl();
    }

    @Override
    public void getAllMusicData(MainCallBack callBack) {
        try {
            List<MusicEntity> list=mDao.queryData(mContext);
            callBack.success(list);
        }catch (Exception e){
            callBack.fail();
        }
    }
}

dao

dao负责的职能也很清晰,就是数据的存取和修改。

IMusicDao:

public interface IMusicDao {

    /**
     * 查找音乐数据
     */
    List<MusicEntity> queryData(Context context);

}

MusicDaoImpl:

public class MusicDaoImpl implements IMusicDao {

    @Override
    public List<MusicEntity> queryData(Context context){
        List<MusicEntity> list=new ArrayList<>();
        //媒体库查询语言
        Cursor cursor=context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Audio.AudioColumns.IS_MUSIC);
        if(cursor!=null){
            MusicEntity musicEntity=null;
            while (cursor.moveToNext()){
                musicEntity=null;
                int song_id=cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media._ID));
                String song_name=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
                String singer=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
                String album=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
                //媒体库中的歌曲名称信息可能不规范
                //1、名称中包含歌手
                if(song_name.contains("-")){
                    String[] str=song_name.split("-");
                    song_name=str[1].trim();
                    singer=str[0].trim();
                }
                //2、名称中包含文件格式后缀
                if(song_name.contains(".mp3") || song_name.contains(".MP3")){
                    song_name=song_name.substring(0,song_name.length()-4);
                }
                musicEntity=new MusicEntity(song_id,song_name,singer,album);
                list.add(musicEntity);
            }
        }
        cursor.close();
        return list;
    }

}

运行示意图:

后记

网上关于MVP的讲解很多,我就不赘述了。概念讲解和理解是我的弱项,我只想知道1分的概念和9分的项目编写。不过这毛病在公司面试的时候暴露无遗,被大佬怼的无fuck可说,确实需要加强一下了。

-------------本文结束感谢您的阅读-------------