kotlin_基础_枚举和密封类(sealed class)

2023-11-05

转载自:https://blog.csdn.net/deng_hui_long/article/details/108173544

写这篇文章之前,做了很多调研,查阅了很多资料,文章也反复推敲打磨了很多遍,为什么我要去做这么多的调研工作,因为 Sealed Classes 概念很简单,但是想要写好它很难,由于目前我对 Sealed Classes 的理解也有限,所以做了很多工作。希望这篇文章的内容不会让小伙伴们失望,如果你对 Sealed Classes 有独到的理解,也欢迎在评论区和大家一起分享。

在上一篇文章 Google 推荐在项目中使用 Sealed 和 RemoteMediator 中介绍了如何使用 Sealed Classes 在 Flow 基础上对网络请求成功和失败进行处理,而这篇文章是对 Sealed Classes 更加深入的解析,结合函数式编程功能很强大,掌握并且灵活运用它,需要大量的实践。

通过这篇文章你将学习到以下内容:

  • Sealed Classes 原理分析?
  • 枚举和抽象类都有那些局限性?
  • 为什么枚举可以作为单例?枚举作为单例有那些优点?
  • 分别在什么情况下使用枚举和 Sealed Classes?
  • Sealed Classes 究竟是什么?
  • 为什么 Sealed Classes 用于表示受限制的类层次结构?
  • 为什么说 Sealed Classes 是枚举类的扩展?
  • Sealed Classes 的子类可以表示不同状态的实例,那么在项目中如何使用?
  • 禁止在 Sealed Classes 所定义的文件外使用, Kotlin 是如何做到的呢?

枚举和抽象类的局限性

在分析 Sealed Classes 之前,我们先来分析一下枚举和抽象类都有那些局限性,注意:这些局限性是相对于 Sealed Classes 而言的,但是相对于它们自身而言是优点,而 Sealed Classes 出现也正是为了解决这些问题。先来看一下枚举的局限性:

  • 限制枚举每个类型只允许有一个实例
  • 限制所有枚举常量使用相同的类型的值

限制枚举每个类型只允许有一个实例

enum class Color(val value: Int) {
    Red(1)
}

fun main(args: Array<String>) {
    val red1 = Color.Red
    val red2 = Color.Red
    println("${red1 == red2}") // true
}

最后输出结果

red1 == red2 : true

正如你看到的,我们定义了一个单元素的枚举类型,无论 Color.Red 有多少个对象,最终他们的实例都是一个,每个枚举常量仅作为一个实例存在,而一个密封类的子类可以有多个包含状态的实例,这既是枚举的局限性也是枚举的优点

枚举常量作为一个实例存在的优点: 枚举不仅能防止多次实例化,而且还可以防止反序列化,还能避免多线程同步问题,所以它也被列为实现单例方法之一。简单汇总一下。
在这里插入图片描述

《Effective Java》 一书的作者 Josh Bloch 建议我们使用枚举作为单例,虽然使用枚举实现单例的方法还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton 的最佳方法。

我们来看一下如何用枚举实现一个单例(与 Java 的实现方式相同),这里不会深究其原理,因为这不是本文的重点内容,小伙伴们可以从掘金搜索,有很多分析这方面原理的文章。

interface ISingleton {
    fun doSomething()
}

enum class Singleton : ISingleton {
    INSTANCE {
        override fun doSomething() {
            // to do
        }
    };

    fun getInstance(): Singleton = Singleton.INSTANCE
}

但是在实际项目中使用枚举作为单例的很少,我看了很多开源项目,将枚举作为单例的场景少之有少,很大部分原因是因为使用枚举的时候非常不方便。

我这有个建议如果涉及反序列化创建对象的时候,建议使用枚举,因为 Java 规定,枚举的序列化和反序列化是有特殊定制的,因此禁用编译器使用 writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve 等方法。

限制所有枚举常量使用相同的类型的值

限制所有枚举常量使用相同的类型的值,也就是说每个枚举常量类型的值是相同的,我们还是用刚才的例子做个演示。

enum class Color(val value: Int) {
    Red(1),
    Green(2),
    Blue(3);
}

正如你所见,我们在枚举 Color 中定义了三个常量 Red 、Green 、Blue,但是它们只能使用 Int 类型的值,不能使用其他类型的值,如果使用其它类型的值会怎么样?如下所示:

