当前位置:   article > 正文

【Kotlin For Android】(二): Kotlin Android Extensions

property would not be serialized into a 'parcel'. add '@ignoredonparcel' ann

kotlin-android-extensions 插件

官网介绍

一、简介

Kotlin Android扩展插件 可以节省 findviewbyid(),实现 和 Data-BindingDagger 框架的效果,不需要添加任何额外代码,也不影响任何运行时体验。

Kotlin Android扩展 是 Kotlin 插件的组成之一,不需要在单独安装插件。

如下实例:

  1. // Using R.layout.activity_main from the 'main' source set
  2. import kotlinx.android.synthetic.main.activity_main.*
  3. class MyActivity : Activity() {
  4. override fun onCreate(savedInstanceState: Bundle?) {
  5. super.onCreate(savedInstanceState)
  6. setContentView(R.layout.activity_main)
  7. // Instead of findViewById<TextView>(R.id.textView)
  8. textView.setText("Hello, world!")
  9. }
  10. }

textView 是Activity的扩展属性,而且它和 在 activity_main 中声明的 有同样的类型(因此它是一个 TextView)。

二、使用 Kotlin Android Extensions

2.1、Gradle 配置

添加

apply plugin: 'kotlin-android-extensions'

2.2、导入合成属性

**在 Activity中: ** 按照 import kotlinx.android.synthetic.main.<布局>.*格式,可以导入布局文件中所有控件属性。

**在 View 中(Adapter , Fragment等) 中: ** 按照 import kotlinx.android.synthetic.main.<布局>.view.*格式,可以导入布局文件中所有控件属性。

例子:

  1. <TextView
  2. android:id="@+id/hello"
  3. android:layout_width="fill_parent"
  4. android:layout_height="wrap_content"/>
activity.hello.text = "Hello World!"

2.3、实验模式(Experimental Mode)

Android 扩展插件包含一些实验性功能。比如,LayoutContainer支持 和 Parcelable实现生成器。 这些功能目前还是实验性的,所以需要在 build.gradle中打开它以能够去使用这些功能:

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

2.4、LayoutContainer Support

安卓扩展插件支持多种 containers,最基本的一些有 ActivityFragmentView,但是可以通过 实现 LayoutController 接口 将任何类 (实际上)转为Android 扩展容器:

  1. import kotlinx.android.extensions.LayoutContainer
  2. class ViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer {
  3. fun setup(title: String) {
  4. itemTitle.text = "Hello World!"
  5. }
  6. }

需要配置 experimental mode

2.4.1、Kotlin之Fragment中直接引用视图控件id,报空指针的问题

解决方案一

要想直接使用控件 id需要符合前置条件,就是对应的 layout 文件加载完毕后才可以直接使用控件id来操作,如果你在 onCreateView() 方法中去直接使用控件id去操作,肯定是空指针异常,因为return view还没有执行呢 。

在确保 onCreateView() 方法执行完毕后,就可以直接使用控件id来操作。

解决方案二

在fragment使用这个extensions的时候,会找不到那个控件,解决办法

  1. onCreateView()
  2. tv_show = view?.findViewById(R.id.tv_show) as TextView
  3. importkotlinx.android.synthetic.main.gv_home_fragment_item.*
  4. onViewCreated()
  5. tv_show.text="123456789"

2.5、Flavor Support

安卓扩展插件支持 Android flavors

2.5.1、什么是 flavor

Flavaor 的作用: 将 debug 和 release 的 维度 扩大。

flavorDimensions: 是基于 flavor 的一个扩展,作用是再次扩大维度,它是一个属性,学会用它必须要先会用 flavor

2.5.2、维度

在理解了flavor的前提下(Flavor基本使用),我们需要明白一个概念—————维度

维度在 gradle 中的体现是 : flavorDimensions

源码:

  1. public void flavorDimensions(String... dimensions) {
  2. this.checkWritability();
  3. this.flavorDimensionList = Arrays.asList(dimensions);
  4. }

譬如:

flavorDimensions(“money”, “channel”)

这是一个正确的维度扩展思路:

  • money 场景,一个APP,我们推出收费和免费的版本。
  • channel 场景,不同的市场渠道。

根据这个思路来改代码:
原有:

  1. buildTypes {
  2. release {
  3. minifyEnabled true
  4. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  5. signingConfig signingConfigs.release
  6. }
  7. debug {
  8. minifyEnabled false
  9. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  10. signingConfig signingConfigs.debug
  11. }
  12. }
  13. productFlavors {
  14. baidu {
  15. manifestPlaceholders = [CHANNEL: "baidu"]
  16. }
  17. xiaomi {
  18. manifestPlaceholders = [CHANNEL: "xiaomi"]
  19. }
  20. }

