当前位置: 首页 > news >正文

Android学习总结之kotlin篇(一)

1. open 关键字的用法和作用深入源码分析

  • 类的 open 修饰:在 Kotlin 字节码层面,对于一个open类,编译器会在生成的字节码中添加ACC_SUPERACC_OPEN标志。例如,定义一个open class TestOpenClass,反编译其字节码可以看到类似如下信息:
public class TestOpenClass {// 类的成员
}
// 字节码中会包含这些标志
ACC_PUBLIC, ACC_SUPER, ACC_OPEN

这表明该类是可继承的,ACC_OPEN标志告知虚拟机这个类可以被其他类继承。当有类继承这个open类时,如class SubTestOpenClass : TestOpenClass(),在生成的SubTestOpenClass字节码中会体现出对TestOpenClass的继承关系。

  • 成员函数的 open 修饰:当一个函数被open修饰,在字节码层面,函数会带有ACC_OPEN标志。例如:
open class OpenFunctionClass {open fun openFunction() {// 函数体}
}

反编译字节码,openFunction函数的字节码会有ACC_PUBLIC, ACC_VIRTUAL, ACC_OPEN标志。ACC_VIRTUAL表示这是一个虚方法,支持多态调用,ACC_OPEN则表示该方法可以在子类中被重写。子类重写该方法时,会在字节码中体现重写关系,并遵循 Java 虚拟机规范中的方法重写规则。

  • 属性的 open 修饰:对于open修饰的属性,以open var openProperty: String为例,在字节码层面,属性的gettersetter方法(如果是可变属性)会带有ACC_OPEN标志。例如反编译字节码后,getOpenProperty方法会有ACC_PUBLIC, ACC_OPEN标志,这意味着子类可以重写该属性的访问器方法来改变属性的获取或设置行为。

2. 为什么 Kotlin 要用 open 关键字来标志当前类可以继承深入源码分析

  • 默认 final 类的实现:Kotlin 默认类是final的,这在字节码层面体现为类的字节码标志只有ACC_PUBLIC, ACC_SUPER(没有ACC_OPEN)。例如:
class FinalClass {// 类成员
}

反编译字节码,不会出现ACC_OPEN标志,这使得该类不能被继承。这种设计的好处是,从字节码角度保证了类的稳定性和安全性,防止在运行时被意外继承而破坏原有逻辑。

  • open 关键字改变继承性原理:当使用open关键字修饰类时,如前所述,字节码中会添加ACC_OPEN标志。这是 Kotlin 编译器在编译过程中根据open关键字做出的处理,告知虚拟机该类可被继承。这种设计使得开发者可以在需要时,通过添加open关键字,有针对性地开放类的继承,符合 Kotlin 对代码安全性和可维护性的追求。从语言设计角度看,Kotlin 编译器在处理继承关系时,会根据类和成员的open修饰情况,生成符合 Java 虚拟机规范的字节码,保证继承关系的正确实现。

3. by lazy 和 lateinit 深入源码分析(回答 Android 面试官问题)

  • by lazyby lazy的实现依赖于kotlin.Lazy接口及其实现类kotlin.LazyKt__LazyJVMKt.LazyImplby lazy本质上是通过委托给Lazy对象来实现延迟初始化。例如:
val lazyValue: String by lazy { "Hello" }

这里lazy是一个函数,其定义如下:

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

SynchronizedLazyImplLazy接口的一个实现类,它会在getValue方法被调用时(即第一次访问属性时),调用initializer函数进行初始化,并将结果缓存起来,后续访问直接返回缓存值。在 Android 开发中,这种机制利用了对象的延迟初始化特性,减少了不必要的资源消耗,提升了应用性能。

