Kotlin 一劳永逸实现 TAG

文章介绍了在Kotlin中如何优雅地处理Android开发中的日志TAG,包括使用扩展函数实现统一的TAG定义,处理不同API级别下的长度限制,以及处理匿名内部类和混淆问题。推荐的写法是使用内联扩展函数,动态计算类名作为TAG。
摘要由CSDN通过智能技术生成

1 TAG 经典写法

        对于 Android 开发,当我们需要在类中打印 Log 时,通常在Java中会这么定义一个 TAG:

    private static final String TAG = "TestClass";

        或者不具体指定名字:

    private static final String TAG = TestClass.class.getSimpleName();

那么,在 Kotlin 中

        我们通常会这样定义:

    private val TAG = TestClass::class.java.simpleName

        或者采用类似于 Java 的静态方式实现:

    companion object {
        private val TAG = TestClass::class.java.simpleName
    }

        甚至还加上 @JvmField 注解,方便外部调用:

    companion object {
        @JvmField val TAG: String = TestClass::class.java.simpleName
    }

        companion object 的属性/方法会产生额外的 static final 的同伴类实例,因此在性能和内存方面都很糟糕,所以大可不必将 TAG 放在 companion object,下面2张图或许可以告诉你一些东西:

2 TAG 一劳永逸

        以上方式,都需要在每个类定义相应的 TAG,是比较繁琐的。

        那么,在 Kotlin 中可以通过扩展函数统一声明 TAG,避免在每个类中定义相应的 TAG。

我们可以通过如下两种方式实现:

        方式一:

val Any.TAG: String
    get() {
        return javaClass.simpleName
    }

        方式二:

inline val <reified T> T.TAG: String
    get() = T::class.java.simpleName

3 TAG 持续优化

3.1 TAG 长度限制

        我们知道,从API 24开始,TAG 就没有长度限制,所以需要对长度做个兼容:

        方式一:

val Any.TAG: String
    get() {
        val name = javaClass.simpleName
        return if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
        else name.substring(0, 23)
    }

        方式二: 

inline val <reified T>T.TAG: String
    get() {
        val name = T::class.java.simpleName
        return if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
        else name.substring(0, 23)
    }

3.2 匿名内部类的 TAG

        匿名内部类长度较长,我们可以使用 name 代替 simpleName 去解析相应 TAG:

        方式一:

val Any.TAG: String
    get() {
        return if (!javaClass.isAnonymousClass) {
            val name = javaClass.simpleName
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(0, 23)
        } else {
            val name = javaClass.name
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(name.length - 23, name.length)
        }
    }

        方式二: 

inline val <reified T> T.TAG: String
    get() {
        return if (!T::class.javaClass.isAnonymousClass) {
            val name = T::class.java.simpleName
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(0, 23)
        } else {
            val name = T::class.java.name
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(name.length - 23, name.length)
        }
    }

3.3 类被混淆的 TAG

        当类被混淆时,simpleName 不是一个好的解决方案,例如 MyActivity 可能会变成 M,此刻或许在类中显示声明 TAG 会更好:

    private val TAG = "TestClass"

4 推荐写法

        两种方式差别不大,由于作者更喜欢方式二内联实现,所以本文推荐方式二,实现一劳永逸的 TAG。

inline val <reified T> T.TAG: String
    get() {
        return if (!T::class.javaClass.isAnonymousClass) {
            val name = T::class.java.simpleName
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(0, 23)
        } else {
            val name = T::class.java.name
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(name.length - 23, name.length)
        }
    }


走一段令人留恋的路,做一个不负自己的人

本文 Demo 代码参考参见后文


【Extensions.kt 文件】

package com.agg.utils

import android.os.Build

/**
 * Description:
 * CreateDate:     2023/6/20 16:12
 * Author:         agg
 */
class Extensions {}

//     private static final String TAG = "TestClass";

//     private static final String TAG = TestClass.class.getSimpleName();

//     private val TAG = TestClass::class.java.simpleName

//      companion object {
//          private val TAG = TestClass::class.java.simpleName
//      }

//      companion object {
//          @JvmField val TAG: String = TestClass::class.java.simpleName
//      }

