[Android] Gramática básica Kotlin detalhada (início rápido)

prefácio

No Google I/O 2017, o Google anunciou Kotlin como a linguagem de desenvolvimento oficial para Android. Kotlin é uma linguagem de programação estaticamente tipada que roda na máquina virtual Java. Kotlin pode ser compilado em bytecode Java ou JavaScript, e pode rodar perfeitamente no ambiente Java.

A linguagem Kotlin tem principalmente as seguintes características:

1. Sintaxe simples e avançada 2.
Segurança de ponteiro nulo
3. Totalmente compatível com Java : Kotlin e Java podem ser misturados para desenvolvimento
4. Programação funcional : funções estendidas, suporte para expressões Lambda
5. Inferência de tipo : não há necessidade de especificar o tipo
6 Coroutines poderosas : lidar com problemas de threading
7. Delegar : carregamento lento, melhorar a velocidade de carregamento
8. Dados imutáveis
​​9. Não há necessidade de findViewById para obter controles : não recomendado em alguns lugares

Pode-se dizer que o Kotlin é uma coleção dos pontos fortes de todos, o que melhora muito a eficiência do desenvolvimento do código nativo do Android e é muito popular.


1. Construção do ambiente Kotlin

Ao criar um projeto de projeto, marque diretamente a opção Include Kotlin support e selecione a linguagem Kotlin.
Se for um projeto Java antigo, o Android Studio precisa instalar o plug-in Kotlin e configurar manualmente o gradle ao mesmo tempo, a configuração é a seguinte:

Adicione o seguinte código no gradle do módulo app:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

Adicione o seguinte código no gradle do módulo do projeto:

ext.kotlin_version = '1.4.21'
dependencies {
    
    
	classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

A seguir, apresentamos a introdução e o uso específicos da linguagem Kotlin e comparamos com a linguagem Java para facilitar o entendimento.

Dois, o objeto

Como escrever Java

MainActivity.this

Escrevendo em Kotlin

this@MainActivity

Três, classe

Como escrever Java

MainActivity.class

Escrevendo em Kotlin

MainActivity::class.java

4. Herança

Como escrever Java

public class MainActivity extends AppCompatActivity {
    
    
    
}

Escrita em Kotlin (classes herdadas em Kotlin devem ser modificadas com a palavra-chave open)

class MainActivity : AppCompatActivity() {
    
    
    
}

5. Variáveis

Como escrever Java

Intent intent = new Intent();

Escrevendo em Kotlin

var intent = Intent()

6. Constantes

Como escrever Java

final String text = "";

Escrevendo em Kotlin

val text = ""

7. Constantes estáticas

Como escrever Java

public class MainActivity extends AppCompatActivity {
    
    

    static final String text = "";
}

Método de escrita do Kotlin (observe que as variáveis ​​estáticas devem ser definidas acima da classe)

const val text = ""

class MainActivity : AppCompatActivity() {
    
    

}

8. Método de definição

Como escrever Java

public void test(String message) {
    
    

}

Método de escrita do Kotlin (Unit tem o mesmo efeito que void)

fun test(message : String) : Unit {
    
    

}
// 在 Kotlin 可以省略 Unit 这种返回值
fun test(message : String) {
    
    

}

Nove, método de sobrecarga

Como escrever Java

public class MainActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
    }
}

Escrevendo em Kotlin

class MainActivity : AppCompatActivity() {
    
    

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
    }
}

10. Tipos básicos de dados

Como escrever Java

int i = 1;
long l = 2;
boolean b = true;
float f = 0;
double d = 0;
char c = 'A';
String s = "text";

Escrevendo em Kotlin

var i : Int = 1
var l : Long = 2
var b : Boolean = true
var f : Float = 0F
var d : Double = 0.0
var c : Char = 'A'
var s : String = "text"
// 更简洁点可以这样,自动推倒类型
var i = 1
var l = 2
var b = true
var f = 0F
var d = 0.0
var c = 'A'
var s = "text"

11. Tipo de comparação

Como escrever Java

if ("" instanceof String) {
    
    

}

Escrevendo em Kotlin

if ("" is String) {
    
    

}

12. Conversor

Como escrever Java

int number = 100;
System.out.println(String.format("数量有%d", number));

Escrevendo em Kotlin

var number = 100
println("数量有${
      
      number}")
// 换种简洁的写法
var number = 100
println("数量有$number")
// 如果不想字符串被转义可以使用\$
var number = 100
println("数量有\$number")