  • lateinitlateinit主要是 Kotlin 编译器层面的一种处理机制。当使用lateinit声明属性时,编译器会进行特殊处理。例如lateinit var myProperty: MyType,编译器会在字节码中标记该属性为延迟初始化属性。在使用该属性时,编译器会生成代码检查属性是否已经初始化,如果未初始化则抛出UninitializedPropertyAccessException异常。在 Android 开发中,这种机制方便了视图绑定等场景,让开发者可以先声明属性,在合适时机进行初始化,同时通过编译器的检查保证了代码的健壮性。

kotlin的扩展函数使用

1. View 相关扩展

1.1 设置 View 透明度动画
import android.animation.ObjectAnimator
import android.view.View/*** 为 View 设置透明度动画* @param targetAlpha 目标透明度,取值范围 0f 到 1f* @param duration 动画持续时间,单位为毫秒*/
fun View.fadeTo(targetAlpha: Float, duration: Long = 300L) {// 创建透明度动画,从当前透明度过渡到目标透明度val animator = ObjectAnimator.ofFloat(this, "alpha", alpha, targetAlpha)// 设置动画持续时间animator.duration = duration// 启动动画animator.start()
}// 示例调用
val myView: View = findViewById(R.id.my_view)
myView.fadeTo(0.5f)

1.2 测量 View 的尺寸
import android.view.View/*** 测量 View 的尺寸* @return 包含 View 测量宽度和高度的 Pair*/
fun View.measureSize(): Pair<Int, Int> {// 获取 View 的布局参数val layoutParams = layoutParams// 计算测量规格val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(layoutParams.width,if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {View.MeasureSpec.AT_MOST} else {View.MeasureSpec.EXACTLY})val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(layoutParams.height,if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {View.MeasureSpec.AT_MOST} else {View.MeasureSpec.EXACTLY})// 进行测量measure(widthMeasureSpec, heightMeasureSpec)// 返回测量结果return measuredWidth to measuredHeight
}// 示例调用
val myView: View = findViewById(R.id.my_view)
val (width, height) = myView.measureSize()

2. Context 相关扩展

2.1 获取屏幕宽度和高度
import android.content.Context
import android.util.DisplayMetrics/*** 获取屏幕宽度* @return 屏幕宽度,单位为像素*/
fun Context.screenWidth(): Int {// 获取屏幕显示指标val displayMetrics = resources.displayMetricsreturn displayMetrics.widthPixels
}/*** 获取屏幕高度* @return 屏幕高度,单位为像素*/
fun Context.screenHeight(): Int {// 获取屏幕显示指标val displayMetrics = resources.displayMetricsreturn displayMetrics.heightPixels
}// 示例调用
val context: Context = this
val screenWidth = context.screenWidth()
val screenHeight = context.screenHeight()
2.2 从 Context 启动 Service
import android.content.Context
import android.content.Intent/*** 从 Context 启动 Service* @param T 要启动的 Service 类* @param init 用于配置 Intent 的 Lambda 表达式*/
inline fun <reified T : Service> Context.startServiceEx(noinline init: Intent.() -> Unit = {}) {// 创建启动 Service 的 Intentval intent = Intent(this, T::class.java)// 配置 Intentintent.init()// 启动 ServicestartService(intent)
}// 示例调用
class MyService : Service() {// Service 实现代码
}val context: Context = this
context.startServiceEx<MyService> {putExtra("key", "value")
}
2.3 获取 Drawable 资源
import android.content.Context
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompatfun Context.getDrawableCompat(resId: Int): Drawable? {return ContextCompat.getDrawable(this, resId)
}// 示例调用
val context: Context = this
val drawable = context.getDrawableCompat(R.drawable.my_drawable)
2.4 获取颜色资源
import android.content.Context
import androidx.core.content.ContextCompatfun Context.getColorCompat(resId: Int): Int {return ContextCompat.getColor(this, resId)
}// 示例调用
val context: Context = this
val color = context.getColorCompat(R.color.my_color)

3. Activity 相关扩展

3.1 关闭当前 Activity 并返回结果
import android.app.Activity
import android.content.Intent/*** 关闭当前 Activity 并返回结果* @param resultCode 结果码* @param data 要返回的数据*/
fun Activity.finishWithResult(resultCode: Int, data: Intent? = null) {// 设置返回结果setResult(resultCode, data)// 关闭 Activityfinish()
}// 示例调用
val activity: Activity = this
val resultIntent = Intent()
resultIntent.putExtra("result", "success")
activity.finishWithResult(Activity.RESULT_OK, resultIntent)
3.2 隐藏软键盘
import android.app.Activity
import android.view.inputmethod.InputMethodManager/*** 隐藏当前 Activity 的软键盘*/
fun Activity.hideSoftKeyboard() {// 获取当前焦点 Viewval view = currentFocusif (view != null) {// 获取输入法管理器val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager// 隐藏软键盘imm.hideSoftInputFromWindow(view.windowToken, 0)}
}// 示例调用
val activity: Activity = this
activity.hideSoftKeyboard()

4. Fragment 相关扩展

4.1 获取 Fragment 的父 Activity 并进行类型转换
import androidx.fragment.app.Fragment/*** 获取 Fragment 的父 Activity 并进行类型转换* @param T 要转换的 Activity 类型* @return 转换后的 Activity 实例,如果转换失败则返回 null*/
inline fun <reified T : Activity> Fragment.getParentActivity(): T? {return activity as? T
}// 示例调用
class MyFragment : Fragment() {fun doSomething() {val parentActivity = getParentActivity<MainActivity>()parentActivity?.let {// 使用父 Activity 进行操作}}
}
4.2 为 Fragment 添加回退栈
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction/*** 将 Fragment 添加到指定容器并添加到回退栈* @param containerId 容器 ID* @param tag Fragment 的标签*/
fun Fragment.addToBackStack(containerId: Int, tag: String? = null) {// 获取 FragmentManagerval fragmentManager = requireActivity().supportFragmentManager// 开始事务fragmentManager.beginTransaction().add(containerId, this, tag).addToBackStack(tag).commit()
}// 示例调用
val myFragment = MyFragment()
myFragment.addToBackStack(R.id.fragment_container, "MyFragment")
4.3 替换 Fragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransactionfun FragmentManager.replaceFragment(containerId: Int, fragment: Fragment) {beginTransaction().replace(containerId, fragment).commit()
}// 示例调用
val fragmentManager = supportFragmentManager
val newFragment = MyFragment()
fragmentManager.replaceFragment(R.id.fragment_container, newFragment)
4.4 获取 Activity 的视图绑定
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBindinginline fun <reified T : ViewBinding> Fragment.viewBinding() =FragmentViewBindingDelegate(T::class.java, this)// 自定义委托类
class FragmentViewBindingDelegate<T : ViewBinding>(private val bindingClass: Class<T>,fragment: Fragment
) : ReadOnlyProperty<Fragment, T> {private var binding: T? = nullinit {fragment.lifecycle.addObserver(object : LifecycleEventObserver {override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {if (event == Lifecycle.Event.ON_DESTROY) {binding = null}}})}override fun getValue(thisRef: Fragment, property: KProperty<*>): T {binding?.let { return it }val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java)val invokeLayout = inflateMethod.invoke(null, thisRef.layoutInflater) as TthisRef.view = invokeLayout.rootreturn invokeLayout.also { binding = it }}
}// 在 Fragment 中使用
class MyFragment : Fragment(R.layout.fragment_my) {private val binding by viewBinding<FragmentMyBinding>()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 使用 bindingbinding.textView.text = "Hello, World!"}
}

5. RecyclerView 相关扩展

5.1 为 RecyclerView 设置空视图
import androidx.recyclerview.widget.RecyclerView
import android.view.View/*** 为 RecyclerView 设置空视图* @param emptyView 空视图*/
fun RecyclerView.setEmptyView(emptyView: View) {// 监听 RecyclerView 的数据变化adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {override fun onChanged() {super.onChanged()// 根据数据数量显示或隐藏空视图和 RecyclerViewshowHideEmptyView()}override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {super.onItemRangeInserted(positionStart, itemCount)showHideEmptyView()}override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {super.onItemRangeRemoved(positionStart, itemCount)showHideEmptyView()}private fun showHideEmptyView() {if (adapter?.itemCount == 0) {emptyView.visibility = View.VISIBLEthis@setEmptyView.visibility = View.GONE} else {emptyView.visibility = View.GONEthis@setEmptyView.visibility = View.VISIBLE}}})// 初始时显示或隐藏空视图和 RecyclerViewshowHideEmptyView()
}// 示例调用
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
val emptyView: View = findViewById(R.id.empty_view)
recyclerView.setEmptyView(emptyView)
5.2 滚动到 RecyclerView 的顶部
import androidx.recyclerview.widget.RecyclerView/*** 滚动 RecyclerView 到顶部*/
fun RecyclerView.scrollToTop() {// 平滑滚动到顶部smoothScrollToPosition(0)
}// 示例调用
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
recyclerView.scrollToTop()
5.3 快速设置 RecyclerView
import androidx.recyclerview.widget.RecyclerViewfun RecyclerView.setup(layoutManager: RecyclerView.LayoutManager,adapter: RecyclerView.Adapter<*>,hasFixedSize: Boolean = true
) {this.layoutManager = layoutManagerthis.adapter = adapterthis.setHasFixedSize(hasFixedSize)
}// 示例调用
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
val layoutManager = LinearLayoutManager(this)
val adapter = MyAdapter()
recyclerView.setup(layoutManager, adapter)
5.2 添加分割线
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerViewfun RecyclerView.addDivider() {val divider = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)addItemDecoration(divider)
}// 示例调用
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
recyclerView.addDivider()