编译器会告诉你只接受 Int 类型的值,无法更改它的类型,也就是说你无法为枚举类型,添加额外的信息。

抽象类的局限性

对于一个抽象类我们可以用一些子类去继承它,但是子类不是固定的,它可以随意扩展,同时也失去枚举常量的受限性。

Sealed Classes 包含了抽象类和枚举的优势:抽象类表示的灵活性和枚举常量的受限性

到这里可能会有一个疑问,如果 Sealed Classes 没有枚举和抽象类的局限性,那么它能在实际项目中给我们带来哪些好处呢?在了解它能带来哪些好处之前,我们先来看看官方对 Sealed Classes 的解释。

Sealed Classes 是什么?

我们先来看一下官方对 Sealed Classes 的解释

我们将上面这段话,简单的总结一下:

  • Sealed Classes 用于表示受限制的类层次结构
  • 从某种意义上说,Sealed Classes 是枚举类的扩展
  • 枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例

那上面这三段话分别是什么意思呢?接下来我们围绕这三个方面来分析。

Sealed Classes 用于表示受限制的类层次结构

Sealed Classes 用于表示受限制的类层次结构,其实这句话可以拆成两句话来理解。

  • Sealed Classes 用于表示层级关系: 子类可以是任意的类, 数据类、Kotlin 对象、普通的类,甚至也可以是另一个 Sealed
  • Sealed Classes 受限制: 必须在同一文件中,或者在 Sealed Classes 类的内部中使用,在Kotlin 1.1 之前,规则更加严格,子类只能在 Sealed Classes 类的内部中使用

Sealed Classes 的用法也非常的简单,我们来看一下如何使用 Sealed Classes。

sealed class Color {
    class Red(val value: Int) : Color()
    class Green(val value: Int) : Color()
    class Blue(val name: String) : Color()
}

fun isInstance(color: Color) {
    when (color) {
        is Color.Red -> TODO()
        is Color.Green -> TODO()
        is Color.Blue -> TODO()
    }
}

在这里推荐大家一个快捷键 Mac/Win/Linux:Alt + Enter 可以补全 when 语句下的所有分支,效果如下所示:

更多 AndroidStudio 快捷键,可以看之前的两篇文章

Sealed Classes 是枚举类的扩展

从某种意义上说,Sealed Classes 是枚举类的扩展,其实 Sealed Classes 和枚举很像,我们先来看一个例子。

sealed-enu

正如你所看到的,在 Sealed Classes 内部中,使用 object 声明时,我们可以重用它们,不需要每次创建一个新实例,当这样使用时候,它看起来和枚举非常相似。

注意:实际上很少有人会这么使用,而且也不建议这么用,因为在这种情况枚举比 Sealed Classes 更适合

在什么情况下使用枚举

如果你不需要多次实例化,也不需要不提供特殊行为,或者也不需要添加额外的信息,仅作为单个实例存在,这个时候使用枚举更加合适。

我们来看一下 Paging3 中是如何使用枚举的,一起来看一下 androidx.paging.LoadType 这个类的源码。

enum class LoadType {
    REFRESH,
    PREPEND,
    APPEND
}

枚举常量

作用

refresh

在初始化刷新的使用

append

在加载更多的时候使用

prepend

在当前列表头部添加数据的时候使用

它们不需要多次实例化,也不需要添加任何额外的信息,仅仅表示某种状态,而且它在很多地方都会用到比如 RemoteMediator、PagingSource 等等,想了解更多关于 Paging3 原理和实战案例可以看之前写的几篇文章。

4.Sealed Classes 的子类可以表示不同状态的实例

与枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例,我们来看个例子可能更容易理解这句话。

这里我们延用之前在 Google 推荐在项目中使用 sealed 和 RemoteMediator 这篇文章中用到的例子,在请求网络的时候需要对成功或者失败进行处理,我们来看一下用 Sealed Classes 如何进行封装。

sealed class PokemonResult<out T> {
    data class Success<out T>(val value: T) : PokemonResult<T>()

    data class Failure(val throwable: Throwable?) : PokemonResult<Nothing>()
}

这里只贴出来部分代码,核心实现可以查看项目 PokemonGo
GitHub 地址:https://github.com/hi-dhl/PokemonGo
代码路径:PokemonGo/app/…/com/hi/dhl/pokemon/data/remote/PokemonResult.kt

