组件化架构图

前言

xx_android 是单一工程模式,所有业务功能都是集中在同一个工程里实现的,业务模块以package方式进行组织。

单一工程

  1. 合代码经常发生冲突。
  2. 代码严重耦合,新人学习成本高,迭代和维护成本大,功能经常发生衰退。
  3. 改了一行代码 或只调了一点UI,就要run整个项目,编译时间效率低。
  4. 多人团队协作效率低效。

组件化介绍

1. 模块化

  1. module与模块基本一个概念,就是原本app模块承载了所有的功能。
  2. 模块化就是拆分成多个模块放在不同的module里面,每个功能代码都在自己所属的module中添加。
  3. 通常情况都会有基础模块module_commom,主要提供baseActivity,baseFragment,http,图片加载等基础能力,然后每个业务模块都会依赖module_commom模块。
  4. 毋庸置疑业务模块之间也需要相互依赖。例如:‘首页’、‘订单’、‘我的’、‘优惠券’、都需要跳转到‘油站详情’的,必须依赖‘油站详情’,而‘油站详情’需要选择优惠券能力,也得需要依赖‘优惠券’。
  5. 那么模块之间的存在复杂的依赖关系。

模块化

模块化 在各个业务功能比较独立的情况下是比较合理的,但多个模块中肯定会有页面跳转、数据传递、方法调用 等情况,所以必然存在以上这种依赖关系,即模块间有着高耦合度。 高耦合度 加上 代码量大,就极易出现上面提到的那些问题了,严重影响了团队的开发效率及质量。

2. 组件化

组件化,去除模块间的耦合,使得每个业务模块可以独立当做App存在,对于其他模块没有直接的依赖关系。 此时业务模块就成为了业务组件。
除了业务组件,还有抽离出来的业务基础组件,是提供给业务组件使用,但不是独立的业务,例如分享组件、广告组件;还有基础组件,即单独的基础功能,与业务无关,例如 图片加载、网络请求等。

组件化

  1. 组件依赖关系上层依赖下层,修改频率是上层高于下层
  2. 基础组件是通用基础能力,修改频率相对于低,作为sdk提供所有的项目集成使用。
  3. common组件,作为支撑业务组件、业务基础服务的基础(BaseActivity/BaseFragment等基础能力),同时依赖所有的功能基础组件,提供多数业务组件需要的基本功能,并且统一了基础组件的版本号。所以业务组件、业务基础服务 所需的基础能力只需要依赖common组件即可获得。
  4. 业务组件、业务基础服务,都依赖common组件。但业务组件之间不存在依赖关系,业务基础服务之间不存在依赖关系。而业务组件 是依赖所需的业务基础组件的,例如几乎所有业务组件都会依赖广告组件 来展示Banner广告、弹窗广告等。
  5. 最上层则是主工程,即所谓的“壳工程”,主要是集成所有的业务组件、提供Application唯一实现、gradle、manifest配置,整合成完备的App

组件开发规范

  • 规范1:资源命名规则

展示项目资源图

  1. 所有的资源文件名称都需要以业务组件名前缀命名,需要注意不能与其他组件前缀冲突,例如业务组件名为:’login’,则相关资源文件命名例子:

    • layout文件:login_activity_main.xml
    • anim文件:login_anmin_hide.xml
    • drawable文件:login_et_bg.xml
    • string文件:login_mobile_input

    还需要在build.gradle文件配置,
    //但是resourcePrefix这个值只能限定xml里面的资源,并不能限定图片资源,所有图片资源仍然需要手动去修改资源名。
    resourcePrefix “login_”

  2. 所有的业务代码文件都需要以业务名称前缀命名,需要注意不能与其他组件业务代码前缀冲突,例如业务组件名称:’order’

    • 订单列表:orderList.kt
    • 订单详情:orderDetail.kt
  • 规范2:组件间通信

    1. 平时开发中我们常用接口进行解耦,对接口的实现不用关心,避免接口调用与业务逻辑实现紧密关联。这里组件间的解耦也是相同的思路,仅依赖和调用服务接口,不会依赖接口的实现