扩展函数在 Android 开发中能够显著提高代码的复用性和可维护性,让开发过程更加高效。

相关文章:

  • 关于图论的知识
  • 正则表达式三剑客之——grep和sed
  • 有关图的类型的题目(1)
  • 从基础到实战的量化交易全流程学习:1.2 金融市场基础
  • Springboot用IDEA打jar包 运行时 错误: 找不到或无法加载主类
  • 路由器重分发(OSPF+RIP),RIP充当翻译官,OSPF充当翻译官
  • 【C++】15. 模板进阶
  • Eigen几何变换类 (Transform, Quaternion等)
  • 学习笔记:Qlib 量化投资平台框架 — GETTING STARTED
  • 将服务器接到路由器上访问
  • 【Leetcode 每日一题】2444. 统计定界子数组的数目
  • 图像特征检测算法对比及说明
  • 2025.4.26总结
  • ADC单通道采集实验
  • 3:QT联合HALCON编程—海康相机SDK二次程序开发
  • Android12源码编译及刷机
  • 详解Adobe Photoshop 2024 下载与安装教程
  • 论文笔记(八十)π0.5: a Vision-Language-Action Model with Open-World Generalization
  • Python并行计算:2.Python多线程编程:threading模块详解与守护线程实战
  • Spring Boot 3.4 实战指南:从性能优化到云原生增强
  • 一场与纪录并行的伦敦马拉松,超40项新世界纪录诞生
  • 最近这75年,谁建造了上海?
  • 非法收受财物逾1648万,湖南原副厅级干部康月林一审被判十年半
  • 事关稳就业稳经济,10张海报看懂这场发布会的政策信号
  • 五一假期“热潮”来袭,计划南下的小伙伴注意了
  • 饶权已任国家文物局局长