作者是各自领域的专家,并就其所展示经验的主题撰写文章. 我们所有的内容都由同一领域的顶级专家同行评审和验证.
Tomasz Czura's profile image

Tomasz Czura

Tomasz (MCS)是Android向导和团队领导. 他最喜欢的项目是做酒店娱乐系统的应用程序和后端。.

Previously At

Welltok
Share

En el perfecto mundo de Android, Java的主要语言非常现代, claro y elegante, 每次有新函数出现时,你都可以通过做更多的事情来少写, 开发人员可以通过升级版本来使用它 Gradle. 当创建一个漂亮的应用程序时,功能看起来是完全可测试的、可扩展的和可维护的. 我们的活动不是太大或太复杂, 我们可以将数据源从数据库切换到web,没有太多的差异,等等. Suena bien, ¿verdad? 不幸的是,在Android的世界里,这并不理想. 谷歌继续追求完美,但我们都知道理想世界并不存在. 因此,我们必须在Android世界的伟大旅程中帮助自己.

Can Kotlin replace Java?

Kotlin是Android世界的主要玩家. Pero, ¿podrá reemplazar a Java?

什么是Kotlin,为什么你应该使用它?

Así que, el primer idioma. 我认为Java不是优雅或清晰的大师,既不现代也不富有表现力(我想你也同意). 缺点是低于Android N, 我们仍然局限于Java 6(包括Java 7的一些小部分). 开发人员也可以附加 RetroLambda 在代码中使用lambda表达式,这在使用时非常有用 RxJava. Por encima de Android N, 我们可以使用Java 8的一些新特性, 但它仍然是古老而沉重的Java. 我经常听到[Android开发者](http://www.toptal.说“我希望Android支持更友好的语言”, como el iOS lo hace con Swift”. 如果我告诉你你可以用一种非常愉快和简单的语言呢, con seguridad nula, lambdas, 以及许多其他新功能? Bienvenido a Kotlin.

¿Qué es Kotlin?

Kotlin 它是一种新的语言(有时被称为Android的Swift),由 JetBrains y ahora está en su versión 1.0.2. 让它在Android开发中有用的是它编译到JVM字节码, 也可以用JavaScript编译. 它完全兼容Java, Kotlin代码可以简单地转换为Java代码,反之亦然(有一个 plugin de JetBrains). 这意味着Kotlin可以使用任何框架、库等。., escrito en Java. En Android, se integra por Gradle. 如果你有一个现有的Android应用程序,你想在Kotlin中实现一个新功能,而不需要重写整个应用程序, 只要开始用Kotlin写,它就会工作.

但是,什么是“伟大的新功能”? Permíteme enumerar algunas:

指定和可选函数参数

Fecha de crear Diversión(day: Int, month: Int, year: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) {
  print (" TEST ", " $ day - $ month -美元一年美元小时:$分钟:$ second ")
}

Podemos Llamar Al Método Fechacreación De Distintas Maneras

 
日期创作(1,2,2016)打印:' 1-2-2016 0:0:0 '
日期创作(1,2,2016,12)打印:' 1-2-2016 12:0:0 '
日期创作(1,2,2016,分钟= 30)打印:' 1-2-2016 0:30:0 '

Seguridad Nula

如果一个变量可以为空,代码将不会编译,除非我们强迫它们这样做. 下面的代码会有一个错误: nullable Var puede ser nulo:

var nullableVar: String? = “”;
nullableVar.length;

要编译,我们必须检查它是否为空:

if(nullableVar){
        	nullableVar.length
}

O inclusive más corto:

nullableVar?.length

这样,如果nullableVar为空,什么也不会发生. De lo contrario, 我们可以将变量标记为不可空, 类型后没有问号:

var nonNullableVar: String = “”;
nonNullableVar.length;

这段代码被编译,如果我们想将null赋值给no-NullableVar,编译器将显示一个错误.

猫王的操作员也很有用:

var stringLength = nullableVar?.length ?: 0

所以当nullableVar为零时(所以nullableVar?.length为null), stringLength为0.

Variables Mutables e Inmutables

En el ejemplo anterior, utilizo var cuando se define una variable. 这是可变的,我们可以随时重新分配它. 如果我们希望这个变量是不可变的(在许多情况下我们应该),我们使用 val (como valor, no variable):

val immutable: Int = 1

在那之后,编译器将不允许我们重新映射到不可变的.

Lambdas

我们都知道lambda是什么,所以这里我将向你展示如何在Kotlin中使用它:

button.setOnClickListener({ view -> Log.d("Kotlin","Click")})

或者函数是唯一的还是最后一个参数:

button.setOnClickListener { Log.d("Kotlin","Click")}

Extenciones

扩展是一个非常有用的语言特性, 感谢它,我们可以“扩展”现有的类, 即使它们是最终的,或者我们无法访问它们的源代码.

Por ejemplo, 获取编辑文本字符串值, 而不是每次都写editText.text.toString()我们可以编写函数:

fun EditText.textValue(): String{
  return text.toString()
 
}

O inclusive más corta:

fun EditText.textValue() = text.toString()

Y ahora, con cada instancia de EditText:

editText.textValue()

或者,我们可以添加一个返回相同属性的属性:

var EditText.textValue: String
  get() = text.toString()
  set(v) {setText(v)}

Sobrecarga del Operador

如果我们想要添加、乘法或比较对象,有时它是有用的. Kotlin允许重载:二进制操作符(more, less, plusAssign, range等).), operadores de matriz (get, set, get range, 设置范围)和相等运算和一元运算(+ a), -a, etc.).

