首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

(一) Kotlin Android 项目实战

  • 25-04-20 11:01
  • 4555
  • 11315
juejin.cn

一、前言

安卓开发快七年了,说到底还没有完全地用kotlin实战过一个项目。这段时间打算再次重新捡起Java、Kotlin、Android。也算是对这么多年安卓知识的一次总结吧。 太多零零碎碎的知识了,一直没有认真的总结过,打算用三个月的时间再好好捡起来这些。

二、第一个Kotlin项目

Kotlin的基础语法就不从头开始了,我们就从安卓切入,将kotlin化的安卓项目遇到的一些关键函数以及思想整理一下。

三、New Project

image.png

image.png

这次在选择语言的时候记得选择主语言Kotlin

Build Config language 选择 Kotlin DSL

点击Finsh后就得到我们的第一个Kotlin项目啦

四、var和val

var:可变变量.可以通过重新分配来更改为另一个值的变量,这种声明变量的方式和java中声明变量的方式一样

val:只读变量.这种声明变量的方式相当于java中的final变量。一个val创建的时候必须初始化,因为以后不能被改变。

kotlin
代码解读
复制代码
private lateinit var binding: ActivityMainBinding

五、lateinit 和 by lazy

在这里初始化安卓项目时使用了mvvm中的viewBinding,ActivityMainBinding使用了lateinit关键字,和by lazy放在一起对比一下。

5.1 lateinit(延迟初始化)
  • 用于var变量:不可修饰基本类型(Int、Boolean),可修饰非空类型引用(如String、View等)

这里我们后面讨论一下为什么不可修饰基本类型

  • 使用过程:在调用之前主动赋值,否则空对象调用会崩溃
  • 适用场景:生命周期明确、需要在特定的生命周期回调中初始化的对象
kotlin
代码解读
复制代码
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding //先占坑,稍后绑定(不能用val,必须是var) lateinit var navView: BottomNavigationView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) println("赋值之前") checkNavView() navView = binding.navView println("赋值之后") checkNavView() val navController = findNavController(R.id.nav_host_fragment_activity_main) // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. val appBarConfiguration = AppBarConfiguration( setOf( R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications ) ) setupActionBarWithNavController(navController, appBarConfiguration) navView.setupWithNavController(navController) } fun checkNavView(){ if(::navView.isInitialized){ //检查是否初始化 println("navView 已绑定") }else{ println("navView 还未绑定") } } }
csharp
代码解读
复制代码
2025-04-01 09:46:57.256 17313-17313 System.out com.example.kotlinapplication I 赋值之前 2025-04-01 09:46:57.256 17313-17313 System.out com.example.kotlinapplication I navView 还未绑定 2025-04-01 09:46:57.256 17313-17313 System.out com.example.kotlinapplication I 赋值之后 2025-04-01 09:46:57.256 17313-17313 System.out com.example.kotlinapplication I navView 已绑定

Tips:注意事项

如果直接使用未初始化的对象,会抛出UninitializedPropertyAccessException

php
代码解读
复制代码
2025-04-01 09:49:56.993 17658-17658 AndroidRuntime com.example.kotlinapplication E FATAL EXCEPTION: main Process: com.example.kotlinapplication, PID: 17658 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.kotlinapplication/com.example.kotlinapplication.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property navView has not been initialized at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3338) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3487) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2071) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:224) at android.app.ActivityThread.main(ActivityThread.java:7561) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995) Caused by: kotlin.UninitializedPropertyAccessException: lateinit property navView has not been initialized at com.example.kotlinapplication.MainActivity.getNavView(MainActivity.kt:17) at com.example.kotlinapplication.MainActivity.onCreate(MainActivity.kt:41) at android.app.Activity.performCreate(Activity.java:7893) at android.app.Activity.performCreate(Activity.java:7880) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1306) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3313) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3487)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2071)  at android.os.Handler.dispatchMessage(Handler.java:107)  at android.os.Looper.loop(Looper.java:224)  at android.app.ActivityThread.main(ActivityThread.java:7561)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995) 
5.1 by lazy(惰性初始化)
  • 用于val变量:初始化后不可变,支持任何类型(包括基本类型)
  • 自动初始化:第一次访问时执行初始化代码,之后直接返回缓存值
  • 线程安全:默认多线程安全(可配置为高性能非安全模式)
  • 适用场景:高成本操作(如读取文件)、单例对象、按需加载的配置
