如何用 Clean 架构开发 Android 应用 已翻译 100%

oschina 投递于 2016/12/20 11:19 (共 15 段, 翻译完成于 12-21)
阅读 2866
收藏 82
2
加载中

自我开始开发 Android 应用以来就有一种感觉——我可以把它做得更好。在我的职业生涯中,我看到过不少烂代码,其中一些还是我自己写的。Android 的复杂性和烂代码势必造成大问题。所以,从错误中汲取教训并持续改善十分重要。在多次尝试寻找更好的开发方式后,我遇到了 Clean 架构(简洁架构)。于是我将其应用在了 Android 开发中,并结合我的开发经验做了调整,写出了这篇我觉得较为实用、值得分享的文章。

最近我用 Clean 架构为客户构建了 app,并收到了很好的反馈。因此,在这篇文章中我会手把手教你如何用 Clean 架构开发 Android 应用。

边城
边城
翻译于 2016/12/20 13:04
2

什么是 Clean 架构?

有许多文章已经对 Clean 架构的概念做过介绍。在此我讲一讲 Clean 架构的核心内容。

通常所说的 Clean,是指代码被分为像洋葱状的多个层,其规则基础:内层不需要知道外层在干什么。即向内依赖

这是上一段内容的直观呈现:

                                          简洁架构极佳的视觉表现。图片来自Uncle Bob

文中提到的 Clean 架构会给代码提供以下属性:

  • 不依赖框架。

  • 可测试。

  • 不依赖 UI。

  • 不依赖数据库。

  • 不依赖其它外部力量

我希望你能理解这几点在下面的示例中是如何体现的。更多关于 Clean 架构的解释,我推荐你看看这篇文章和这个视频

边城
边城
翻译于 2016/12/20 13:15
0

这在 Anroid 中意味着什么

一般来说,你的应用可以有任意数量的层,除非你的 Android 应用包含企业级的业务逻辑,最常见的是3层:

  • 外层:实现层

  • 中间层:接口适配层

  • 内层:业务逻辑层

实现层是框架要求所有事情发生的地方。构架代码包括不解决特定问题的代码,比如所有 Android 开发者都喜欢的创建 Activity 和 Fragment,发送 Intent,以及其它网络和数据库相关的框架代码。

接口适配层的目标是连接业务逻辑和框架代码。

最重要的问题是业务逻辑层。这里是你的应用中实际解决问题的地方。这里不会有框架代码,你应该能在没有模拟器支持下运行这部分代码。这样你的业务逻辑代码才容易测试、开发和维护。这是 Clean 架构的主要优势。

边城
边城
翻译于 2016/12/20 13:44
0

核心层之上的每一层都需要为下一层转换模型结构。内层不会引用外层的模型,但外层可以使用内层的模型。这也是前面提到的依赖规则。虽然这样做会导致更大的开销,但能确保各层代码之间的解耦。

为什么需要模型转换?举个例子,当逻辑层的模型不能直接很优雅地展现给用户,或是需要同时展示多个逻辑层的模型时,最好创建一个 ViewModel 类来更好的进行 UI 展示。这样可以在外层使用转换器类将业务模型转换成合适的 ViewModel。
    另一个例子:假设你要从外部数据层的 ContentProvider 得到一个 Cursor 对象,外层要先把它转换成内层的业务模型,再送给你的业务逻辑层进行处理。

文末我会给出更多相关资源,以便你了解更多相关信息。现在我们已经了解 Clean 架构的基本原理,接下来我们需要用代码示例进行说明:用 Clean 架构构建一个示例功能。

Viyi
Viyi
翻译于 2016/12/20 22:24
1

怎样开始构建一个 Clean 应用?

我做了一个样板项目,它为你提供了所有的底层命令。这是一个 Clean 启动包,在设计之初就包含最常用的一些工具包。你可免费下载和修改,还能用它建立自己的应用程序。

你可以在这里找到入门项目: Android Clean Boilerplate

开始编写新用例

本节将解释所有需要编写的代码,你可通过上一节提供的样板文件使用 Clean 方法创建一个示例。 一个示例只代表应用程序中的部分独立功能。 用户(例如,在点击时)可以选择启用或不启用。

首先我们来解释这种方法的结构和术语。这里要说的是我如何构建应用程序,其方法并不固定,你可根据你的需求组织不同的结构。

紫系流月
紫系流月
翻译于 2016/12/20 17:21
0

结构

一般的 Android 应用结构如下:

  • 外层包:UI、Storage、Network 等。

  • 中层包:Presenters, Converters

  • 内层包:Interactors、Models、Repositories、Executor

外层

上面已经提到过,这里是框架的细节。

UI —包括 Activite、Fragment、Adapter 和其它用户界面相关的代码。

Storage — 数据库相关代码,实现 Interactor 需要使用的接口,用于访问和存储数据。包含如 ContentProviders 或者像 DBFlow 这样的 ORM。

Network — 类似 Retrofit 的网络操作。

中层

粘合代码层,将实现细节与业务逻辑连接起来。