展示暴露服务api

  • 暴露组件,只存放服务接口、服务接口相关的实体类、路由信息、Fragment的获取、便于服务调用uitl等。
  • 服务调用方只依赖服务提供方的报露组件,接口的具体实现由服务方与调用相互协调后,提供工具让调用方直接调用。如home_module依赖login_export,而不是home_module依赖login_module。
  • 组件需要依赖自己暴露的组件,并实现服务接口,如login_module依赖login_cart 并实现其中的服务接口。
  • 接口的实现注入由阿里ARouter路由框架完成,和页面跳转一样使用路由信息。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
buildscript {

repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}

allprojects {
repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}
  • 规范3:数据存储

    1. 使用SharedPreferences和MMKV时,每个业务模块只管理自己模块需要的数据,SharedPreferences文件名需要通过业务前缀来区分,MMKV实例(MMKV.mmkvWithID)需要通过业务前缀来区分,防止不同组件间数据发生冲突
    2. 当某些数据需要全局共享时,可以考虑下沉到底层模块
  • 规范4:组件生命周期分发
1
2
3
task clean(type: Delete) {
delete rootProject.buildDir
}
  • 规范5:开发文档

    1. 每个组件都要维护好说明文档,通常都是一个readme文件。一般包含以下说明:

    2. 组件的功能介绍

    3. 组件功能使用说明

    4. 组件历史版本记录

    5. 注意事项

      尽量做到团队内任何一个成员,通过该文档就能使用组件,而不需要找到组件的开发人员来讲解。

mac flutter 支持多个版本配置

首先从官方下载需要的版本
https://flutter.dev/docs/development/tools/sdk/releases?tab=macos#macos
1.将下载的放在自己存放的目录 例如我的如下
Alt text

2.配置Flutter环境变量
终端打开 执行 open ~/.bash_profile 打开bash_profile文件

将文件中的:export PATH=/xx/xx/flutter/bin:$PATH [xx代表你的文件目录]

改成需要配置的flutter版本地址:export PATH=/xx/xx/flutter1.20.0/bin:$PATH

然后执行:source ~/.bash_profile

最后检测flutter是否设置成功:flutter doctor

此处红框位置Flutter (Channel stable, 1.20.0, on macOS 11.4 20F71, locale zh-Hans-CN)
此处是bash_profile配置好的版本的话代表版本安装成功。

Alt text

3.打开 Android studio 配置flutter版本和dart版本

Alt text
Alt text

4.执行pubspec.yaml 的package get 更新一下库,若不报错,即更新完成
至此,flutter更新完成。若想使用低版本,把以上地址都改回去,然后package get一下即可。

Android 7.0以上 charles OpenSSLhttps抓包

Fragment中的 Fragment的FragmentTransaction 对应
commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingStateLoss()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fragmentManager = getSupportFragmentManager();

  lifeFragment1 = new FragmentLife();
Bundle bundle = new Bundle();
bundle.putString("extra_test", "FragmentLife1");
lifeFragment1.setArguments(bundle);

//其实是通过FragmentManagerImpl获取一个BackStackRecord,
// 只能在activity存储它的状态(onSaveInstanceState(),当用户要离开activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常。
// 这是因为当activity再次被恢复时commit之后的状态将丢失。如果丢失也没关系,那么使用commitAllowingStateLoss()方法。
// commit和CommitNow都会抛出异常,如果在onSaveInstanceState()后执行的话。allowStateLoss=false,方法后面会执行检查checkStateLoss(),就是已经保存状态或stop的就会抛出异常IllegalStateException
// commitNow不允许addToBackStack,commitNow是不允许加入BackStack中去的,会将mAddToBackStack标志设置为false