一起来看一下如何使用

when (result) {
    is PokemonResult.Failure -> {
        // 进行失败提示
    }
    is PokemonResult.Success -> {
        // 进行成功处理
    }
}

我们在来看另外一个例子,在一个列表中可能会有不同类型的数据,比如图片、文本等等,那么用 Sealed Classes 如何表示。

sealed class ListItem {
    class Text(val title: String, val content: String) : ListItem()
    class Image(val url: String) : ListItem()
}

这是两个比较常见的例子,当然 Sealed Classes 强大不止于此,还有更多场景,等着一起来挖掘。

我们来看一下大神 Antonio Leiva 在这篇文章 Sealed classes in Kotlin: enums with super-powers 分享的一个比较有趣的例子,对 View 进行的一系列操作可以封装在 Sealed Classes 中,我们来看一下会有什么样的效果。

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
    class TranslateX(val px: Float): UiOp()
    class TranslateY(val px: Float): UiOp()
}

fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
    is UiOp.TranslateX -> view.translationX = op.px
    is UiOp.TranslateY -> view.translationY = op.px
}

在 Sealed Classes 类中,我们定义了一系列 View 的操作 ShowHideTranslateXTranslateY ,现在我们创建一个类,将这些对视图的操作整合在一起。

class Ui(val uiOps: List = emptyList()) {
    operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}

在 Ui 这个类中声明了一个 List 存储了所有的操作,并重写了 plus 操作符,关于 plus 操作符可以看之前的文章 为数不多的人知道的 Kotlin 技巧以及 原理解析,通过 plus 操作符将这些对视图的操作拼接在一起,这样不仅可以提高代码的可读性,而且使用起来也非常的方便,都定义好之后,我们来看一下如何使用这个类。

val ui = Ui() +
        UiOp.Show +
        UiOp.TranslateX(20f) +
        UiOp.TranslateY(40f) +
        UiOp.Hide
        
run(view, ui)

定义了一系列操作之后,然后通过 run 方法来执行这些操作,来看一下 run 方法的实现。

fun run(view: View, ui: Ui) {
    ui.uiOps.forEach { execute(view, it) }
}

代码很简单,这里就不多做解释了,在 kotlin 中函数可以作为参数传递,可以将 run 方法传递给另一个函数或者一个类,并且这些操作完全可互换的,将它们结合在一起功能将非常强大。

Sealed Classes 强大不止于此,还有很多很多非常实用的场景,现在我对 Sealed Classes 的理解也非常有限,还不够灵活的使用它,我相信在更多项目,更多的场景,会看到更多实用的一些技巧。

Sealed Classes 原理

在这里我们还是使用上文中用到的例子,来分析 Sealed Classes 原理。

sealed class Color {
    object Red : Color()
    object Green : Color()
    object Blue : Color()
}

一起来分析一下反编译后的 Java 代码都做了什么。PS:Tools → Kotlin → Show Kotlin Bytecode

... // 省略部分代码

@Metadata(
   mv = {1, 1, 13},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0004\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0000\b6\u0018\u00002\u00020\u0001:\u0003\u0003\u0004\u0005B\u0007\b\u0002¢\u0006\u0002\u0010\u0002\u0082\u0001\u0003\u0006\u0007\b¨\u0006\t"},
   d2 = {"Lcom/hidhl/leetcode/test/saledvsemun/Color;", "", "()V", "Blue", "Green", "Red", "Lcom/hidhl/leetcode/test/saledvsemun/Color$Red;", "Lcom/hidhl/leetcode/test/saledvsemun/Color$Green;", "Lcom/hidhl/leetcode/test/saledvsemun/Color$Blue;", "Java-kotlin"}
)
public abstract class Color {
   private Color() {
   }

   public Color(DefaultConstructorMarker $constructor_marker) {
      this();
   }
}
... // 省略部分代码

@Metadata 这个注解会出现在 Kotlin 编译器生成的任何类文件中,可以通过反射的方式获取 @Metadata 信息。参数名称都非常短,可以帮助减少 class 文件的大小。