//val Any.TAG: String
//    get() {
//        return javaClass.simpleName
//    }

//inline val <reified T> T.TAG: String
//    get() = T::class.java.simpleName

//val Any.TAG: String
//    get() {
//        val name = javaClass.simpleName
//        return if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
//        else name.substring(0, 23)
//    }

//inline val <reified T>T.TAG: String
//    get() {
//        val name = T::class.java.simpleName
//        return if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
//        else name.substring(0, 23)
//    }

//val Any.TAG: String
//    get() {
//        return if (!javaClass.isAnonymousClass) {
//            val name = javaClass.simpleName
//            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
//            else name.substring(0, 23)
//        } else {
//            val name = javaClass.name
//            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
//            else name.substring(name.length - 23, name.length)
//        }
//    }

inline val <reified T> T.TAG: String
    get() {
        return if (!T::class.javaClass.isAnonymousClass) {
            val name = T::class.java.simpleName
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(0, 23)
        } else {
            val name = T::class.java.name
            if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name
            else name.substring(name.length - 23, name.length)
        }
    }

【参考文档】在Kotlin中定义log TAG常量的最佳方式是什么?

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是使用Kotlin实现日期范围选择器的示例代码: ```kotlin import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.borax12.materialdaterangepicker.date.DatePickerDialog import kotlinx.android.synthetic.main.activity_main.* import java.text.SimpleDateFormat import java.util.* class MainActivity : AppCompatActivity(), DatePickerDialog.OnDateSetListener { private lateinit var startDate: Calendar private lateinit var endDate: Calendar override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 初始化日期范围选择器 startDate = Calendar.getInstance() endDate = Calendar.getInstance() val now = Calendar.getInstance() val dpd = DatePickerDialog.newInstance( this@MainActivity, startDate.get(Calendar.YEAR), startDate.get(Calendar.MONTH), startDate.get(Calendar.DAY_OF_MONTH) ) dpd.minDate = now dpd.showYearPickerFirst(true) dpd.setVersion(DatePickerDialog.Version.VERSION_2) dpd.setTitle("Select Date Range") // 设置起始日期按钮 start_date_button.setOnClickListener { dpd.setOnCancelListener { // 如果用户取消了选择,则不更新日期 } dpd.show(supportFragmentManager, "Datepickerdialog") } // 设置结束日期按钮 end_date_button.setOnClickListener { dpd.setOnCancelListener { // 如果用户取消了选择,则不更新日期 } dpd.show(supportFragmentManager, "Datepickerdialog") } // 设置搜索按钮 search_button.setOnClickListener { val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val startDateString = dateFormat.format(startDate.time) val endDateString = dateFormat.format(endDate.time) val searchQuery = search_box.text.toString() // 在这里添加处理搜索和日期范围的逻辑 } } override fun onDateSet(view: DatePickerDialog?, year: Int, monthOfYear: Int, dayOfMonth: Int, yearEnd: Int, monthOfYearEnd: Int, dayOfMonthEnd: Int) { startDate.set(Calendar.YEAR, year) startDate.set(Calendar.MONTH, monthOfYear) startDate.set(Calendar.DAY_OF_MONTH, dayOfMonth) endDate.set(Calendar.YEAR, yearEnd) endDate.set(Calendar.MONTH, monthOfYearEnd) endDate.set(Calendar.DAY_OF_MONTH, dayOfMonthEnd) updateDateLabels() } private fun updateDateLabels() { val dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.getDefault()) start_date_label.text = dateFormat.format(startDate.time) end_date_label.text = dateFormat.format(endDate.time) } } ``` 在这个示例中,我们使用了DatePickerDialog库来实现日期范围选择器。我们还使用了SimpleDateFormat来格式化所选日期,并使用lateinit来声明startDate和endDate变量,并在onDateSet回调中更新它们。 我们还使用了kotlinx.android.synthetic来引用布局文件中的视图,这使得我们可以轻松地访问和操作视图。 最后,我们添加了一个处理搜索和日期范围的逻辑,您可以根据您的应用程序需求进行自定义。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值