//class BackStackRecord extends FragmentTransaction implements BackStackEntry, OpGenerator
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.fragment_container, lifeFragment1);
// transaction.addToBackStack("frag1"); //设置BackStackRecord的mAddToBackStack标志为true
//int类型的返回值,而commitNow是void类型返回值。
transaction.commit();
transaction.commitAllowingStateLoss();
//同commit一样调用内部的commitInternal()方法,只不过传递的参数不同,commitAllowStateLoss的allowStateLoss是true,允许丢失状态不做检查,所以不会抛异常。
//commit、commitAllowingStateLoss调用了FragmentManagerImpl.enqueueAction的方法,丢进线程队列中

transaction.commitNow();

这段代码我们经常写,会很熟悉。但有时我们可能会碰到一个异常,信息如下:

1
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

大意是在activity的onSaveInstanceState调用后再commit的Transaction导致的异常。为了不抛出异常有人建议使用commitAllowingStateLoss来代替commit。

那么commit和commitAllowingStateLoss有什么区别?

1
2
3
4
5
6
7
public int commit() {
return commitInternal(false);
}

public int commitAllowingStateLoss() {
return commitInternal(true);
}

commit和commitAllowingStateLoss都调用了commitInternal()方法,只是一个传了false,一个传了true,接着往下看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int commitInternal(boolean allowStateLoss) {
if (mCommitted) {
throw new IllegalStateException("commit already called");
}
......
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}

主要是mManager.enqueueAction(this, allowStateLoss)来执行这个任务,根据传入的参数继续往下走,可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}

可以看到最开始传进来的allowStateLoss在这里只做了检查状态的操作;

1
2
3
4
5
6
7
8
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException("Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException("Can not perform this action inside of " + mNoTransactionsBecause);
}
}

如果activity的状态被保存了,这里再提交就会检查这个状态,符合这个条件就抛出一个异常来终止应用进程。也就是说在activity调用了onSaveInstanceState()之后,再commit一个事务就会出现该异常。那如果不想抛出异常,也可以很简单调用commitAllowingStateLoss()方法来略过这个检查就可以了,但是这是危险的,如果activity随后需要从它保存的状态中恢复,这个commit是会丢失的。因此它仅仅适用在ui状态的改变对用户来说是可以接受的,允许丢失一部分状态。

总结:

1.在Activity的生命周期方法中提交事务要小心,越早越好,比如onCreate。尽量避免在onActivityResult()方法中提交。
2.避免在异步的回调方法中执行commit,因为他们感知不到当前Activity生命周期的状态。
3.使用commitAllowingStateLoss()代替commit()。相比于异常crash,UI状态的改变对用户来说是可以接受的。
4.如果你需要在Activity执行完onSaveInstanceState()之后还要进行提交,而且不关心恢复时是否会丢失此次提交,那么可以使用commitAllowingStateLoss()或commitNowAllowingStateLoss()。

2、 commitNow以及commitNowAllowingstateLoss()

在API_24版本FragmentTranslation里添加了该两个方法:

下面拿commitNow为例:
1
2
3
4
   public void commitNow() {
this.disallowAddToBackStack();
this.mManager.execSingleAction(this, false);
}

该方法不支持加入BackStack回退栈中,disallowAddToBackStack()。

  源码没有再使用Handler,而是直接执行(源码如下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void execSingleAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss || this.mHost != null && !this.mDestroyed) {
this.ensureExecReady(allowStateLoss);
if (action.generateOps(this.mTmpRecords, this.mTmpIsPop)) {
this.mExecutingActions = true;

try {
this.removeRedundantOperationsAndExecute(this.mTmpRecords, this.mTmpIsPop);
} finally {
this.cleanupExec();
}
}

this.doPendingDeferredStart();
this.burpActive();
}
}

官方更推荐使用commitNow()和commitNowAllowingStateLoss()来代替先执行commit()/commitAllowingStateLoss()

kotlin Android day