@Metadata 存储了 Kotlin 主要的语法信息例如扩展函数、typealias 等等,这些信息都是由 kotlinc 编译器,并以注解的形式存放在 Java 的字节码中的,如果元数据被丢弃掉,运行在 JVM 上会抛出异常,那么如何才能确定它们之间的对应关系呢,其实就是通过 @Metadata 这个注解提供的信息。

正因为元数据不能被丢掉,R8 带了新的优化,将元数据信息记录在 R8 的内部数据结构中,当 R8 完成对第三库或者应用程序的优化和收缩时,它会为所有 Kotlin 类合成新的正确的 Kotlin 元数据,其目的就是为了减少应用程序的大小,目前我也在研究中,日后会分享。

而在本例中 @Metadata 保存了一个子类的列表,编译器在使用的时候会用到这些信息。正如你看到的 Sealed class 被编译成了 abstract class,它本身是不能被实例化,只能用它的子类实例化对象。

抽象类 Color 默认的构造方法被私有化了,所以在 Kotlin 1.1 之前,子类必须嵌套在 Sealed Classes 类中,后来放宽了要求,禁止在 Sealed Classes 所定义的文件外使用, Kotlin 是如何做到的呢?如果我们在 Sealed Classes 所定义的文件外使用会怎么样?

正如你所看到,会导致编译错误,那么为什么 Sealed Classes 可以在同文件内使用呢?来看一下反编译后的代码。

// sealed
sealed class Color
// 同文件中使用 sealed class
class Red : Color()
    
    
// 以下是反编译代码 
  
... // 省略部分代码  
public final class Red extends Color {
   public Red() {
      super((DefaultConstructorMarker)null);
   }
}

... // 省略部分代码    
public abstract class Color {
   private Color() {
   }

   // $FF: synthetic method
   public Color(DefaultConstructorMarker $constructor_marker) {
      this();
   }
}
... // 省略部分代码

可以看到 Red class 被编译成了 final class, Sealed class 被编译成了 abstract class,同时编译器生成了一个 公有 的构造方法,其他的类无法直接调用,只有 Kotlin 编译器可以使用,Red class 被编译成 final class,在其构造方法内调用了 Color class 公有 的构造方法,而这些都是 Kotlin 编译器帮我们做的。

  • 构造函数私有化,限制了子类必须嵌套在 Sealed Classes 类中
  • 编译器生成了一个 公有 的构造方法,在子类的构造方法中调用了父类 公有 的构造方法,而这些都是 Kotlin 编译器帮我们做的

总结

枚举的局限性

  • 限制枚举每个类型只允许有一个实例
  • 限制所有枚举常量使用相同的类型的值

抽象类的局限性

对于一个抽象类我们可以用一些子类去继承它,但是子类不是固定的,它可以随意扩展,同时也失去枚举常量受限性。

枚举作为单例的优点

是否只有一个实例

是否反序列化

是否是线程安全

是否是懒加载

Sealed Classes 是什么?

Sealed 是一个 abstract 类,它本身是不能被实例化,只能用它的子类实例化对象。Sealed 的构造方法私有化,禁止在 Sealed 所定义的文件外使用。

  • Sealed Classes 用于表示受限制的类层次结构
  • 从某种意义上说,Sealed Classes 是枚举类的扩展
  • 枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例。

在什么情况下使用枚举或者 Sealed?

  • 如果涉及反序列化创建对象的时候,建议使用枚举,因为 Java 规定,枚举的序列化和反序列化是有特殊定制的,因此禁用编译器使用 writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve 等方法。
  • 如果你不需要多次实例化,也不需要不提供特殊行为,或者也不需要添加额外的信息,仅作为单个实例存在,这个时候使用枚举更加合适。
  • 其他情况下使用 Sealed Classes,在一定程度上可以使用 Sealed Classes 代替枚举

自动补全 when 语句下的所有分支

推荐给大家一个快捷键 Mac/Win/Linux:Alt + Enter 可以补全 when 语句下的所有分支,效果如下所示:

参考文献

正在建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,可以前去查看:AndroidX-Jetpack-Practice, 如果这个仓库对你有帮助,请在仓库右上角帮我点个赞,后面我会陆续完成更多 Jetpack 新成员的项目实践

结语