Presenters — 处理来自 UI 的事件(比如用户单击)或者常用作内层(Interactor)的回调。

Converters — 转换器对象负责把内部模型转换为外部模型,反之亦然。

内层

核心层包含大部分高等级代码。这里的所有类都是 POJO。这一层中的类和对象都不是特定运行在 Android 应用中,可以非常容易的移植到其它 JVM 运行。

Viyi
Viyi
翻译于 2016/12/20 22:37
0

Interactors - 这些是实际包含业务逻辑代码的类。这些类在后台运行,并使用回调向上层传递事件。在一些项目中,它们也被称为用例(可能是一个合适的名称)。在您的项目中可能有很多小的用于解决特定问题 Interactor 类,这属正常现象。可以说,它符合单一责任原则,而且这样的理解更容易让人接受。

Models - 这些是您在业务逻辑中处理的业务模型。

Repositories  - 此包仅包含数据库或其他外层实现的接口。Interactors 使用这些接口来访问和存储数据。也称为仓库模式

Executor - 此包包含用于调用工作线程执行器在后台执行 Interactors 的代码。这个包一般不需要你修改任何部分。

Tocy
Tocy
翻译于 2016/12/20 14:07
0

一个简单的示例

在这个示例中,我们的用例是: “在 app 启动时读取存储在数据库中的消息并展示。“ 此示例将会展示如何使用下面三个程序包来完成用例的功能:

  • presentation 包(展示包)

  • storage 包(存储包)

  • domain 包(主包)

前两个属于外层实现,最后一个属于内部/核心层实现。

Presentation 包主要负责所有与屏幕显示相关的部分——包括全部的 MVP 栈,即包括 UI 和 presenter 这两个不同层的组件。

Tocy
Tocy
翻译于 2016/12/20 14:16
0

编写新的 Interactor (内部/核心层)

事实上你可以从架构的任意层开始编码,但是我还是推荐你首先从核心业务逻辑开始。因为逻辑代码写好之后可以测试,不需要 activity 也可以正常运行。

所以我们先从创建一个 Interactor 开始。Interactor 是用例主逻辑实现的地方。所有的 Interactors 都运行在后台线程,因此应该不会对 UI 展示造成影响。 我们在这里新建一个 Interactor,叫做 WelcomingInteractor

public interface WelcomingInteractor extends Interactor { 
 
    interface Callback { 
 
        void onMessageRetrieved(String message);
 
        void onRetrievalFailed(String error);
    } 
}

Callback 负责和主线程中的 UI 交互,我们之所以将其放在 Interactor 接口中是因为我们不需要将其重新命名为 WelcomingInteractorCallback——用于将其与其他回调区分。下面让我们实现取回消息的逻辑。假设我们有一个 Interactor 的 MessageRepository,可以给我们发送欢迎消息。

  MessageRepository { 
    String getWelcomeMessage();
}
Tocy
Tocy
翻译于 2016/12/20 14:26
0

下面让我们参考业务逻辑实现 Interactor 接口。我们的实现必须扩展自 AbstractInteractor,这样代码就能在后台执行了。

public class WelcomingInteractorImpl extends AbstractInteractor implements WelcomingInteractor {
    
    ...    
    private void notifyError() {
        mMainThread.post(new Runnable() {            @Override
            public void run() {
                mCallback.onRetrievalFailed("Nothing to welcome you with :(");
            }
        });
    }    private void postMessage(final String msg) {
        mMainThread.post(new Runnable() {            @Override
            public void run() {
                mCallback.onMessageRetrieved(msg);
            }
        });
    }    @Override
    public void run() {        // retrieve the message
        final String message = mMessageRepository.getWelcomeMessage();        // check if we have failed to retrieve our message
        if (message == null || message.length() == 0) {            // notify the failure on the main thread
            notifyError();            return;
        }        // we have retrieved our message, notify the UI on the main thread
        postMessage(message);
    }

                                              WelcomingInteractor 运行方法。

这里尝试获取了数据,并发送消息或者错误码到 UI 层用于显示。我们通过 Callback 通知 UI,这个 Callback 扮演的是 presenter 的角色。这段代码是我业务逻辑的关键。其他框架都是依赖于框架本身。

让我们看一下 Interactor 究竟有哪些依赖:

import com.kodelabs.boilerplate.domain.executor.Executor;
import com.kodelabs.boilerplate.domain.executor.MainThread;
import com.kodelabs.boilerplate.domain.interactors.WelcomingInteractor;
import com.kodelabs.boilerplate.domain.interactors.base.AbstractInteractor;
import com.kodelabs.boilerplate.domain.repository.MessageRepository;

正如你所看到的,这里没有提到任何 Android 代码,这就是 Clean 架构的主要好处。你可以看到框架的独立性。 另外,我们不需要关注 UI 或数据库的细节,我们只是调用外层实现的接口方法。

Tocy
Tocy
翻译于 2016/12/20 14:35
0
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(1)

开源中国阅卷组组长
开源中国阅卷组组长
好厉害的样子,有时间学习一下
返回顶部
顶部