kotlin 匿名 与 具名 函数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
//匿名函数
showInfo("lr", "男", 29) {
println("$it")
}
//具名函数
showInfo("lr", "男", 29, ::showResultImpl)
}

fun showResultImpl(result: String) {
println(result)
}

inline fun showInfo(name: String, sex: String, age: Int, showResult: (String) -> Unit) {
val str = "显示信息 name = ${name} , sex = ${sex} , age = ${age}"
showResult(str)
}

Java的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ClassTest {


public static void main(String[] args) {

//匿名实现方式
showInfo("lr", "男", 29, new ShowResult() {

@Override
public void show(String text) {
System.out.println(text);
}
});

//具体实现方式
ShowResultImpl showResult = new ShowResultImpl();
showInfo("lr", "男", 29, showResult);
}


static class ShowResultImpl implements ShowResult {

@Override
public void show(String text) {
System.out.println(text);
}
}

public static void showInfo(String name, String sex, int age, ShowResult showResult) {
String value = "显示信息 name = " + name + " , sex = " + sex + " , age = " + age;
showResult.show(value);
}


interface ShowResult {
void show(String text);
}
}

【?】 安全调用操作符

1
2
3
4
5
6
7
8

var lr : String? = " I'm wang "

lr = null

lr?.capitalize()// lr是可空类型 可能是null 想要使用lr 必须给出补救措施

lr?.capitalize()// lr是可空类型 如果真的是null ?.后面的函数不执行,就不会引起空指针异常

【if】 安全调用操作符

1
2
3
4
5
6
7
8
9
10

var lr: String? = " I'm wang "

lr = null

if (lr != null) {
val r = lr.capitalize()
} else {
println(lr)
}

【?:】 空合并操作符号

1
2
3
4
5
6
7
8
9
10

var lr: String? = " I'm wang "

lr = null

//空合并操作符
println(lr ?: "I'm wang ")//如果lr等于null 就会执行?:后面的区域

//let函数 + 空合并操作符
println(lr ?.let{ "[$it]" } ?: "I'm wang")//如果lr等于null 则不会执行.let{}函数 会执行?:后面的区域

【!!】 非空断言操作符

1
2
3
4
5
6
7
8
9

var lr : String? = null

lr = " I'm wang "

//lr是可空类型 可能是null 想要使用lr 必须给出补救措施
val r = lr!!.capitalize()// !!断言 不管lr是不是null 都执行,这个和java一样

//如果百分百保证lr是有值,那么才可以使用断言!!

【let】 安全调用

1
2
3
4
5
6
7
8
9
10
11
12
var lr: String? = null
lr = " I'm wang "
//lr是可空类型 如果真的是null ?.后面的函数不执行,就不会引起空指针异常
lr?.let {
it == lr
//如果能够执行这里 it一定不为null
if (lr.isBlank()) {
"Default"
} else {
it
}
}

【substring】截取字符串

1
2
3
4
5
6
7
8
9
10
11
val subStr = "I'm lr"
val indexOf = subStr.indexOf(' ')
println(subStr.substring(0, indexOf))
println(subStr.substring(0 until indexOf))

//输出如下

I'm
I'm

Process finished with exit code 0

【split】分割字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val splitStr = "I'm,lr,wang,kotlin"
//splist 自动类型推断成 list = List<String>
val splist = splitStr.split(",")
println("分割后的list里面的元素有 ${splist}")

//C++ 解构 kt解构
val (v1, v2, v3, v4) = splist
println("解构四个只读变量值是 ${v1}, ${v2}, ${v3}, ${v4}")

//输出如下