Clase de Datos

在Java中实现一个具有三个属性的用户类需要多少行代码:copy, igual, hashCode y toString? En Kotlin sólo necesitas una línea:

数据类用户(val name: String, val name: String, val age: Int)

这个数据类提供了equal()、hashCode()和copy()方法 toString (), que imprime al Usuario como:

用户(名=John,姓=Doe,年龄=23)

数据类还提供了一些其他有用的函数和属性,可以在Kotlin文档中看到.

Extensiones de Anko

Utiliza Butterknife o extensiones de Android, ¿no? 如果你甚至不需要使用这个库,在XML中声明视图之后,你只是通过它的ID从代码中使用它(就像c#中的XAML):

loginBtn.setOnClickListener{}

Kotlin有非常有用的[Anko扩展](http://github.com/Kotlin/anko),这样你就不需要告诉你的活动是什么 loginBtn,只需“导入”xml即可:

import kotlinx.android.synthetic.main.activity_main.*

在Anko中还有很多其他有用的东西, incluyendo actividades de inicio, 媒体活动等等. 这不是Anko的主要目标——它的设计目的是轻松创建代码布局. 所以如果你需要创建一个编程设计,这是最好的方法.

这只是科特林的一个简短概述. 我推荐阅读安东尼奥·莱瓦的博客和他的书 Kotlin for Android Developers,当然还有Kotlin的官方网站.

¿Qué Es MVP y Por Qué?

良好、有力和清晰的语言是不够的. 如果没有良好的架构,用所有语言编写杂乱的应用程序是非常容易的. Android开发者(主要是那些刚刚起步的人), 但也包括最先进的)通常赋予活动对周围一切的责任. La actividad (o Fragmento, u otra vista) descarga datos, guarda para enviar, los presenta, 响应用户交互, 编辑数据并管理所有子视图. . . Y muchas veces mucho más. 对于像活动或碎片这样不稳定的对象来说,这太多了(只要旋转屏幕,活动就会说“再见……”).

一个非常好的主意是将责任与意见隔离开来,并使它们尽可能愚蠢. Las vistas (actividades, fragmentos, 自定义视图或任何在屏幕上显示数据的东西)应该只负责管理它们的子视图. 视图必须有演示者,他们与模型沟通并告诉他们应该做什么. Esto en resumen, es el patrón Model-View-Presenter (para mí, debe ser nombrado Model-Presenter-View 显示图层之间的连接).

MVC vs MVP

“嘿,我知道类似的东西,它叫MVC!” - ¿no crees? No, MVP no es lo mismo que MVC. 在MVC模式中,视图可以与模型通信. 当你使用MVP时,你不允许这两层之间有任何通信,唯一的方式 View puede comunicarse con Model es a través de Presenter. Lo único que View sabe sobre Model puede ser la estructura de datos. View sabe cómo, por ejemplo, mostrar al Usuario, pero no sabe cuándo. Aquí hay un ejemplo sencillo:

眼睛知道“我是活动,我有两个 EditTexts y un botón. 当有人点击按钮时,我必须告诉我的演讲者,并将值传递给他 EditTexts. 就这样,我可以睡到下一次点击,或者主持人告诉我该怎么做。”.

Presenter 知道某个地方是一个视图,并且知道这个视图可以执行什么操作. 你也知道当你得到两个字符串, 您必须从这两个字符串创建用户,并将数据发送到模型以保存, y si se guarda con éxito, 告诉vista“显示成功信息”.

模型只知道数据在哪里, 它们应该存储在哪里,应该对数据执行什么操作.

用MVP编写的应用程序易于测试、维护和重用. 一个纯粹的演讲者不应该对Android平台一无所知. 必须是纯Java类(或Kotlin,在本例中). 多亏了这一点,我们可以在其他项目中重用我们的演示者. 我们还可以通过单独测试轻松地编写单元测试 Model, View y Presenter.

MVP模式通过保持用户界面和业务逻辑真正分离,从而产生更好、更简单的代码库..

稍微离题一下:MVP应该是 Uncle Bob’s Clean Architecture 使应用程序更加灵活和设计良好. 下次我会试着写的.

MVP和Kotlin的示例应用

这是足够的理论,让我们看看一些代码! 让我们试着创建一个简单的应用程序. 这个应用程序的主要目标是创建一个用户. 第一个屏幕将有两个EditTexts(姓名和姓氏)和一个按钮(保存). 输入姓名和姓氏后,点击“保存”, 应用程序应该显示“用户已保存”,并移动到下一个屏幕, 显示保存的姓名和姓氏的地方. 当名或姓为空时, 应用程序不应该保存用户,然后显示一个错误,表明什么是错误的.

创建项目后要做的第一件事 Android Studio es configurar Kotlin. Deberías instalar el complemento Kotlin y después de reiniciar, en Herramientas > Kotlin puedes hacer clic en ‘Configurar Kotlin en Proyecto’. IDE将向Gradle添加Kotlin依赖项. Si tienes algún código existente, 您可以很容易地转换为Kotlin (Ctrl + shift + Alt + K或编码 & gt; Convertir archivo de Java en Kotlin). 如果有什么不工作,项目没有编译, o Gradle no ve Kotlin, 你可以在GitHub上查看可用的应用程序代码.

Kotlin不仅可以很好地与Java框架和库交互, 但它允许你继续使用你已经熟悉的大多数相同的工具.

现在我们有了一个项目,让我们开始创建我们的第一个视图: Create User View. 这个视图应该具有上面提到的功能, 我们可以为此编写一个接口:

interface CreateUserView
: View {
  fun
showEmptyNameError() /* name为空时显示错误*/
  fun
showEmptySurnameError() /*当昵称为空时显示错误*/
  fun
showUserSaved() /*显示用户保存的信息*/
  fun
showUserDetails(user: user) /*显示用户详细信息*/
}

如您所见,Kotlin在声明函数方面类似于Java. 所有函数都不返回任何东西,最后一个函数有一个参数. 这就是区别,参数类型在名称之后. 视图界面不是Android:它是我们简单而空的界面:

interface View

La interfaz de Presenter basic必须有一个类型为vista的属性, 至少在方法中(例如onDestroy), 其中该属性将被设置为null:

interface Presenter {
   var view: T?

   fun onDestroy(){
       view = null
   }
}

这里您可以看到Kotlin的另一个特性:您可以在接口中声明属性,也可以在接口中实现方法.

Nuestro CreateUserView necesita comunicarse con CreateUserPresenter. 这个演示者唯一需要的额外功能是 saveUser con dos argumentos de cadena:

interface CreateUserPresenter: Presenter {
   fun saveUser(name: String, username: String)
}

我们还需要一个模型定义——在前面的类类型中提到过:

数据类用户(val name: String, val name: String)

在声明所有接口之后,我们可以开始实现.

CreateUserPresenter será implementado en CreateUserPresenterImpl:

class
覆盖var视图:CreateUserView?):
CreateUserPresenter {
 
  override fun
saveUser(name: String, username: String) {
  }
}