致力于分享一系列 Android 系统源码、逆向分析、算法、翻译、Jetpack 源码相关的文章,正在努力写出更好的文章,如果这篇文章对你有帮助给个 star,文章中有什么没有写明白的地方,或者有什么更好的建议欢迎留言,欢迎一起来学习,在技术的道路上一起前进。

Android10-Source-Analysis

正在写一系列的 Android 10 源码分析的文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,如果你同我一样喜欢研究 Android 源码,可以关注我 GitHub 上的 Android10-Source-Analysis

Leetcode-Solutions-with-Java-And-Kotlin

由于 LeetCode 的题库庞大,每个分类都能筛选出数百道题,由于每个人的精力有限,不可能刷完所有题目,因此我按照经典类型题目去分类、和题目的难易程度去排序。

  • 数据结构: 数组、栈、队列、字符串、链表、树……
  • 算法: 查找算法、搜索算法、位运算、排序、数学、……

每道题目都会用 Java 和 kotlin 去实现,并且每道题目都有解题思路,如果你同我一样喜欢算法、LeetCode,可以关注我 GitHub 上的 LeetCode 题解:Leetcode-Solutions-with-Java-And-Kotlin

Technical-Article-Translation

目前正在整理和翻译一系列精选国外的技术文章,不仅仅是翻译,很多优秀的英文技术文章提供了很好思路和方法,每篇文章都会有译者思考部分,对原文的更加深入的解读,可以关注我 GitHub 上的 Technical-Article-Translation

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

kotlin_基础_枚举和密封类(sealed class) 的相关文章

  • 使用 Android 前台服务为 MediaPlayer 创建通知

    问题就在这里 我目前正在开发一个应用程序 该应用程序必须提供 A 广播播放器 来自 URL 的 AAC 直播 还有一个播客播放器 来自 URL 的 MP3 流 该应用程序必须能够在后台运行 Android 服务 并通过以下方式向用户公开持续
  • 从 Throwable 获取错误代码 - Android

    我怎样才能从错误代码可投掷 https developer android com reference java lang Throwable html public void onFailure Throwable exception 我
  • 如何从 SQLite 获取记录总数

    我正在尝试从 Sqlite DB 获取行的总数 以下是我想要做的代码片段 我不知道我在这里做错了什么 public static int getTotalCount Context context Cursor c null try c g
  • Android libgdx 首选项丢失

    我在 Libgdx 引擎中创建了 Android 游戏 一段时间后 我注意到在某些应用程序杀手中杀死该应用程序后 或者如果我在 Android 设置中执行 强制关闭 操作 我保存到首选项中的游戏选项就会丢失 有办法防止这种情况吗 我从来没有
  • 如何将 Java 赋值表达式转换为 Kotlin

    java中的一些东西就像 int a 1 b 2 c 1 if a b c System out print true 现在它应该转换为 kotlin 就像 var a Int 1 var b Int 2 var c Int 1 if a
  • KitKat(及更低版本)设备上的 Android Material Design

    我将在我们学校开发一个 Android 应用程序作为一个项目 我想使用 Google 的新 Material Design 但我知道它仅适用于 Android L 设备 Jack Underwood 最近发布了名为 Today Calend
  • 设置从 Facebook 登录获取用户电子邮件 ID 的权限

    我在用着Facebook 3 0 SDK对于安卓 我必须实施Facebook登录 我正在访问用户的基本信息 例如姓名 用户 ID 但我也想访问用户的电子邮件 我浏览了很多博客和论坛 但不知道该怎么做 我正在使用我自己的 android 按钮
  • 在 Google Analytics 中跟踪应用程序版本

    我正在使用谷歌分析模块 https marketplace appcelerator com apps 5081 2014113336 https marketplace appcelerator com apps 5081 2014113
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • 如何在 sqlite 中将 2 列合并为新列

    我有一个包含 3 列的表 我必须将 2 列中的值按降序排列到一列中 A B C z 1 2 f 5 7 s 9 5 使用此示例 输出会将 B 列和 C 列中的值放入其中 如下所示 A B s 9 f 7 f 5 s 5 z 2 z 1 我当
  • 请求位置更新参数

    这就是 requestLocationUpdates 的样子 我使用它的方式 requestLocationUpdates String provider long minTime float minDistance LocationLis
  • 如何在C(Linux)中的while循环中准确地睡眠?

    在 C 代码 Linux 操作系统 中 我需要在 while 循环内准确地休眠 比如说 10000 微秒 1000 次 我尝试过usleep nanosleep select pselect和其他一些方法 但没有成功 一旦大约 50 次 它
  • 使用 Matrix.setPolyToPoly 选择位图上具有 4 个点的区域

    我正在 Android 上使用位图 在使用 4 个点选择位图上的区域时遇到问题 并非所有 4 点组都适合我 在某些情况下 结果只是一个空白位图 而不是裁剪后的位图 如图所示 并且 logcat 中没有任何错误 甚至是内存错误 这是我用来进行
  • Android 设备上的静默安装

    我已经接受了一段时间了 在 Android 上静默安装应用程序是不可能的 也就是说 让程序安装捆绑为 APK 的应用程序 而不提供标准操作系统安装提示并完成应用程序安装程序活动 但现在我已经拿到了 Appbrain 快速网络安装程序的副本
  • 当手机旋转(方向改变)时如何最好地重新创建标记/折线

    背景 开发一个使用 Android Google Map v2 的本机 Android 应用程序 使用android support v4 app FragmentActivity 在 Android v2 2 上运行 客观的 在更改手机方
  • Android - 将 ImageView 保存到具有全分辨率图像的文件

    我将图像放入 ImageView 中 并实现了多点触控来调整 ImageView 中的图像大小和移动图像 现在我需要将调整大小的图像保存到图像文件中 我已经尝试过 getDrawingCache 但该图像具有 ImageView 的大小 我
  • SharedFlow 和 StateFlow 的主要区别

    两者有什么区别共享流 and 状态流 以及如何使用这些MVI建筑学 使用简单更好吗Flow或者这些作为状态和事件 Flow 是冷的 意味着它仅在收集数据时才发出数据 另外Flow不能保存数据 可以把它看成是水在里面流动的管道 Flow中的数
  • 使用 Espresso 检查 EditText 的字体大小、高度和宽度

    如何使用 Espresso 检查 EditText 的字体大小 高度和宽度 目前要分割我使用的文本 onView withId R id editText1 perform clearText typeText Amr 并阅读文本 onVi
  • 在webview android中加载本地html文件

    我正在尝试在 android 的 webview 中加载 html 文件的内容 但是 它给了我 网页不可用错误 如果我尝试使用谷歌或雅虎等网站 它们就会起作用 html文件位于src gt main gt assests gt index
  • 无法将 admob 与 firebase iOS/Android 项目链接

    我有两个帐户 A 和 B A 是在 Firebase 上托管 iOS Android unity 手机游戏的主帐户 B 用于将 admob 集成到 iOS Android 手机游戏中 我在尝试将 admob 分析链接到 Firebase 项