Treze, comparação de strings

Como escrever Java

String s1 = "text";
String s2 = "text";
if (s1.equals(s2)) {
    
    
    
}

Método de escrita do Kotlin (o Kotlin otimiza o método de escrita da comparação de strings e outros tipos de comparação de objetos ainda precisam usar o método equals)

var s1 = "text"
var s2 = "text"
if (s1 == s2) {
    
    

}

14. Matriz

Como escrever Java

int[] array1 = {
    
    1, 2, 3};
float[] array2 = {
    
    1f, 2f, 3f};
String[] array3 = {
    
    "1", "2", "3"};

Escrevendo em Kotlin

val array1 = intArrayOf(1, 2, 3)
val array2 = floatArrayOf(1f, 2f, 3f)
val array3 = arrayListOf("1", "2", "3")

15. Ciclo

Como escrever Java

String[] array = {
    
    "1", "2", "3"};

for (int i = 0; i < array.length; i++) {
    
    
    System.out.println(array[i]);
}

Escrevendo em Kotlin

val array = arrayListOf("1", "2", "3")

for (i in array.indices) {
    
    
    println(array[i])
}

16. Ciclo de marca de canto

Como escrever Java

String[] array = {
    
    "1", "2", "3"};

for (int i = 1; i < array.length; i++) {
    
    
    System.out.println(array[i]);
}

A maneira de escrever do Kotlin (esse modo de escrever é chamado de intervalo em Kotlin)

val array = arrayListOf("1", "2", "3")

for (i in IntRange(1, array.size - 1)) {
    
    
    println(array[i])
}
// 换种更简洁的写法
val array = arrayListOf("1", "2", "3")

for (i in 1..array.size - 1) {
    
    
    println(array[i])
}
// 编译器提示要我们换种写法
val array = arrayListOf("1", "2", "3")

for (i in 1 until array.size) {
    
    
    println(array[i])
}

Dezessete, ciclo avançado

Como escrever Java

String[] array = {
    
    "1", "2", "3"};

for (String text : array) {
    
    
    System.out.println(text);
}

Escrevendo em Kotlin

val array = arrayListOf("1", "2", "3")

for (text in array) {
    
    
    println(text)
}

18. Juiz

Como escrever Java

int count = 1;

switch (count) {
    
    
    case 0:
        System.out.println(count);
        break;
    case 1:
    case 2:
        System.out.println(count);
        break;
    default:
        System.out.println(count);
        break;
}
在这里插入代码片

Escrevendo em Kotlin

var count = 1

when (count) {
    
    
    0 -> {
    
    
        println(count)
    }
    in 1..2 -> {
    
    
        println(count)
    }
    else -> {
    
    
        println(count)
    }
}
var count = 1

// 换种更简洁的写法
when (count) {
    
    
    0 -> println(count)
    in 1..2 -> println(count)
    else -> println(count)
}

19. Construtor

Como escrever Java

public class MyView extends View {
    
    

    public MyView(Context context) {
    
    
        this(context, null);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
    
    
        this(context, attrs, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    
        super(context, attrs, defStyleAttr);
    }
}

Escrevendo em Kotlin

class MyView : View {
    
    

    constructor(context : Context) : this(context, null) {
    
    

    }

    constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0) {
    
    

    }

    constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr) {
    
    

    }
}
// 换种更简洁的写法
class MyView : View {
    
    

    constructor(context : Context) : this(context, null)

    constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0)

    constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr)
}
// 只有一种构造函数的还可以这样写
class MyView(context: Context?) : View(context) {
    
    

}

Vinte, criação de classe

Como escrever Java

public class Person {
    
    

    String name;
    int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }
}
Person person = new Person("Zhangsan", 100);
person.setName("ZS");
person.setAge(18);
System.out.println("name: " + person.getName() + ", age: " + person.getAge());

A maneira de escrever do Kotlin (se você não deseja expor o método set das variáveis ​​de membro, pode alterar var para val)

class Person {
    
    

    var name : String? = null
    get() = field
    set(value) {
    
    field = value}

    var age : Int = 0
    get() = field
    set(value) {
    
    field = value}
}
// 换种更简洁的写法
class Person(var name : String, var age : Int)
var person = Person("Zhangsan", 100)
person.name = "ZS"
person.age = 18
println("name: {
     
     $person.name}, age: {
     
     $person.age}")

Vinte e um, método de conjunto de privatizações

Como escrever Java