第一行带有类定义:

CreateUserPresenterImpl(override
var view: CreateUserView?)

它是一个构造函数,我们用它来分配视图的属性,在接口中定义.

MainActivity, which is our CreateUserView 实施,需要参考 CreateUserPresenter:

class MainActivity :
AppCompatActivity(), CreateUserView {
 
  private val
presenter: CreateUserPresenter by lazy {
      CreateUserPresenterImpl(this)
  }
 
  override fun
onCreate (Bundle savedInstanceState:?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
 
      saveUserBtn.setOnClickListener{
          presenter.saveUser(userName.textValue(),
userSurname.textValue()) /*使用textValue()扩展,提到较早*/
 
      }
  }
 
 
 
  override fun
showEmptyNameError() {
      userName.error
= getString(R.string.name_empty_error) /*等于userName.setError() -
Kotlin allows us to use property */
 
  }
 
  override fun
showEmptySurnameError() {
      userSurname.error
= getString(R.string.surname_empty_error)
  }
 
  override fun
showUserSaved() {
      toast(R.string.user_saved)
/* anko extension - equal to Toast.makeText(this, R.string.user_saved,
Toast.LENGTH_LONG) */
 
  }
 
  override fun
showUserDetails(user: User) {
     
  }
 
override fun onDestroy()
{
  presenter.onDestroy()
}
}

