传统的AndroidApp架构Android最原生也是最基础的架构

传统的AndroidApp架构Android最原生也是最基础的架构

如果你要设计App的整体框架,你首先要明白我们在做什么。

一般来说,我们与网络交互的方式有两种:主动请求(http)和长连接推送。

我们根据网络交互数据的方式来谈谈我们开发的App的类型和特点:

一般我们做的app都是类型1。简单来说,这类app的主要工作是

从服务器拉取数据并将其显示给用户。 将用户在客户端修改的数据上传到服务器进行处理。

因此,该类App的网络调用相当频繁,需要考虑该App在网络较差或无网络等情况下的运行情况。 成熟商业应用的网络调用一般遵循以下流程:

UI发起请求 - 检查缓存 - 调用网络模块 - 解析返回JSON/统一处理异常 - JSON对象映射为Java对象 - 缓存 - UI获取数据并显示

我们可以看到明确的职责分工,即:数据采集; 数据管理; 数据显示

一旦确定了自己的职责,就可以开始做正事了。

1. 传统Android App架构

Android最原生、最基础的架构可以理解为MVC。 控制器是Activity和Fragment。 然而,这两者掌握了Android系统中的绝大多数资源,并且直接控制内部的View。 因此,传统的Android App一般都是以Activity和Fragment为核心,将网络模块、数据库管理模块、文件管理模块、常用工具类等分离成几个工具包供Activity和Fragment使用。

库开发数据游戏怎么做_数据库小游戏_游戏开发 数据库

这是一个比较基础的Android项目架构。 市场上的大多数应用程序都是这种形状。

优点:开发简单,面向页面; 建设水平好的话,项目基本已经模块化了。 基于Activity和Fragment这两个神一般的存在,很多事情都可以直接完成,不需要绕道。

缺点:由于是面向页面的,维护比较困难。 有些需要共享的业务逻辑会很烦人。 不要重复你自己。 您想重复吗? 如果不想重复,就得写模块。 随着时间的推移,项目最终会变成一堆杂乱的小模块。 另一方面,测试也很困难,因为所有的数据处理都在Activity和Fragment中。 如果想先用假数据来显示,就必须直接改变Activity和Fragment的数据控制逻辑。

还有一个最烦人的问题,就是随着业务的复杂化,Activity和Fragment的代码量急剧增加。 例如,如果你只管理一个电商应用购物车中的商品,无非就是查看、删除、修改。 调用,列表管理,300多行代码都要搞定。 如果现在添加优惠券提醒怎么办? 光有优惠券还不够,还有满减,还有订单组合,还得算运费。 还需要能够领取优惠券……哦,我忘了,一般都会有商品推荐的。 好吧,现在有两个列表需要管理。 你认为2000行代码就能停止CartActivity吗?

从上面缺点的描述中我们可以看出一个很大的痛点是:Activity和Fragment不应该照顾那么多的数据处理逻辑。

2. 分层架构

如果你仔细观察你的项目,你可以发现大多数数据处理代码不需要使用Activity和Fragment持有的资源(比如Context),很多时候我们需要多个页面共享一组数据和请求逻辑,这是非常困难的。 经典的例子就是应用程序中的User对象,它一般是全局单例。

写了这么多全局数据源,很容易想到将数据处理抽取到一个统一的层,向上层提供数据接口。 上层不关心数据的来源(内存、缓存、网络),因为不需要从Activity中获取资源,而是从Fragment中获取资源,其主要工作是数据处理,所以这一层没什么可关心的与UI一起做,这大大提高了可重用性。 我将这一层称为DataManager 层。

这是我的一个项目的包结构

库开发数据游戏怎么做_游戏开发 数据库_数据库小游戏

Activity和Fragment剥离数据处理职责后,它们持有DataManager的引用,负责获取和显示数据,将数据传递给DataManager,并且从不进行网络请求或缓存读写。

数据库小游戏_游戏开发 数据库_库开发数据游戏怎么做

举个例子,页面加载

一般来说,分页加载接口返回的数据是这样的

{
    "code":0,
    "message":"success",
    "data":{
        "page":1,
        "totalPage":10,
        "pageSize":20,
        "total":200,
        "list":[......]
    }   
}

传统的写法中,一般会在Activity/Fragment中缓存page、totalPage、pageSize来进行分页请求,根据请求结果刷新数据并判断是否还有; 每个分页接口必须写一次。 如果这个逻辑是放在DataManager中会发生什么?这是我写的

//定义回调接口
public interface ActionCallback<T> {
    void onSuccess(T data);
    void onFailure(String message, Throwable e);
}

分页加载DataManager实现