目前有四个渠道:

  • baiduDebug
  • baiduRelease
  • xiaomiDebug
  • xiaomiRelease

添加flavorDimensions后:

  1. android{
  2. ...
  3. flavorDimensions("money", "channel")
  4. productFlavors {
  5. vip {
  6. dimension "money"
  7. }
  8. free{
  9. dimension "money"
  10. }
  11. baidu {
  12. manifestPlaceholders = [CHANNEL: "baidu"]
  13. dimension "channel"
  14. }
  15. xiaomi {
  16. manifestPlaceholders = [CHANNEL: "xiaomi"]
  17. dimension "channel"
  18. }
  19. }
  20. ...
  21. }

现在就会有8个渠道了:

  • vipBaiduDebug
  • vipBaiduRelease
  • vipXiaomiDebug
  • vipXiaomiRelease
  • freeBaiduDebug
  • freeBaiduRelease
  • freeXiaomiDebug
  • freeXiaomiRelease

逻辑上就是:

由:

debug/releas(2) * 渠道(2) = 4个

变成了:

debug/releas(2) * 渠道(2) * 新增维度vip/free(2) = 4 * 2 = 8个

然后我们再从代码理解上来:

  • 只要你定义了flavorDimensions,那么你后面的每个flavor就必须要写”dimension”这个属性。
  • baidu和xiaomi是属于渠道这个维度,所以它的dimension是channel。
  • vip和free这俩个flavor是属于money这个维度(收费与免费),所以它的dimension是money。

2.5.3、添加 flavor

  1. android {
  2. productFlavors {
  3. free {
  4. versionName "1.0-free"
  5. }
  6. }
  7. }

所以可以将free/res/layout/activity_free.xml的布局通过导入添加:

import kotlinx.android.synthetic.free.activity_free.*

experimental mode中,您可以指定任何 variant 名称(不仅仅是 flavor),例如 freeDebugfreeRelease也起作用。(这个不是很懂

2.6、视图缓存 (View Caching)

使用 findViewById()可能会变得很慢,尤其是很复杂的视图层中。所以,Android 扩展试图通过 缓存试图来减少 findViewById()

默认情况下,Android 扩展 在每个 container written in KotlinActivity, Fragment, View or a LayoutContainer implementation) 添加了一个隐藏的缓存函数 和一个存储 field。

这个方法非常小,所以不会怎么增加 APK 的大小。

下面的例子中,findViewById()只被调用了一次:

  1. class MyActivity : Activity()
  2. fun MyActivity.a() {
  3. textView.text = "Hidden view"
  4. textView.visibility = View.INVISIBLE
  5. }

但是,在下面的例子中:

  1. fun Activity.b() {
  2. textView.text = "Hidden view"
  3. textView.visibility = View.INVISIBLE
  4. }

我们不知道 这个函数是否仅仅在 我们源代码的 activities 或者 简单的Java activities 中调用。因此,即使 MyActivity 的实例作为接收者传递,我们不会在这里使用缓存。

2.7、更改视图缓存策略

您可以更改全局或每个容器的缓存策略。这也需要打开 experimental mode

2.7.1 设置项目全局缓存策略

项目全局缓存策略 在 build.gradle文件中配置:

  1. android{
  2. androidExtensions {
  3. experimental = true
  4. defaultCacheImplementation = "HASH_MAP" // also SPARSE_ARRAY, NONE
  5. }
  6. }

默认情况下,Android扩展插件 使用 HashMap作为后备存储,但您可以切换到 SparseArray 实现或者关闭缓存。当您仅使用Android扩展的 Parcelable 部分时,后者特别有用。

2.7.2、设置 container 缓存策略

用一个容器注解@ContainerOptions来改变它的缓存策略:

  1. import kotlinx.android.extensions.ContainerOptions
  2. @ContainerOptions(cache = CacheImplementation.NO_CACHE)
  3. class MyActivity : Activity()
  4. fun MyActivity.a() {
  5. // findViewById() will be called twice
  6. textView.text = "Hidden view"
  7. textView.visibility = View.INVISIBLE
  8. }

2.8、Parcelable (序列化)

Kotlin 1.1.4 开始,Android扩展插件 提供了 Parcelable 实现生成器作为实验性功能。

Android中的序列化

在开发中,如果有需要用到序列化和反序列化的操作,就会用到 Serializable 或者 Parcelable,它们各有优缺点,会适用于不同的场景。