分割后的list里面的元素有 [I'm, lr, wang, kotlin]
解构四个只读变量值是 I'm, lr, wang, kotlin

Process finished with exit code 0

Process finished with exit code 0

【replace】字符串完成加解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
val sourcePwd = "ABCDEFGHIJKL"
println("加密前处理 字符串 ${sourcePwd}")

val r = sourcePwd.replace(Regex("[ACEGI]")) {
it.value
when (it.value) { //处理每一个字符
"A" -> "1"
"C" -> "2"
"E" -> "3"
"G" -> "4"
"I" -> "5"
else -> it.value //返回原始字符
}
}
println("加密后处理 字符串 ${r}")


val r1 = r.replace(Regex("[12345]")) {
it.value
when (it.value) { //处理每一个字符
"1" -> "A"
"2" -> "C"
"3" -> "E"
"4" -> "G"
"5" -> "I"
else -> it.value //返回原始字符
}
}

println("解密后处理 字符串 ${r1}")

加密前处理 字符串 ABCDEFGHIJKL
加密后处理 字符串 1B2D3F4H5JKL
解密后处理 字符串 ABCDEFGHIJKL

Process finished with exit code 0

【== ===】比较操作

// == 值 内容比较 相当于java的equals
// === 引用的比较

【apply】内置函数

.apply返回的是自己本身 大部分的匿名函数都会返回it 而apply是持有this == 自己本身

【let】内置函数

.let 匿名函数末尾行则作为返回值 let特点 返回类型: 根据函数末尾行返回的变化而变化

let方式 + 空合并操作符 对值判null,并返回

1
2
3
4
5
6
7
8
9
10
11
fun loginApp(acc: String, pwd: String): Boolean {
if (checkText(acc, "请输入账户") == "-1") return false
if (checkText(pwd, "请输入密码") == "-1") return false
return true
}

fun checkText(text: String, hint: String): String {
return text?.let {
"-1"
} ?: hint
}

【run】内置函数

.run 根据函数末尾行返回的变化而变化 而run是持有this == 自己本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

//输出
"请输入的信息必须是中文"
.run(::isChinese)
.run(::isHint)
.run(::println)

//取得返回值
var str: String = "请输入的信息必须是中文"
.run(::isChinese)
.run(::isHint)
.run(::isRuHint)

fun isChinese(text: String): Boolean {
val reg = "[\\u4e00-\\u9fa5]".toRegex()
return reg.containsMatchIn(text)
}

fun isNumber(bol: Boolean): String {
return if (bol) "-1" else "请输入的信息必须是中文"
}

fun isRuHint(str: String): String {
return "[$str]"
}

主构造函数

规范化写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// _xxx都是 临时输入类型不能直接引用 需要临时变量接收
class PersonInfo(_name: String, _age: String, _job: String) {

private var name = _name
private set(value) {
field = value
}
private var age = _age
private set(value) {
field = value
}
private var job = _job
private set(value) {
field = value
}

fun show() {
println("$name ,$age , $job,")
}
}

主构造函数里得属性

1
2
3
4
5
6
7
//  var name  就相当于   private var name = _name   的写法  得简洁写法
class PersonInfo(var name: String, var age: String, var job: String) {

fun show() {
println("$name ,$age , $job,")
}
}

次构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PersonInfo(name: String) {

constructor(name: String, age: String) : this(name) {
println("2个参数的次构造函数 name:$name ,sex:$age")
}

constructor(name: String, age: String, job: String) : this(name) {
println("3个参数的次构造函数 name:$name ,sex:$age ,job:$job")
}
}

val lr = PersonInfo("lr")
PersonInfo("lr", "16")
PersonInfo("lr", "16", "A股")

//输出

2个参数的次构造函数 name:lr ,sex:16
3个参数的次构造函数 name:lr ,sex:16 ,job:A股

构造函数默认参数

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PersonInfo(_name: String, _age: String, _job: String) {

//此次与Java对比 不是Java的static{}
//是相当于Java的{}代 码块
init { //1
println("主构造函数被调用了...")
}

constructor(nickName: String) : this(nickName, "19", "A") {
println("次构造函数被调用了...")//2
}
}


PersonInfo("lr", "1", "job")
println("...")
PersonInfo("io")

//答应你打印顺序为
主构造函数被调用了...
...
主构造函数被调用了...
次构造函数被调用了...

函数执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//第一步生成val job: String
class PersonInfo(_name: String, _age: String, val _job: String) {

//第二步:生成val job
val job = _job

//此次与Java对比 不是Java的static{}
//是相当于Java的{}代 码块
init { //1
val jobValue = _job //第三步:生成jobValue细节
println("主构造函数被调用了...$jobValue")
}

//次构造函数
constructor(nickName: String) : this(nickName, "19", "A") {
//第四步: 生成次构造的细节
println("次构造函数被调用了...")//2
}
}


二三步骤是并行的 成员与init是同时生成的
//细节 因为 val job = _job 在init前之前所以就会先生成,再到init。

延迟初始化lateinit学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class LateinitClass {
lateinit var job: String


fun request() {
job = "编程"
}

fun showResponseResult() {

//处理技巧
if (::job.isInitialized) println("showResponseResult $job") else println("你都没有初始化加载 ")

//输出
println("showResponseResult $job")
}
}


//执行就会报错
var lc = LateinitClass()
lc.showResponseResult()

//正确方式
var lc = LateinitClass()
lc.request()
lc.showResponseResult()

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property job has not been initialized
at test.kotlin.LateinitClass.showResponseResult(ktFunction.kt:128)
at test.kotlin.KtFunctionKt.main(ktFunction.kt:115)
at test.kotlin.KtFunctionKt.main(ktFunction.kt)

因为你还没初始化 lateinit var job: String 就开始使用了。

惰性初始化 by lazay

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

class ByLazyClass {

val dataBaseData1 = readSQLServerDatabase()//01

val dataBaseData by lazy {

readSQLServerDatabase()
}

private fun readSQLServerDatabase() {
println("开始启动操作......")
println("加载读取操作......")
println("加载读取操作......")
println("加载读取操作......")
println("加载读取操作......")
println("加载读取操作......")
println("结束所有操作......")
}
}
//构建时候不会加载
var bylazy = ByLazyClass() //此次构建得时候会执行 01处代码
//开始使用才会加载
bylazy.dataBaseData

初始化常见陷阱问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14

//问题一

class TextClass(_info: String) {

//如下会报错 因为kotlin会严格按顺序执行下来的。
// variable cannot be initialized before declaration
//声明前不能初始化变量

init {
number = number.times(9)
}
private var number = 9
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

//问题二

class TextClass() {

private val info: String //01

init { //02
getInfoMethod()
info = "DerryOk"
}

fun getInfoMethod() { //03
println("info:${info[0]}")
}

}

//执行会报错 因为 TextClass()时候就会执行 02块 然后info还没被初始化就引用就会报错

TextClass().getInfoMethod()


Exception in thread "main" java.lang.NullPointerException
at test.kotlin.TextClass.getInfoMethod(ktFunction.kt:134)
at test.kotlin.TextClass.<init>(ktFunction.kt:129)
at test.kotlin.KtFunctionKt.main(ktFunction.kt:120)
at test.kotlin.KtFunctionKt.main(ktFunction.kt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

//问题三

class TextClass(_info: String) {
val content: String = getInfoMethod() //01

private val info = _info //02

private fun getInfoMethod() = info //03
}

println("内容的长度:${TextClass("test").content.length}")

//执行就会报错
Exception in thread "main" java.lang.NullPointerException
at test.kotlin.KtFunctionKt.main(ktFunction.kt:118)
at test.kotlin.KtFunctionKt.main(ktFunction.kt)


//因为调用.content时候会执行 03 再执行 02 这时候info还未被初始化。
解决方法就是将 02 与 01 顺序调换

kotlin的 继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

//Kt所有的类,默认是final修饰的,不能被继承 与Java相反
//通过关键字 open:移除final修饰
open class PersonInfo(private val name: String) {

fun showName() = "父类 的姓名是 $name"

open fun outInfo() = println(showName())
}

class Student(private val subName: String) : PersonInfo(subName) {

private fun showName() = "子类 的姓名是 $subName"

override fun outInfo() = println(showName())
}



//返回父类的引用 调用则返回是子类实现 因为重写了方法
val personInfo: PersonInfo = Student("lr")
personInfo.outInfo()

kotlin类型转换 【 is + as 】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//Kt所有的类,默认是final修饰的,不能被继承 与Java相反
//通过关键字 open:移除final修饰
open class PersonInfo(private val name: String) {

fun showName1() = "父类 的姓名是 $name"

open fun outInfo() = println(showName1())
}

class Student(private val subName: String) : PersonInfo(subName) {

private fun showName() = "子类 的姓名是 $subName"

override fun outInfo() = println(showName())
}



val personInfo: PersonInfo = Student("lr")
personInfo.outInfo()

if(personInfo is Student){
(personInfo as Student).outInfo()
}


if (personInfo is Student) {
(personInfo as Student).outInfo()
}
if (personInfo is PersonInfo) {
println ((personInfo as PersonInfo).showName1())
}



//输出

子类 的姓名是 lr
父类 的姓名是 lr

Kotlin 序列化Parcelable/Serializable

Parcelable

1.添加配置
app目录下的build.gradle文件

1
2
3
4
5
6
android {
...
androidExtensions {
experimental = true
}
}

2.添加注解并实现Parcelable
@Parcelize
data class User(val name: String) : Parcelable

完成序列化操作

3.泛型序列化

1
2
3
4
5
6
@Parcelize
data class BaseBean<T : Parcelable>(
val errorMsg: String,
val errorCode: Int,
val data: T
) : Parcelable

这里需要注意的是泛型也要实现Parcelable,即

Parcelable源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Parcelable {
int CONTENTS_FILE_DESCRIPTOR = 1;
int PARCELABLE_WRITE_RETURN_VALUE = 1;

int describeContents();

void writeToParcel(Parcel var1, int var2);

public interface Creator<T> {
T createFromParcel(Parcel var1);

T[] newArray(int var1);
}

public interface ClassLoaderCreator<T> extends Parcelable.Creator<T> {
T createFromParcel(Parcel var1, ClassLoader var2);
}
}

可以看到还是有writeToParcel方法和Creator等,其实也没少,只是不再需要我们去写了。

Serializable

Serializable的方式比较简单,直接实现Serializable就可以了

1
2
3
4
data class User(
val id: Int,
val name: String
) : Serializable

Kotlin 集合

Kotlin 集合

与Java集合类似,Kotlin 也引入了集合的概念。一个集合通常包含许多相同类型的对象,集合中的这些对象称为元素或项。 Kotlin 标准库提供了一组丰富的集合管理工具。

集合类型

在 Kotlin 中,集合分为两种形式。

1.不可变集合
2.可变集合

1. 不可变集合

这意味着它仅支持只读功能并且不能修改其元素。不可变集合及其对应的方法有:

  • 列表– listOf() 和 listOf()
  • 集合– setOf()
  • 地图– mapOf()

列表——它是一个有序集合,我们可以通过使用索引来访问元素或项目——定义每个元素位置的整数。元素可以在列表中重复任意次数。我们不能在不可变列表中执行添加或删除操作。

Kotlin 程序演示不可变列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//  Kotlin
// An example for immutable list
fun main(args: Array) {
val immutableList = listOf("Mahipal","Nikhil","Rahul")
// gives compile time error
// immutableList.add = "Praveen"
for(item in immutableList){
println(item)
}
}

输出内容:
Mahipal
Nikhil
Rahul



fun main(args: Array) {
// initialize with duplicate values
// but output with no repetition
var immutableSet = setOf(6,9,9,0,0,"Mahipal","Nikhil")
// gives compile time error
// immutableSet.add(7)
for(item in immutableSet){
println(item)
}
}

输出内容:
6
9
0
Mahipal
Nikhil

// Java
// An example for immutable map
fun main(args : Array) {
var immutableMap = mapOf(9 to "Mahipal",8 to "Nikhil",7 to "Rahul")
// gives compile time error
// immutableMap.put(9,"Praveen")
for(key in immutableMap.keys){
println(immutableMap[key])
}
}

输出内容:
Mahipal
Nikhil
Rahul

Set - 它是无序元素的集合,也不支持重复元素。它是独特元素的集合。通常,集合元素的顺序没有显着影响。我们不能执行添加或删除操作,因为它是一个不可变的 Set。

演示不可变集的 Kotlin 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
fun main(args: Array) {
// initialize with duplicate values
// but output with no repetition
var immutableSet = setOf(6,9,9,0,0,"Mahipal","Nikhil")
// gives compile time error
// immutableSet.add(7)
for(item in immutableSet){
println(item)
}
}

输出内容:
6
9
0
Mahipal
Nikhil

Map – Map 键是唯一的,每个键只保存一个值,它是一组键值对。每个键都映射到一个值。值可以是重复的,但键应该是唯一的。地图用于存储两个对象之间的逻辑连接,例如,学生 ID 及其姓名。由于它是不可变的,它的大小是固定的,并且它的方法支持只读访问。

Kotlin 程序演示不可变映射:

// An example for immutable map
fun main(args : Array) {
var immutableMap = mapOf(9 to "Mahipal",8 to "Nikhil",7 to "Rahul")
// gives compile time error
// immutableMap.put(9,"Praveen")
for(key in immutableMap.keys){
println(immutableMap[key])
}
}
输出内容:
Mahipal
Nikhil
Rahul

2. 可变集合

它支持读取和写入功能。可变集合及其对应的方法有:

列表– mutableListOf()、arrayListOf() 和 ArrayList
集合——mutableSetOf()、hashSetOf()
映射– mutableMapOf()、hashMapOf() 和 HashMap

列表——由于可变列表支持读写操作,列表中声明的元素可以被删除或添加。

Kotlin 程序演示可变列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main(args : Array) {
var mutableList = mutableListOf("Mahipal","Nikhil","Rahul")
// we can modify the element
mutableList[0] = "Praveen"
// add one more element in the list
mutableList.add("Abhi")
for(item in mutableList){
println(item)
}
}

输出内容:
Praveen
Nikhil
Rahul
Abhi

//Set – 可变 Set 支持读取和写入功能。我们可以轻松地从集合中添加或删除元素,它将保留元素的顺序。

Kotlin 程序演示可变集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
fun main(args: Array) {
var mutableSet = mutableSetOf(6,10)
// adding elements in set
mutableSet.add(2)
mutableSet.add(5)
for(item in mutableSet){
println(item)
}
}

输出内容:
6
10
2
5


//Map – 它是可变的,因此它支持 put、remove、clear 等功能。

Kotlin 程序来演示可变映射。

fun main(args : Array) {
var mutableMap = mutableMapOf(1 to "Mahipal",2 to "Nikhil",3 to "Rahul")
// we can modify the element
mutableMap.put(1,"Praveen")
// add one more element in the list
mutableMap.put(4,"Abhi")
for(item in mutableMap.values){
println(item)
}
}

输出内容:
Praveen
Nikhil
Rahul
Abhi

Kotlin语言中 集合的api中有很多强大的功能

shuffled()的函数可以对集合进行洗牌(类似于扑克牌的洗牌),这样吧每次可以获取到不同的数据,可以很容易实现类似于首页随机推荐几条数据的功能。

1
2
3
4
5
6
7
8

//运行以下函数
fun main(args:Array<String>){
val list = mutableListOf<String>("1","2","3","4","5")
list.shuffled().take(3).forEach {
print(it.toString())
}
}