public class Person {
    
    

    String name;
    int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    private void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    private void setAge(int age) {
    
    
        this.age = age;
    }
}

Escrevendo em Kotlin

class Person {
    
    

    var name : String? = null
    private set

    var age : Int = 0
    private set
}

Vinte e dois, método de obtenção de privatização

Como escrever Java

public class Person {
    
    

    String name;
    int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    private String getName() {
    
    
        return name;
    }

    private void setName(String name) {
    
    
        this.name = name;
    }

    private int getAge() {
    
    
        return age;
    }

    private void setAge(int age) {
    
    
        this.age = age;
    }
}

Escrevendo em Kotlin

class Person {
    
    

    private var name : String? = null

    private var age : Int = 0
}

23. Enumeração

Como escrever Java

enum Sex {
    
    

    MAN(true), WOMAN(false);

    Sex(boolean isMan) {
    
    }
}

Escrevendo em Kotlin

enum class Sex (var isMan: Boolean) {
    
    

    MAN(true), WOMAN(false)
}

24. Interface

Como escrever Java

public interface Callback {
    
    
    void onSuccess();
    void onFail();
}

Método de escrita Kotlin (o método de interface Kotlin pode ser implementado por você, então não será demonstrado aqui)

interface Callback {
    
    
    fun onSuccess()
    fun onFail()
}

25. Classe interna anônima

Como escrever Java

new Callback() {
    
    

    @Override
    public void onSuccess() {
    
    
        
    }

    @Override
    public void onFail() {
    
    

    }
};

Escrevendo em Kotlin

object:Callback {
    
    

    override fun onSuccess() {
    
    
        
    }

    override fun onFail() {
    
    
        
    }
}

Vinte e seis, classe interna

Como escrever Java

public class MainActivity extends AppCompatActivity {
    
    

    public class MyTask {
    
    

    }
}

Escrevendo em Kotlin

class MainActivity : AppCompatActivity() {
    
    

    inner class MyTask {
    
    
        
    }
}

Vinte e sete, a classe interna acessa a variável com o mesmo nome da classe externa

Como escrever Java

String name = "Zhangsan";

public class MyTask {
    
    

    String name = "ZS";

    public void show() {
    
    
        System.out.println(name + "---" + MainActivity.this.name);
    }
}

Escrevendo em Kotlin

var name = "Zhangsan"

inner class MyTask {
    
    

    var name = "ZS"

    fun show() {
    
    
        println(name + "---" + this@MainActivity.name)
    }
}

Vinte e oito, classe abstrata

Como escrever Java

public abstract class BaseActivity extends AppCompatActivity implements Runnable {
    
    

    abstract void init();
}

Escrevendo em Kotlin

abstract class BaseActivity : AppCompatActivity(), Runnable {
    
    

    abstract fun init()
}

Vinte e nove, variáveis ​​e métodos estáticos

Como escrever Java

public class ToastUtils {

    public static Toast sToast;

    public static void show() {
        sToast.show();
    }
}

A maneira de escrever do Kotlin (em Kotlin, essa maneira é chamada de objeto complementar)

companion object ToastUtils {
    
    

    var sToast : Toast? = null

    fun show() {
    
    
        sToast!!.show()
    }
}

Trinta, parâmetros variáveis

Como escrever Java

public int add(int... array) {
    
    
    int count = 0;
    for (int i : array) {
    
    
        count += i;
    }
    return count;
}

Escrevendo em Kotlin

fun add(vararg array: Int) : Int {
    
    
    var count = 0
    //for (i in array) {
    
    
    //    count += i
    //}
    array.forEach {
    
    
        count += it
    }
    return count
}

Trinta e um, genérico

Como escrever Java

public class Bean<T extends String> {
    
    

    T data;
    public Bean(T t) {
    
    
        this.data = t;
    }
}
Bean<String> bean = new Bean<>("666666");

Escrevendo em Kotlin

class Bean<T : Comparable<String>>(t: T) {
    
    
    var data = t
}
var bean = Bean<String>("666666")
// 换种更简洁的写法
var bean = Bean("666666")

Trinta e dois, construindo blocos de código

Como escrever Java

public class MainActivity extends AppCompatActivity {
    
    

    int number;

    {
    
    
        number = 1;
    }
}

Escrevendo em Kotlin

class MainActivity : AppCompatActivity() {
    
    

    var number = 0

    init {
    
    
        number = 1
    }
}

Trinta e três, bloco de código estático

