Android 开发与 Kotlin

cover

了解 Android 系统与应用

Android 系统架构

  • Linux 内核层

为 Android 设备的各种硬件提供了底层的驱动(显示,音频,摄像,蓝牙,WIFI,电源…)

  • 系统运行库层

一些C/C++库(SQLite,OpenGL,Webkit),一些 Android 运行时库,其中包含 Dalvik(-> ART) 虚拟机,它使得每一个Android应用都能运行在独立的进程中,并且拥有一个自己的虚拟机实例。相较于Java虚拟机,Dalvik 和 ART 针对手机内存、CPU 性能有限等情况做了优化处理。

  • 应用框架层

提供各种 API 以供开发者构建自己的应用程序

  • 应用层

所有安装在手机上的应用程序

Android 应用开发特色

  • 四大组件

    • Activity(应用程序的门面,用户看到的一切)
    • Service(后台服务相关)
    • BroadcastReceiver(接受电话,短信等广播消息,发送广播消息)
    • ContentProvider(使应用程序之间共享数据的手段)
  • 各种系统控件

提供了丰富的系统自带的控件效果(例如 Material Design 等等)

  • SQLite数据库

自带了这种轻量级、运算速度极快的嵌入式关系型数据库,还可以通过 Android 封装好的 API 进行操作,方便了数据的存取

分析 Android 程序的项目结构

Project 视图下的目录结构

  • .gradle 和 .idea

Android Studio自动生成的一些文件

  • app/

代码、资源等内容,主要开发目录

  • build/

编译时自动生成

  • gradle/

gradle wrapper的配置文件,通过这个可以自动根据本地的缓存情况决定是否需要联网下载 gradle

  • build.gradle

项目全局的gradle构建脚本,通常无需修改

app/src/main/

  • res/
    • drawable 开头的目录用来放图片
    • mipmap 开头的目录用来放应用图标
    • values 开头的目录用来放字符串、样式、颜色等配置
    • layout 开头的目录用来放布局文件
    • -hdpi, -xhdpi, -xxhdpi 标识不同分辨率,来适配不同设备

Android 的日志工具Log

  • Log.v(tag: String, msg: String) verbose 级别
  • Log.d(...) debug 级别
  • Log.i(...) info 级别
  • Log.w(...) warn 级别
  • Log.e(...) error 级别

Kotlin 之于 Java

Kotlin的语法更加简洁,对于同样的功能,使用 Kotlin 开发的代码量可能会比使用 Java 开发的减少 50% 甚至更多。另外,Kotlin 的语法更加高级,相比于 Java 比较老旧的语法,Kotlin 增加了很多现代高级语言的语法特性,使得开发效率大大提升。还有,Kotlin 在语言安全性方面下了很多工夫,几乎杜绝了空指针这个全球崩溃率最高的异常

变量和函数

关键字 说明
val value,用来声明一个不可变的变量(不可重新赋值),对应 Java 的 final
var variable,用来声明一个可变的变量(可重新赋值),对应 Java 的非 final 变量
  • 项目复杂时,可变变量会导致出现一些很难排查的问题,Kotlin 通过设计这两个关键字,使开发者主动思考设计变量的可变性。一个好的习惯是,永远优先使用 val,仅当无法满足需求时再使用 var
  • 自动类型推导
1
2
val a = 10 
val a: Int = 10 // 显式声明
  • 函数声明
1
2
3
fun methodName(param1: Int, param2: Int): Int {
return 0
}
  • 简化写法:当函数体只有一行
1
fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)

根据类型推导机制,进一步简化

1
fun largerNumber(num1: Int, num2: Int) = max(num1, num2)

逻辑控制

if

用法与 Java 几乎相同,额外特性:Kotlin 中的 if 带返回值

原代码

1
2
3
4
5
6
7
8
9
fun largerNumber(num1: Int, num2: Int): Int {
var value = 0
if (num1 > num2) {
value = num1
} else {
value = num2
}
return value
}

简化到最后

1
fun largeNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

when

更加强大的 switch

1
2
3
4
5
6
7
8
fun getScore(name: String) = when (name) {
// 匹配值 -> { 执行逻辑 }
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}

类型匹配功能

1
2
3
4
5
fun checkNumber(num: Number) = when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}

when 还可以不带参数

1
2
3
4
5
6
7
fun getScore(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}

for-in

  • 区间的定义
1
2
3
val range = 0..10       // [0, 10]
val range = 0 until 10 // [0, 10)
val range = 10 downTo 1 // 10 到 1 的降序区间

循环体

1
2
3
4
5
val count: Int = 2
val range = 0..10
for (i in range step count ) { // step 表示循环步长
...
}

此外 for-in 还可以遍历数组、集合等

面向对象

  • 创建类,定义方法,并实例化
1
2
3
4
5
6
7
8
9
10
11
class Person {
var name = ""
var age = 0

fun eat() {
println(name + " is eating. He is " + age + " years old.")
}
}

val p = Person()
p.eat()
  • 关于继承:Kotlin 默认所有非抽象类都是不可以被继承的,因此需要额外关键字 open
1
2
3
4
5
6
7
8
// open Person类
open class Person {
...
}
// 继承Person类
class Student: Person() {
...
}
  • 主构造函数(init 中用来存放主构造函数的逻辑)
1
2
3
4
5
class Student(val sno: String, val grade: Int) : Person() {
init {
...
}
}

根据规定,构造函数必须调用父类的构造函数,因此,若 Person 定义如下

1
open class Person(val name: String, val age: Int) {}

则创建子类时

1
2
3
4
class Student(val sno: String, val grade: Int, name: String, age: Int) :
Person(name, age) {
...
} // 注意 name 和 age 不要声明为 val ,否则会导致字段冲突
  • 次构造函数:Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)
1
2
3
4
5
class Student(val sno: String, val grade: Int, name: String, age: Int) :
Person(name, age) {
constructor(name: String, age: Int) : this("", 0, name, age) {} // 直接调用主构造函数
constructor() : this("", 0) {} // 通过上面的次构造函数间接调用主构造函数
}
  • 接口:与 Java 几乎一致

创建接口

1
2
3
4
interface Study {
fun readBooks()
fun doHomework()
}

实现接口

1
2
3
4
class Student(name: String, age: Int) : Person(name, age), Study {
override fun readBooks() {...}
override fun doHomework() {...}
}
  • 函数的可见性修饰符
关键字 说明
public (默认项)同 Java,对所有类都可见
private 同 Java,只对当前类内部可见
protected 只对当前类和子类可见
internal 只对同一模块中的类可见
  • 数据类与单例类

数据类通常需要重写equals()、hashCode()、toString()这几个方法,在 Java 中实现比较繁琐,在 Kotlin 中只需 data 关键字

1
data class Cellphone(val brand: String, val price: Double)

就可实现下面的操作

1
2
3
4
5
6
fun main() {
val cellphone1 = Cellphone("Samsung", 1299.99)
val cellphone2 = Cellphone("Samsung", 1299.99)
println(cellphone1)
println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}

单例模式,即希望某个类在全局最多只能拥有一个实例,避免重复的对象,在 Java 中

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void singletonTest() {
System.out.println("singletonTest is called.");
}
}

Kotlin 写法:使用 object 关键字

1
2
3
4
5
object Singleton {
fun singletonTest() {
println("singletonTest is called.")
}
}