kotlin
代码解读
复制代码
class AppConfig { val config by lazy{ println("读取配置文件...") loadConfigFromFile() } private fun loadConfigFromFile():Map{ //耗时处理逻辑 return mapOf("theme" to "dark","language" to "zh") } }
scss
代码解读
复制代码
//使用 by lazy val appConfig = AppConfig() println("第一次触发读取,输出:读取配置文件..." + appConfig.config) println("直接返回缓存,不再读取" + appConfig.config)
ini
代码解读
复制代码
2025-04-01 10:02:33.165 19527-19527 System.out com.example.kotlinapplication I 读取配置文件... 2025-04-01 10:02:33.165 19527-19527 System.out com.example.kotlinapplication I 第一次触发读取,输出:读取配置文件...{theme=dark, language=zh} 2025-04-01 10:02:33.165 19527-19527 System.out com.example.kotlinapplication I 直接返回缓存,不再读取{theme=dark, language=zh}
线程模式
csharp
代码解读
复制代码
//高性能模式(非线程安全,适用于单线程) val fastLazyValue by lazy(LazyThreadSafetyMode.NONE){ //初始化代码(确保只在单线程调用) }

懒委托提供了几种懒加载模式供选择

1、LazyThreadSafetyMode.SYNCHRONIZED

同步模式,确保只有单个线程可以初始化实例,这种模式下初始化是线程安全的,当by lazy没有指定模式的时候,默认使用这种模式

2、LazyThreadSafetyMode.PUBLICATION:

并发模式,在多线程下允许并发初始化,但是只有第一个返回的值作为实例,这种模式下是线程安全的。和LazyThreadSafetyMode.SYNCHRONIZED最大区别是,这种模式在多线程并发访问下初始化效率是最高的,本质上面是用空间换时间,哪个的线程执行快就让哪个先返回结果,其他线程执行的结果抛弃掉。

3、LazyThreadSafetyMode.NONE

普通模式,这种模式不会使用锁来限制多线程访问,所以是线程不安全的,所以请勿在多线程并发的情况下使用。

lateinit vs by lazy

再回到上面的问题,为什么lateinit不能修饰基本类型(如Int、Boolean等) lateinit的设计初衷是允许延迟初始化一个非空引用类型(如String、MyClass等).但它的底层实现实际上是通过一个可空引用来跟踪初始化状态的。(比如 private var_value:String?=null)来跟踪初始化状态的。当首次访问lateinit变量时,Kotlin会检查该引用是否为null.

而基本类型在JVM上是非空且不可为null的(例如int、boolean).如果允许lateinit修饰基本类型,就无法用null表示"未初始化"的状态, 导致无法实现延迟初始化的机制.

六、对比

标题lateinitby lazy
变量类型var(可变)val(不可变)
初始化方式手动赋值首次访问时自动初始化
空值处理必须非空可返回任意类型(包括可空类型)
适用类型非空对象(非Int等基本类型)所有类型(包括基本类型)
线程安全需自行保证默认安全(可配置)
崩溃风险未初始化直接使用会崩溃初始化代码异常才会崩溃
典型场景Android控件绑定、依赖注入单例、配置加载、高成本资源初始化

总结

  • lateinit 适用于非空类型的 var 属性,需要自行确保在属性访问前被初始化,不保证线程安全,不能用于基本数据类型,没有默认值

  • by lazy 适用于任何类型(可空和非空都行)的属性(var 和 val),在首次访问时才完成延迟初始化(懒加载),默认线程安全,适用于延迟初始化开销较大的对象

注:本文转载自juejin.cn的从零开始学安卓的文章"https://juejin.cn/post/7487913431667097641"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

140
Android
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top