Serializable

Serializable 的优点是实现简单,你只需要实现一个 Serializable 接口,并不需要任何额外的代码,但是它的序列化和反序列化,实际上是使用反射做的,所以效率会略低,并且它会在序列化的过程中,会创建很多临时变量,所以更容易触发 GC。

Parcelable

Parcelable 需要开发者自己去实现序列化的规则,所以会增加代码量,正是因为规则确定,所以效率会提高很多,并且不容易触发 GC。

2.8.1 Kotlin中,启用 Parcelable支持 :

打开 experimental mode

2.8.2、Kotlin中如何使用

使用 @Parcelize 注解类,然后实现Parcelable接口(方法实现自动生成)。

  1. import kotlinx.android.parcel.Parcelize
  2. @Parcelize
  3. class User(val firstName: String, val lastName: String, val age: Int): Parcelable

@Parcelize要求在主构造函数中声明所有序列化的属性。Android 扩展将 对声明在类体中带有 幕后字段的属性发出警告:

  1. @Parcelize
  2. class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
  3. // [PLUGIN WARNING]:Property would not be serialized into a `Parcel`.
  4. // Add `@IgnoreOnParcer` annotation to remove warning
  5. var sex: Int = 0
  6. }

另外, 如果某些主构造函数参数不是属性,则@Parcelize 不能应用:

  1. class User(val firstName: String, lastName: String, val age: Int) // 可以
  2. // 报错。[PLUGIN ERROR]:`Parcelabel constructor parameter should be val or var
  3. @Parcelize
  4. class User(val firstName: String, lastName: String, val age: Int) : Parcelable

如果类需要更高级的序列化逻辑,你可以把它写在一个伴随类中:

  1. @Parcelize
  2. data class Value(val firstName: String, val lastName: String, val age: Int) : Parcelable {
  3. private companion object : Parceler<User> {
  4. override fun User.write(parcel: Parcel, flags: Int) {
  5. // Custom write implementation
  6. }
  7. override fun create(parcel: Parcel): User {
  8. // Custom read implementation
  9. }
  10. }
  11. }

2.8.3、@Parcelize 支持的类型

原始类型(及其盒装版本); 对象和枚举;

  • String,CharSequence;
  • Exception;
  • Size,SizeF,Bundle,IBinder,IInterface,FileDescriptor;
  • SparseArray,SparseIntArray,SparseLongArray,SparseBooleanArray,
  • 所有Serializable(是的,Date也支持)和Parcelable实现;
  • 所有受支持类型的集合:List( 映射到ArrayList ),Set(映射到LinkedHashSet),Map(映射到LinkedHashMap);
    • 也有一些具体的实现的:ArrayList,LinkedList,SortedSet,NavigableSet,HashSet,LinkedHashSet,TreeSet,SortedMap,NavigableMap,HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap;
  • 所有支持类型的数组;
  • 所有受支持类型的可空版本。

2.8.4、自定义 Parcelers (不理解)

即使你的类型不直接支持,你也可以为它写一个 Parceler 映射对象。

  1. class ExternalClass(val value: Int)
  2. object ExternalClassParceler : Parceler<ExternalClass> {
  3. override fun create(parcel: Parcel) = ExternalClass(parcel.readInt())
  4. override fun ExternalClass.write(parcel: Parcel, flags: Int) {
  5. parcel.writeInt(value)
  6. }
  7. }

使用@TypeParceler@WriteWith注解,外部 parcelers 可以被应用:

  1. // Class-local parceler
  2. @Parcelable
  3. @TypeParceler<ExternalClass, ExternalClassParceler>()
  4. class MyClass(val external: ExternalClass)
  5. // Property-local parceler
  6. @Parcelable
  7. class MyClass(@TypeParceler<ExternalClass, ExternalClassParceler>() val external: ExternalClass)
  8. // Type-local parceler
  9. @Parcelable
  10. class MyClass(val external: @WriteWith<ExternalClassParceler>() ExternalClass)

2.8.3、Activity 传递序列化对象

  1. @Parcelize
  2. data class Model(val title: String, val amount: Int) : Parcelable
  3. // 传递
  4. val intent = Intent(this, DetailActivity::class.java)
  5. intent.putExtra(DetailActivity.EXTRA, model)
  6. startActivity(intent)
  7. // 获取
  8. val model: Model = intent.getParcelableExtra(EXTRA)

转载于:https://my.oschina.net/Agnes2017/blog/1802129

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/259895
推荐阅读
相关标签
  

闽ICP备14008679号