在这个例子的开头,我们定义了我们的演示者:

private val presenter:
CreateUserPresenter by lazy {
      CreateUserPresenterImpl(this)
}

Se define como inmutable (val), y es creado por delegado perezoso, 这将在必要时第一次分配. Por otra parte, 我们确信它不会为空(定义后没有问号).

当用户单击save按钮时, 当使用EditTexts值呈现时,视图发送信息. Cuando esto sucede, el usuario debe ser guardado, 因此,我们必须在Presenter中实现saveUser方法(以及一些模型函数):

override fun
saveUser(name: String, username: String) {
  val user =
User(name, surname)
  when(UserValidator.validateUser(user)){
      UserError.EMPTY_NAME
-> view?.showEmptyNameError()
      UserError.EMPTY_SURNAME
-> view?.showEmptySurnameError()
      UserError.NO_ERROR
-> {
          UserStore.saveUser(user)
view?.showUserSaved()
          view?.showUserDetails(user)
      }
  }
}

当创建用户时,它被发送到 UserValidator para comprobar su validez. 然后,根据验证的结果,调用适当的方法. 当(){}与Java中的switch / case相同时的构造. 但是它更强大——Kotlin不仅允许在' case '中使用enum或int, sino también de rangos, cadenas o tipos de objetos. 你必须包含所有的可能性,或者有一个表达式. 然后覆盖所有UserError值.

Al usar view? .showEmptyNameError()(视图后带问号),我们保护自己 NullPointer 视图可以在方法中被覆盖 onDestroy,有了这个结构,什么都不会发生.

当用户模型没有错误时,它告诉 UserStore 保存它,然后告诉视图显示成功并显示详细信息.

如上所述,我们需要实现一些基本的东西:

enum class UserError {
  EMPTY_NAME,
  EMPTY_SURNAME,
  NO_ERROR
}
 
object UserStore {
  fun
saveUser(user: User){
      //Save
用户某处:数据库,共享偏好,发送到web...
  }
}
 
object UserValidator {
 
  fun
validateUser(user: user): UserError {
      with(user){
          if(name.isNullOrEmpty())
return UserError.EMPTY_NAME
          if(surname.isNullOrEmpty())
return UserError.EMPTY_SURNAME
      }
 
      return
UserError.NO_ERROR
  }
}
 
这里最有趣的是*UserValidator.*通过使用“对象”这个词, podemos crear una clase *Singleton*, sin preocupaciones de hilos, 私人建筑商等等.
 
另外:在validateUser方法中,存在(user){}表达式. 该块中的代码在对象上下文中执行, 传递的名称和姓氏是用户的属性.
 
También hay otra pequeña cosa. Todo el código anterior, de enum a *UserValidator*, 定义放在一个文件中(用户类定义也在这里). Kotlin不强迫您将每个公共类保存在一个文件中(或类名与文件完全相同). Por lo tanto, 如果您有一些与(数据类)相关的短代码片段, extensiones, funciones, 常量- Kotlin不需要函数或常量类), 您可以将它们放在一个文件中,而不是在所有项目文件中传播它们.
 
当用户被保存时,我们的应用程序应该显示它. 我们需要另一个视图:它可以是任何Android视图, vista personalizada, fragmento o actividad. Elige la Actividad.
 
因此,让我们定义*UserDetailsView*的接口. 它可以显示用户,但也应该显示一个错误时,用户不存在:
 
~~~ kotlin
interface
UserDetailsView {
  fun
showUserDetails(user: User)
  fun
showNoUserError()
}

Next, UserDetailsPresenter. It should have a user property:

interface
UserDetailsPresenter: Presenter {
  var user:
User?
}

Esta interfaz se implementará en UserDetailsPresenterImpl. 您必须覆盖用户的属性. 每次分配此属性时,用户都必须在视图中更新. Podemos usar un setter de propiedad para esto:

class
覆盖var视图:UserDetailsView?):
UserDetailsPresenter {
  override var
user: User? = null
      set(value)
{
          field
= value
          if(field
!= null){
              view?.showUserDetails(field!!)
          }
else {
              view?.showNoUserError()
          }
      }
 
}