Como escrever Java

public class MainActivity extends AppCompatActivity {
    
    

    static int number;

    static {
    
    
        number = 1;
    }
}

Escrevendo em Kotlin

class MainActivity : AppCompatActivity() {
    
    

    companion object {
    
    
        
        var number = 0
        
        init {
    
    
            number = 1
        }
    }
}

Trinta e cinco, bloco de código de método

Como escrever Java

void test(){
    
    
    {
    
    
        int a = 1;
    }
}

Escrevendo em Kotlin

fun test() {
    
    
    run {
    
    
        var a =1
    }
}

Trinta e seis, modificador visível

Como escrever em Java (padrão é padrão)

modificador efeito
público Todas as classes são visíveis
protegido subclasses visíveis
padrão Classes sob o mesmo pacote são visíveis
privado Visível apenas para a própria classe

Como escrever em Kotlin (o padrão é public)

modificador efeito
público Todas as classes são visíveis
interno As classes no mesmo módulo são visíveis
protegido subclasses visíveis
privado Visível apenas para a própria classe

Trinta e sete, não há necessidade de findViewById

definido no layout

<TextView
    android:id="@+id/tv_content"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!" />

Defina o texto de TextView diretamente

tv_content.text = "改变文本"

Trinta e oito, Lambda

Embora a expressão Lambda tenha sido adicionada ao Java JDK, ela não foi popularizada. Agora é uma boa opção usar o Kotlin

tv_content.setOnClickListener(View.OnClickListener(

    fun(v : View) {
    
    
        v.visibility = View.GONE
    }
))

A simplificação agora é possível com expressões Lambda

tv_content.setOnClickListener {
    
     v -> v.visibility = View.GONE }

Trinta e nove, variáveis ​​de função

As funções podem ser passadas como variáveis ​​na sintaxe Kotlin

var result = fun(number1 : Int, number2 : Int) : Int {
    
    
    return number1 + number2
}

use esta variável de função

println(result(1, 2))

Quarenta, segurança aérea

Java não nos força a lidar com objetos vazios, por isso geralmente leva ao aparecimento de ponteiros nulos NullPointerException. Agora Kotlin limita os objetos vazios e devemos lidar com se o objeto está vazio no momento da compilação, caso contrário, a compilação não passará .

No caso de objetos não nulos, você pode usar este objeto diretamente

fun getText() : String {
    
    
    return "text"
}
val text = getText()
print(text.length)

No caso de um objeto anulável, é necessário determinar se o objeto está vazio

fun getText() : String? {
    
    
    return null
}
val text = getText()
if (text != null) {
    
    
    print(text.length)
}
// 如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用
val text = getText()
print(text!!.length)
// 还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null
val text = getText()
print(text?.length)

41. O método suporta a adição de parâmetros padrão

Em Java, podemos ter várias sobrecargas para estender um método

