写点什么

【译】使用 Kotlin 从零开始写一个现代 Android- 项目 -Part1

用户头像
Android架构
关注
发布于: 刚刚

前言

经常在 medium.com 上看到一些高质量的技术帖子,但是由于国内的上网环境或者有的同学对于看英文比较排斥,错过了不少好文章。因此,西哥决定弄一个《优质译文专栏》,花一些时间翻译一些优质技术文给大家。这篇文章是一个小系列,用 Kotlin 开发现代 Android APP,总共四篇,后面的会陆续翻译!以下是正文。


现在,真的很难找到一个涵盖所有 Android 新技术的项目,因此我决定自己来写一个,在本文中,我们将用到如下技术:


  • 0 、Android Studio

  • 1、Kotlin 语言

  • 2、构建变体

  • 3、ConstraintLayout

  • 4、DataBinding 库

  • 5、MVVM+repository+Android Manager 架构模式

  • 6、RxJava2 及其对架构的帮助

  • 7、Dagger 2.11,什么是依赖注入?为什么要使用它?

  • 8、Retrofit + RxJava2 实现网络请求

  • 9、RooM + RxJava2 实现储存

我们的 APP 最终是什么样子?

我们的 APP 是一个非常简单的应用程序,它涵盖了上面提到的所有技术。只有一个简单的功能:从 Github 获取googlesamples用户下的所有仓库,将数据储存到本地数据库,然后在界面展示它。


我将尝试解释更多的代码,你也可以看看你 Github 上的代码提交。


Github:https://github.com/mladenrako...


让我们开始吧。

0、Android Studio

首先安卓 Android Studio 3 beta 1(注:现在最新版为 Android Studio 4.0),Android Studio 已经支持 Kotlin,去到Create Android Project界面,你将在此处看到新的内容:带有标签的复选框include Kotlin support。默认情况下选中。按两次下一步,然后选择EmptyActivity,然后完成了。 恭喜!你用 Kotlin 开发了第一个 Android app)

1、Kotlin

在刚才新建的项目中,你可以看到一个MainActivity.kt:


package me.mladenrakonjac.modernandroidapp


import android.support.v7.app.AppCompatActivityimport android.os.Bundle


class MainActivity : AppCompatActivity() {


override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}}


.kt后缀代表了这是一个 Kotlin 文件


MainActivity : AppCompatActivity()?表示我们的MainActivity继承自AppCompatActivity?。


此外,所有的方法都必须有一个关键字fun,在 Kotlin 中,你不能使用@override注解,如果你要表明方法是复写父类或者接口的方法的话,直接使用override关键字,注意:它和 Java 不一样,不是一个注解了。


然后,savedInstanceState: Bundle??中的?代表什么呢?它代表了savedInstanceState这个参数可以是Bundle或者 null。Kotlin 是一门 null 安全语言,如果你像下面这样写:


var?a : String


你将会得到一个编译错误。因为a变量必须被初始化,并且不能为 null,因此你要像这样写:


var?a : String =?"Init value"


并且,如果你执行以下操作,也会报编译错误:


a =?null


要想使a变量为 null ,你必须这样写:


var?a : String?


为什么这是 Kotlin 语言的一个重要功能呢?因为它帮我们避免了 NPE,Androd 开发者已经对 NPE 感到厌倦了,甚至是 null 的发明者-Tony Hoare先生,也为发明它而道歉。假设我们有一个可以为空的nameTextView。如果为 null,以下代码将会发生 NPE:


nameTextView.setEnabled(true)


但实际上,Kotlin 做得很好,它甚至不允许我们做这样的事情。它会强制我们使用?或者!!操作符。如果我们使用?操作符:


nameTextView?.setEnabled(true)


仅当nameTextView不为 null 时,这行代码才会继续执行。另一种情况下,如果我们使用!!操作符:


nameTextView!!.setEnabled(true)


如果nameTextView为 null,它将为我们提供 NPE。它只适合喜欢冒险的家伙)