La implementación UserDetailsView, UserDetailsActivity, es muy simple. 和前面一样,我们有一个由空闲加载创建的presenter对象. 显示时的用户必须通过意图传递. 现在有一个小问题,我们一会儿会解决的. 当我们有意图用户时,View需要将其分配给它的演示者. Después de eso, 用户将在屏幕上更新或, si es nulo, 错误将出现(活动将结束, pero el presentador no lo sabe):

class
UserDetailsActivity: AppCompatActivity(), UserDetailsView
 
  private val
presenter: UserDetailsPresenter by lazy {
      UserDetailsPresenterImpl(this)
  }
 
  override fun
onCreate (Bundle savedInstanceState:?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_user_details)
 
      val
user = intent.getParcelableExtra(USER_KEY)
      presenter.user
= user
  }
 
  override fun
showUserDetails(user: User) {
      userFullName.text
= "${user.name} ${user.surname}"
  }
 
  override fun
showNoUserError() {
      toast(R.string.no_user_error)
      finish()
  }
 
override fun onDestroy()
{
  presenter.onDestroy()
}
 
}

通过尝试传递对象需要该对象实现Parcelable接口. Esto es un trabajo muy “sucio”. Personalmente, 我讨厌这样做,因为所有的创造者, las propiedades, el ahorro, la restauración, y así sucesivamente. Afortunadamente, hay un plugin adecuado, Parcelable para Kotlin. 安装后,我们可以生成Parcelable只需点击一下.

我们要做的最后一件事就是实现 showUserDetails (用户:用户)在我们的主要活动:

取消fun showUserDetails(用户:用户){
  startActivity(USER_KEY to user)
/* anko扩展-启动UserDetailsActivity并将用户传递给USER_KEY in
intent */
}

Y eso es todo.

Demo Android app in Kotlin

我们有一个简单的应用程序来保存用户(实际上, no se guarda, 但我们可以在不接触演示者或视图的情况下添加这个功能),并将其呈现在屏幕上. En el futuro, 如果我们想改变用户在屏幕上的呈现方式, 例如:两个活动到一个活动中的两个片段, o dos vistas personalizadas; los cambios solo se verán en clases Ver. 当然,如果我们不改变模型的功能或结构. 不知道View到底是什么的主讲人不需要任何更改.

¿Qué Sigue?

在我们的应用程序中,每次创建活动时都会创建Presenter. Este enfoque, o su opuesto, 如果Presenter必须在活动实例中持续存在, 这是一个在互联网上讨论很多的话题. 对我来说,这取决于应用程序、它的需求和开发人员. 有时毁掉演讲者更好,有时不. 如果你决定坚持一个,一个非常有趣的技巧是使用 LoaderManager para eso.

如上所述,MVP应该是 Uncle Bob’s Clean architecture. 另一方面,优秀的开发人员应该使用 Dagger 将演示者的依赖关系注入到活动中. 它还有助于将来维护、测试和重用代码. Actualmente, Kotlin与Dagger的工作非常好(在正式发布之前不是那么容易),也与其他有用的Android库.

Conclusión

Para mí, Kotlin es un gran lenguaje. 它是现代的,清晰的,富有表现力的,同时由伟大的人不断发展. 我们可以在任何设备和Android版本上使用任何新版本. 在Java中什么让我生气,Kotlin改进了它.

当然,就像我说的,没有什么是理想的. Kotlin也有一些缺点. 最新版本的Gradle插件(主要是alpha或beta)不能很好地使用这种语言. 许多人抱怨构建时间比纯Java稍微长一些, apk有一些额外的MBs. Sin embargo, Android Studio y Gradle 不断改进,手机有越来越多的应用空间. 这就是为什么我认为Kotlin对所有Android开发者来说都是一种非常好的语言. 试一试,在下面的评论区分享你的想法.

示例应用程序的源代码可在 Github.

就这个问题咨询作者或专家.
Schedule a call
Tomasz Czura's profile image
Tomasz Czura

Located in Kraków, Poland

Member since February 6, 2016

About the author

Tomasz (MCS)是Android向导和团队领导. 他最喜欢的项目是做酒店娱乐系统的应用程序和后端。.

Toptal作者是各自领域的专家,并就其所展示经验的主题撰写文章. 我们所有的内容都由同一领域的顶级专家同行评审和验证.

Previously At

Welltok

世界级的文章,每周交付.

Subscription implies consent to our privacy policy

世界级的文章,每周交付.

Subscription implies consent to our privacy policy

Toptal Developers

Join the Toptal® community.