随机推荐

  • 树状数组(二叉索引树)

    目录 一 树状数组 1 树状数组 2 区间更新单点查询 3 离散化 二 模板代码 三 OJ实战 CodeForces 706B Interesting drink CSU 1770 按钮控制彩灯实验 HDU 1166 敌兵布阵 POJ 23
  • 连接数据库报错com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure的解决方法

    控制台报错 Caused by com mysql cj exceptions CJCommunicationsException Communications link failure com mysql cj jdbc exceptio
  • Dependency-check

    文章目录 前言 工具简介 工具原理 原理 检测过程 NVD CVSS 工具安装 工具地址 环境依赖 工具安装 Jenkins Plugin Command Line On nix On Windows On Mac rec Maven Pl
  • Linux下的man指令

    今天做笔试试卷的时候 有一道选择题是问Linux man指令中查找系统调用的指令是哪一个 当场就不知道选什么了 平常也很少用man指令 都是想要查资料直接上csdn的 这里重新回顾一下Linux下的man指令 man指令后面都可以指定加一个
  • mac系统 拉取vue项目启动时报错:: Permission denied

    Mac 系统运行 vue 启动项目时报错 Permission denied 的解决方式 1 从github拉取的vue项目启动时npm run dev报错 自己试了很多命令 主要还是细心一点对比查看自己目录就好了 解决方式 2 这是因为没
  • React中useRef()和createRef()的使用

    useRef import React useState useRef createRef useEffect from react const Test gt const index setIndex useState 1 const c
  • golang常用库viper

    1 viper的介绍 viper是go一个配置解决方案的库 支持各种配置文件 如JSON TOML YAML HCL envfile和Java属性配置文件 支持监听文件变化以及重新读取配置 支持从环境变量读取配置 支持从远程配置系统 etc
  • 罗定中学2021年高考成绩查询,2020年罗定市各中学高考喜报!罗定中学、廷锴纪念、罗定实验均创历史...

    原标题 2020年罗定市各中学高考喜报 罗定中学 廷锴纪念 罗定实验均创历史 罗定市2020年高考情况 罗定中学 广东省罗定中学 始创于1911年4月11日 由当时的罗定县公立中学和阖县中学合并而成 1912年定名为省立罗定中学校 1926
  • 探秘MySQL底层架构:设计与实现流程

    前言 Mysql 作为一款优秀而广泛使用的数据库管理系统 对于众多Java工程师来说 几乎是日常开发中必不可少的一环 无论是存储海量数据 还是高效地检索和管理数据 Mysql都扮演着重要的角色 然而 除了使用Mysql进行日常开发之外 我们
  • Matlab Excel数据转成面板数据格式

    Stata总是整不明白而且如果后续还想加新的数据列 还得单独用Stata转换 再合并挺麻烦的 于是希望用Matlab直接转成面板数据 然后直接复制粘贴 就方便得多 函数无参数则默认选中的文件中所有的工作簿都转换 有参数的话 第一参数是工作簿
  • git学习------>如何汉化GitLab?

    在上一篇博客中 已经正常安装好了GitLab 然而全部界面都是纯英文的 为了照顾整个团队的英文水平 因此这篇博客的目的是将纯英文的GitLab进行汉化 纯英文界面 第一步 确认GitLab版本号 运行如下命令 查看GitLab版本号 cat
  • sourcetree安装遇到的各种坑

    安装 SourceTree 时 需要使用atlassian授权 多数会卡到这一步 网上给出的办法跳过 atlassian账号 授权方法 安装之后 转到用户本地文件夹下的 SourceTree 目录 没有则新建 LocalAppData At
  • 获得ListView中所点击的数据

    最近写课设 被获取ListView中所点击的数据 必将他传给下一个Actyvity给烦到了 因为要写的与数据库有关联 且要获得数据跳来跳去的 所以写下这篇作为笔记 部分代码 获取数据库的数据并在列表中显示出来 DatabaseHelper
  • ACmix:卷积与self-Attention的融合

    先附代码和文章 可以先结合来看 代码 GitHub LeapLabTHU ACmix Official repository of ACmix CVPR2022 文章 2111 14556 On the Integration of Sel
  • simulink中模块库的建立和维护

    一 模块库的建立与修改 模块库的建立与模型model的建立类似 都是在File菜单栏中 只不过要选择Library而非Model 如下图 然后再该Library中添加了两个模块 并存为名为Interpolation mdl文件 如下图 注意
  • Linux下 C语言实现消息队列

    消息队列 Unix的通信机制之一 可以理解为是一个存放消息 数据 容器 将消息写入消息队列 然后再从消息队列中取消息 一般来说是先进先出的顺序 可以解决两个进程的读写速度不同 处理数据速度不同 系统耦合等问题 而且消息队列里的消息哪怕进程崩
  • spring boot ValidationAutoConfiguration自动注入MethodValidationPostProcessor类导致bean提前被初始化

    文章目录 前言 1 什么是beanPostProcess 2 看下BeanPostProcessorChecker这个报错的地方 3 排查导致这么bean提前初始化的原因 3 1 查看registerBeanPostProcessors 4
  • shell 进入mysql redis hive等tips

    1 src redis cli h 192 168 1 81 p 6379 a password 2 进入hive client前设置队列权限 hive hiveconf mapreduce job queuename root jiaog
  • 今天学习到了robot.txt

    robot txt 搜索引擎通过一种程序robot 又称spider 自动访问互联网上的网页并获取网页信息 您可以在您的网站中创建一个纯文本文件robots txt 在这个文件中声明该网站中不想被robot访问的部分 这样 该网站的部分或全
  • kotlin_基础_枚举和密封类(sealed class)

    转载自 https blog csdn net deng hui long article details 108173544 写这篇文章之前 做了很多调研 查阅了很多资料 文章也反复推敲打磨了很多遍 为什么我要去做这么多的调研工作 因为