public void toast(String text) {
    
    
    toast(this, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text) {
    
    
    toast(context, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text, int time) {
    
    
    Toast.makeText(context, text, time).show();
}
toast("弹个窗");
toast(this, "弹个窗");
toast(this, "弹个窗", Toast.LENGTH_LONG);

Mas no Kotlin, não precisamos sobrecarregar, podemos definir diretamente o valor padrão do parâmetro diretamente no método

fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {
    
    
    Toast.makeText(context, text, time).show()
}
toast(text = "弹个窗")
toast(this, "弹个窗")
toast(this, "弹个窗", Toast.LENGTH_LONG)

Quarenta e dois, extensão de método de classe

Você pode estender o método da classe original sem herança, como estender o método da classe String

fun String.handle() : String {
    
    
    return this + "Zhangsan"
}
// 需要注意,handle 方法在哪个类中被定义,这种扩展只能在那个类里面才能使用
print("ZS = ".handle())
ZS = ZhangSan

Quarenta e três, sobrecarga do operador

O uso de operadores em Kotlin eventualmente chamará os métodos correspondentes do objeto. Podemos fazer com que esse objeto suporte operadores reescrevendo esses métodos

operador método de chamada
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
operador método de chamada
a++ a.inc()
a- a.dez()
operador método de chamada
a + b a.mais(b)
a-b a.menos(b)
a * b a.vezes(b)
a/b a.div(b)
a % b a.rem(b), a.mod(b) (obsoleto)
a…b a.rangeTo(b)
operador método de chamada
a em b b.contém(a)
a!em b !b.contém(a)
operador método de chamada
um [eu] a.get(i)
a[i, j] a.get(i, j)
a[i_1, …, i_n] a.get(i_1, …, i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, …, i_n] = b a.set(i_1, …, i_n, b)
operador método de chamada
a() a.invoke()
a(eu) a. invocar(i)
a(i,j) a. invocar(i, j)
a(i_1, …, i_n) a.invoke(i_1, …, i_n)
operador método de chamada
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
um *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.remAssign(b), a.modAssign(b) (obsoleto)
operador método de chamada
a == b a?.igual(b) ?: (b === nulo)
a != b !(a?.igual(b) ?: (b === nulo))
operador método de chamada
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

四十五、扩展函数

扩展函数是 Kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数

1、let 函数

在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式

一般写法

fun main() {
    
    
    val text = "CurvedBowZhang"
    println(text.length)
    val result = 1000
    println(result)
}

let 写法

fun main() {
    
    
    val result = "CurvedBowZhang".let {
    
    
        println(it.length)
        1000
    }
    println(result)
}

最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理

mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
mVideoPlayer?.let {
    
    
   it.setVideoView(activity.course_video_view)
   it.setControllerView(activity.course_video_controller_view)
   it.setCurtainView(activity.course_video_curtain_view)
}

又或者是需要去明确一个变量所处特定的作用域范围内可以使用

2、with 函数

前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式

定义 Person 类

class Person(var name : String, var age : Int)

一般写法

fun main() {
    
    
    var person = Person("CurvedBowZhang", 100)
    println(person.name + person.age)
    var result = 1000
    println(result)
}

with 写法

fun main() {
    
    
    var result = with(Person("CurvedBowZhang", 100)) {
    
    
        println(name + age)
        1000
    }
    println(result)
}

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于 Android 中RecyclerView中onBinderViewHolder中,数据 model 的属性映射到 UI 上

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    holder.nameView.text = "姓名:${
      
      item.name}"
    holder.ageView.text = "年龄:${
      
      item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    with(item){
    
    
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}

3、run 函数

实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

一般写法

var person = Person("CurvedBowZhang", 100)
println(person.name + "+" + person.age)
var result = 1000
println(result)

run 写法

var person = Person("CurvedBowZhang", 100)
var result = person.run {
    
    
    println("$name + $age")
    1000
}
println(result)

适用于let,with函数任何场景。
因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法;
另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    holder.nameView.text = "姓名:${
      
      item.name}"
    holder.ageView.text = "年龄:${
      
      item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    item?.run {
    
    
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}

4、apply 函数

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身

一般写法

val person = Person("CurvedBowZhang", 100)
person.name = "ZJX"
person.age = 50

apply 写法

val person = Person("CurvedBowZhang", 100).apply {
    
    
    name = "ZJX"
    age = 50
}

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。
apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到

mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0

使用 apply 函数后的代码是这样的

mRootView = View.inflate(activity, R.layout.example_view, null).apply {
    
    
   tv_cancel.paint.isFakeBoldText = true
   tv_confirm.paint.isFakeBoldText = true
   seek_bar.max = 10
   seek_bar.progress = 0
}

多层级判空问题

if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
    
    
    return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
    
    
    renderAnalysis();
    return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
    
    
    fetchQuestionData();
    return;
}

kotlin的apply函数优化

mSectionMetaData?.apply {
    
    

    //mSectionMetaData不为空的时候操作mSectionMetaData

}?.questionnaire?.apply {
    
    

    //questionnaire不为空的时候操作questionnaire

}?.section?.apply {
    
    

    //section不为空的时候操作section

}?.sectionArticle?.apply {
    
    

    //sectionArticle不为空的时候操作sectionArticle

}

5、also 函数

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

fun main() {
    
    
    val result = "CurvedBowZhang".let {
    
    
        println(it.length)
        1000
    }
    println(result) // 打印:1000
}
fun main() {
    
    
    val result = "CurvedBowZhang".also {
    
    
        println(it.length)
    }
    println(result) // 打印:CurvedBowZhang
}

适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用

6、小结

通过以上几种函数的介绍,可以很方便优化 Kotlin 中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景也有相同的地方,比如:run函数就是let和with的结合体。

一张图说明:
在这里插入图片描述

四十六、协程

子任务协作运行,优雅的处理异步问题解决方案

协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能

在当前 app module 中配置环境和依赖(因为现在协程在 Kotlin 中是实验性的)