这是对 Kotlin 的一些介绍。我们继续进行,我将停止描述其他 Kotlin 特定代码。

2、构建变体

通常,在开发中,如果你有两套环境,最常见的是测试环境和生产环境。这些环境在服务器URL图标名称目标api等方面可能有所不同。通常,在开始的每个项目中我都有以下内容:


  • finalProduction: 上传 Google Play 使用

  • demoProduction:该版本使用生产环境服务器 Url,并且它有着 GP 上的版本没有的新功能,用户可以在 Google play 旁边安装,然后可以进行新功能测试和提供反馈。

  • demoTesting:和 demoProduction 一样,只不过它用的是测试地址

  • mock: 对于我来说,作为开发人员和设计师而言都是很有用的。有时我们已经准备好设计,而我们的 API 仍未准备好。等待 API 准备就绪后再开始开发可不是好的解决方案。此构建变体为提供有 mock 数据,因此设计团队可以对其进行测试并提供反馈。对于保证项目进度真的很有帮助,一旦 API 准备就绪,我们便将开发转移到 demoTesting 环境。


在此应用程序中,我们将拥有所有这些变体。它们的 applicationId 和名称不同。 gradle 3.0.0?flavourDimension中有一个新的api,可让您混合不同的产品风味,因此您可以混合demominApi23风味。在我们的应用程序中,我们将仅使用“默认” 的flavorDimension。早 app 的build.gradle中,将此代码插入android {}下:


flavorDimensions "default"