public class PageLoadDataManager extends BaseDataManager {
    private static final int PAGE_COUNT = 20;
    private List<Data> mDataList = new ArrayList<>();
    private int currentPage = 0;
    private int totalPage = 0;
    public PageLoadDataManager() {
        // init something......
    }
    public void loadData(final boolean refresh, ActionListener<Boolean> listener) {
        if (refresh) {
            currentPage = 0;
        }
        currentPage++;
        RequestParams params = new RequestParams();
        params.put("page", currentPage);
        Request request = new Request(url, params);
        request.request(new RequestCallback(){
           @Override 
           public void onSuccess(JSONObject data) {
               if (refresh) {
                    mDataList.clear();
               }
               totalPage = response.optInt("total_page");
               // 返回数据添加到 mDataList ......
               if (listener != null) {
                   boolean hasMore = currentPage <= totalPage
                   listener.onSuccess(hasMore);
               }
           }
           @Override 
           public void onFailure(String message, Throwable e) {
               if (listener != null) {
                   listener.onFailure(message, e);
               }
           }
        });
    }
    public List<Data> getDataList() {
        return mDataList;
    }
}

Activity/Fragment初始化DataManager后,只需要将数据源绑定到Adapter即可。 loadData设置的回调告诉上层是否还有数据。 UI层调用adapter.notifyDataSetChanged(); 至于数据从哪里来,分页逻辑根本不重要。 需要UI层管理。 UI层只需要通过loadData(refresh)告诉DataManager是否需要重新加载分页,完美契合下拉刷新的逻辑。

当然在此基础上实现数据库缓存读写是没有压力的。 DataManager还可以轻松实现对某个数据的多个接口的统一管理,通过单例模式或者其他管理方式将数据分布到多个页面。

优点:大大减轻Activity/Fragment的压力,实现数据的统一管理,DataManager层成为独立于UI的AppSDK层

缺点:需要添加嵌套回调。 这个问题在引入RxJava之后就完美解决了。

事实上,至此,几万行代码已经能够满足大部分中小型App的框架需求了。 而且,分层架构具有数据统一处理、代码复用性高的特点,使其成为项目中按照框架思想实现业务最快、最可靠的方式。 开发方法。

我认为优秀的框架一个很重要的特点就是能够方便业务开发,而不是给开发带来麻烦。 例如,分层设计后,无论开发时间多么紧张,依靠分层框架仍然是最快、最安全的开发方式。 如果一个接口直接写在UI中的话,就意味着数据管理层提供的一切便利都不能直接使用游戏开发 数据库,而其他UI如果使用这个接口,就得重新复制​​粘贴并更改。 相反,依靠框架,网络调用只实现一次游戏开发 数据库,上层可以复用这个业务接口(典型:关注、收藏等)

即便如此,随着项目规模进一步增大,DataManager和Activity/Fragment的压力依然会增大,更高的测试要求需要Activity/Fragment代码进一步分离。这时候可以看看MVP和MVVM

3.MVP架构

MVC的C既保存了具体的Model,又保存了具体的View,所以C非常臃肿。 即使从层次架构中去掉DataManager,本质上仍然是MVC架构,而MVP和MVVM都是C持有特定的View。 我写过一些文章,其中MVP就是把大量的View Model交互分离出来,交给Presenter,Presenter持有抽象的View。

去年写这个答案的时候,我是这么写的:

看起来很不错,但是在尝试应用的时候发现网上很多博客使用的Demo写法并不实用。 就是抽象出很多View接口,然后创建一个Presenter类作为Presenter。 这样我就可以写一些简单的列表获取和登录了。类看起来很漂亮,似乎实现了代码分离,但是当业务场景变得复杂时就变得有点痛苦了。

那时我只是在尝试。 这是一个很感性的理解,觉得不切实际,我也没有多说什么。 当时我正在做一个购物中心应用,在使用MVP编写购物车等复杂场景时遇到了很大的困难。 这让我怀疑我使用 MVP 是否给自己带来了麻烦。 本来写登录信息还可以,但是当写进购物车的时候,我开始怀疑人生了。

一个ICartView需要写多少个接口? 购物车查看、删除和修改、优惠券充值和折扣查看、订单领取、价格计算、运费……二十个接口缺一不可吧? 那么这个抽象View除了供CartActivity使用之外,还有其他用途吗?

如果我写ICartView、IBonusView、IXXXView...但是有些接口不需要删除购物车列表,是否需要进一步细分? 然后让Activity实现一堆接口? 既然如此,万一有一天需求发生变化怎么办……

Presenter听起来很酷,它是leader,但是它没有Activity和Fragment的资源。 怎样才能让它占主导地位呢? 当我需要获取系统的一些信息(需要Context)时该怎么办? 不持有Context可以再次打开界面吗?

写那么多接口、接口实现、Presenter、几百行代码和n个类,只是为了把1到200行代码移出Activity?

放弃吧...