kotlin {
    
    
    experimental {
    
    
        coroutines 'enable'
    }
}

dependencies {
    
    
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'
}

协程的三种启动方式

runBlocking:T     

launch:Job

async/await:Deferred
  • runBlocking

runBlocking :运行阻塞。用代码测试一下

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
runBlocking {
    
    
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000) // 因为 Activity 最长响应时间为 15 秒
    println("测试延迟结束")
}
println("测试结束")
17:02:08.686 System.out: 测试是否为主线程 true
17:02:08.686 System.out: 测试开始
17:02:08.688 System.out: 测试是否为主线程 true
17:02:08.688 System.out: 测试延迟开始
17:02:28.692 System.out: 测试延迟结束
17:02:28.693 System.out: 测试结束

runBlocking 运行在主线程,过程中 App 出现过无响应提示,由此可见 runBlocking 和它的名称一样,真的会阻塞当前的线程,只有等 runBlocking 里面的代码执行完了才会执行 runBlocking 外面的代码

  • launch

launch :启动。用代码测试

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch {
    
    
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
17:19:17.190 System.out: 测试是否为主线程 true
17:19:17.190 System.out: 测试开始
17:19:17.202 System.out: 测试结束
17:19:17.203 System.out: 测试是否为主线程 false
17:19:17.203 System.out: 测试延迟开始
17:19:37.223 System.out: 测试延迟结束
  • async

async :异步。用代码测试

测试的时候是主线程,但是到了 launch 中就会变成子线程,这种效果类似 new Thread(),和 runBlocking 最不同的是 launch 没有执行顺序这个概念

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
async {
    
    
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
17:29:00.694 System.out: 测试是否为主线程 true
17:29:00.694 System.out: 测试开始
17:29:00.697 System.out: 测试结束
17:29:00.697 System.out: 测试是否为主线程 false
17:29:00.697 System.out: 测试延迟开始
17:29:20.707 System.out: 测试延迟结束

这结果不是跟 launch 一样么?那么这两个到底有什么区别呢?,让我们先看一段测试代码

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
val async = async {
    
    
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
    return@async "666666"
}
println("测试结束")

runBlocking {
    
    
    println("测试返回值:" + async.await())
}
17:50:57.117 System.out: 测试是否为主线程 true
17:50:57.117 System.out: 测试开始
17:50:57.120 System.out: 测试结束
17:50:57.120 System.out: 测试是否为主线程 false
17:50:57.120 System.out: 测试延迟开始
17:51:17.131 System.out: 测试延迟结束
17:51:17.133 System.out: 测试返回值:666666

这里就说明了 async 和 launch 还是有区别的,async 可以有返回值,通过它的 await 方法进行获取,需要注意的是这个方法只能在协程的操作符中才能调用

  • 线程调度

协程也有类似 RxJava 的线程调度,先用 launch 试验一下

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(CommonPool) {
    
     // 同学们,敲重点
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
18:00:23.243 System.out: 测试是否为主线程 true
18:00:23.244 System.out: 测试开始
18:00:23.246 System.out: 测试结束
18:00:23.246 System.out: 测试是否为主线程 false
18:00:23.247 System.out: 测试延迟开始
18:00:43.256 System.out: 测试延迟结束

Q:这个跟刚刚的代码有什么不一样吗?

A:当然不一样,假如一个网络请求框架维护了一个线程池,一个图片加载框架也维护了一个线程池…,你会发现其实这样不好的地方在于,这些线程池里面的线程没有被重复利用,于是乎协程主动维护了一个公共的线程池 CommonPool,很好的解决了这个问题

Q:还有刚刚不是说能线程调度吗?为什么还是在子线程运行?

A:因为我刚刚只用了 CommonPool 这个关键字,再介绍另一个关键字 UI,光听名字就知道是啥了

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(UI) {
    
    
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
18:07:20.181 System.out: 测试是否为主线程 true
18:07:20.181 System.out: 测试开始
18:07:20.186 System.out: 测试结束
18:07:20.192 System.out: 测试是否为主线程 true
18:07:20.192 System.out: 测试延迟开始
18:07:40.214 System.out: 测试延迟结束

总结

以上这些仅仅只是 Kotlin 的一些基本使用,还有很多神奇之处和高级用法,帮助我们更加优雅地实现功能,后续我会继续补充在后面,也方便自己开发查阅。

Acho que você gosta

Origin blog.csdn.net/T01151018/article/details/130506726
Recomendado
Clasificación