productFlavors {


finalProduction {dimension "default"applicationId "me.mladenrakonjac.modernandroidapp"resValue "string", "app_name", "Modern App"}


demoProduction {dimension "default"applicationId "me


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


.mladenrakonjac.modernandroidapp.demoproduction"resValue "string", "app_name", "Modern App Demo P"}


demoTesting {dimension "default"applicationId "me.mladenrakonjac.modernandroidapp.demotesting"resValue "string", "app_name", "Modern App Demo T"}


mock {dimension "default"applicationId "me.mladenrakonjac.modernandroidapp.mock"resValue "string", "app_name", "Modern App Mock"}}


打开string.xml文件,删掉app_namestring 资源,因此,我们才不会发生资源冲突,然后点击Sync Now,如果转到屏幕左侧的“构建变体”,则可以看到 4 个不同的构建变体,其中每个都有两种构建类型:“Debug”和“Release”,切换到demoProduction构建变体并运行它。然后切换到另一个并运行它。您就可以看到两个名称不同的应用程序。


3、ConstraintLayout

如果你打开activity_main.xml?,你可以看到跟布局是ConstraintLayout,如果你开发过 iOS 应用程序,你可能知道AutoLayoutConstraintLayout和它非常的相似,他们甚至用了相同的?Cassowary?算法。


<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="me.mladenrakonjac.modernandroidapp.MainActivity">


<TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" />


Constraints 可以帮我们描述 View 之间的关系。对于每一个 View 来说,应该有 4 个约束,每一边一个约束,在这种情况下,我们的 View 就被约束在了父视图的每一边了。


在 Design Tab 中,如果你将Hello World文本稍微向上移动,则在TextTab 中将增加下面这行代码:


app:layout_constraintVertical_bias="0.28"



Design?tab 和?Text?tab 是同步的,我们在 Design 中移动视图,则会影响 Text 中的xml,反之亦然。垂直偏差描述了视图对其约束的垂直趋势。如果要使视图垂直居中,则应使用:


app:layout_constraintVertical_bias="0.28"


我们让Activity只显示一个仓库,它有仓库的名字,star 的数量,作者,并且还会显示是否有 issue



要得到上面的布局设计,代码如下所示:


<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="me.mladenrakonjac.modernandroidapp.MainActivity">


<TextViewandroid:id="@+id/repository_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"android:layout_marginStart="16dp"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.083"tools:text="Modern Android app" />


<TextViewandroid:id="@+id/repository_has_issues"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"android:layout_marginStart="16dp"android:layout_marginTop="8dp"android:text="@string/has_issues"android:textStyle="bold"app:layout_constraintBottom_toBottomOf="@+id/repository_name"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="1.0"app:layout_constraintStart_toEndOf="@+id/repository_name"app:layout_constraintTop_toTopOf="@+id/repository_name"app:layout_constraintVertical_bias="1.0" />


<TextViewandroid:id="@+id/repository_owner"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginBottom="8dp"android:layout_marginEnd="16dp"android:layout_marginStart="16dp"android:layout_marginTop="8dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/repository_name"app:layout_constraintVertical_bias="0.0"tools:text="Mladen Rakonjac" />


<TextViewandroid:id="@+id/number_of_starts"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="8dp"android:layout_marginEnd="16dp"android:layout_marginStart="16dp"android:layout_marginTop="8dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="1"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/repository_owner"app:layout_constraintVertical_bias="0.0"tools:text="0 stars" />


</android.support.constraint.ConstraintLayout>


不要被tools:text搞迷惑了,它的作用仅仅是让我们可以预览我们的布局。


我们可以注意到,我们的布局是扁平的,没有任何嵌套,你应该尽量少的使用布局嵌套,因为它会影响我们的性能。ConstraintLayout 也可以在不同的屏幕尺寸下正常工作。



我有种预感,很快就能达到我们想要的布局效果了。


上面只是一些关于ConstraintLayout的少部分介绍,你也可以看一下关于ConstraintLayout使用的 google code lab:?https://codelabs.developers.g...

4. Data binding library

当我听到 Data binding 库的时候,我的第一反应是:Butterknife 已经很好了,再加上,我现在使用一个插件来从 xml 中获取 View,我为啥要改变,来使用 Data binding 呢?但当我对 Data binding 有了更多的了解之后,我的它的感觉就像我第一次见到 Butterknife 一样,无法自拔。

Butterknife 能帮我们做啥?

ButterKnife 帮助我们摆脱无聊的findViewById。因此,如果您有 5 个视图,而没有 Butterknife,则你有 5 + 5 行代码来绑定您的视图。使用 ButterKnife,您只有我行代码就搞定。就是这样。

Butterknife 的缺点是什么?

Butterknife 仍然没有解决代码可维护问题,使用 ButterKnife 时,我经常发现自己遇到运行时异常,这是因为我删除了 xml 中的视图,而没有删除 Activity/Fragment 类中的绑定代码。另外,如果要在 xml 中添加视图,则必须再次进行绑定。真的很不好维护。你将浪费大量时间来维护 View 绑定。

那与之相比,Data Binding 怎么样呢?

有很多好处,使用 Data Binding,你可以只用一行代码就搞定 View 的绑定,让我们看看它是如何工作的,首先,先将 Data Binding 添加到项目:


// at the top of fileapply plugin: 'kotlin-kapt'


android {//other things that we already useddataBinding.enabled = true}dependencies {//other dependencies that we usedkapt "com.android.databinding:compiler:3.0.0-beta1"}


请注意,数据绑定编译器的版本与项目build.gradle文件中的 gradle 版本相同:


classpath?'com.android.tools.build:gradle:3.0.0-beta1'


然后,点击Sync Now,打开activity_main.xml,将Constraint Layout?用 layout 标签包裹


<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools">


<android.support.constraint.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context="me.mladenrakonjac.modernandroidapp.MainActivity">


<TextViewandroid:id="@+id/repository_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"android:layout_marginStart="16dp"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.083"tools:text="Modern Android app" />


<TextViewandroid:id="@+id/repository_has_issues"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"android:layout_marginStart="16dp"android:layout_marginTop="8dp"android:text="@string/has_issues"android:textStyle="bold"app:layout_constraintBottom_toBottomOf="@+id/repository_name"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="1.0"

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
【译】使用Kotlin从零开始写一个现代Android-项目-Part1