后来Google出了TODO-MVP,但是发现它和上面的demo写法一样麻烦,我也没有实际使用。 后来反编译了一个大型App,发现正好是MVP架构,于是仔细看了下代码。 就像我最初的想法一样,一个IXXXView应该有多少功能就写多少接口。 看了Presenter的实现,突然明白为什么感觉不实用了:

任何尝试构建其他东西来代替 Activity/Fragment 都是自找麻烦。

MVP就是一个典型的例子

既然MVP将Activity/Fragment抽象成了View氛围,那么就意味着当它作为抽象View使用时,生命周期、Context等极其重要的资源Presenter看不到,但不使用这些东西也是不可能的。 。 为了让Presenter能够使用这些,Presenter必须持有Context并绑定Activity和Fragment的生命周期。 即便如此,在某些需要确定Activity和Fragment的使用的情况下,仍然需要使用强制转换。

正是因为Presenter的“一统”,Presenter和Activity/Fragment绑定度高游戏开发素材,Presenter和IXXXView的复用性很少。

这是我对当前 Android MVP 的看法。 如果谁有更好的实践经验,可以在评论中告诉我。

4.MVVM架构

在我学习MVP的时候,MVVM也是一个非常流行的概念,并且有很多基于数据绑定框架的demo。 不过,我看完这个方案后,立即拒绝了。 大多数应用程序从接口获取数据后都会进行数据转换。 ,即使获取图片URL,也会在Java层添加后缀来获取缩略图。 有些要根据数据源来控制View的大小、显示和隐藏。 XML 能做的太少了。 如果Model与XML绑定,大规模应用我们会面临多少陷阱...

与MVP相比,MVVM最重要的概念是“数据绑定”。 Presenter还持有抽象View。 ViewModel 甚至不需要这个。 View通过ViewModel订阅自己需要的数据源,ViewModel向View提供变化的数据。 界面。 当View的操作导致数据改变或者数据源改变时,ViewModel通过订阅的方式通知View,View更新视图。

这就是 MVVM 吸引人的地方。 ViewModel只提供数据订阅和数据接口,与UI分离。 ViewModel比Presenter小,复用性比Presenter好很多,并且可以基于分层架构稍加修改。 可以实现。 唯一的痛点是:如何实现数据绑定?

之前提到的数据绑定不太令人满意,但 Google I/O 2017 上发布的 android-architecture-components 很好地解决了这个问题。

ViewModel组件规范了ViewModel的位置、生命周期、生成方法,以及一个Activity下多个Fragment之间共享ViewModel数据的问题。

LiveData组件在Java层面提供了View订阅ViewModel数据源的实现方案,非常轻量级。

ViewModel的引入可以很好的处理Activity销毁重建时大规模数据恢复的问题,以及多个接口依赖一个接口返回数据的场景。 在这两个组件的规范下实现MVVM架构将是非常容易和有意义的。

由于我在项目中大规模使用了RxJava,因此我使用RxJava解决方案实现了数据绑定。

有关使用 android-architecture-components 组件实现 MVVM 的信息,请参考

googlesamples/android-架构-组件

关于新的MVVM结构的思想,我推荐这三篇文章。

Android 官方架构组件指南

Android官方对ViewModel的架构组件介绍

Android官方架构组件介绍-LiveData

5.组件化和插件化

这两个概念这两年非常流行,但是需要注意的是,这两个概念和上面的东西不是一个级别的。 组件化和插件化是比上面提到的混乱更高层次的东西。 是针对整个大项目下的几个小模块,而这些小模块如何构建还是要看上面的内容:)

6. 总结

一般来说,我们做app的时候,比如小型外包,其实不会使用MVP、MVVM这样的架构。 分层架构足以让我们快速高效地开发应用程序。 选择哪种框架不仅取决于您的应用程序的类型。 ,这还取决于您的应用程序的规模。 基于分层架构,只要接口实现得足够好,代码足够规范,切换到MVVM这样的架构并不困难。

如果你有现成的、成熟的框架,那是不言而喻的,但如果你的应用只有几千行代码,为了追求MVVM,你写了十几个类,踩了很多坑,只是把一个Activity里几十行代码画成ViewModel的话,那不是完全不一样了吗?

最后分享一下我自己的代码库和基础框架项目。 是否有集成RxJava的基础分支,集成RxJava的分层框架分支,以及使用android-arch-components的mvvm-rx分支。 目前,网络通话仍处于开放状态。 这不是很完美。 后面我会逐步完善演示示例。 希望对大家有所帮助。

ShonLin/QuickDevFramework

- - - - - - - 结尾 - - - - - - -

参考

【翻译】Android应用架构-上网的日子

Android App整体架构设计的思考(一)

如何构建一个高质量的Android项目框架并详细描述框架的结构? ——马天宇的回答

Android MVP详解(第1部分)

Android 官方架构组件指南

Android官方对ViewModel的架构组件介绍

Android官方架构组件介绍-LiveData

文章来源:https://www.zhihu.com/question/45517397