去年,哦不,前年五月的时候,谷歌宣布了Kotlin-First 这一重要概念,主张Kotlin 是 Android 开发者的首选语言。
消息发出去后,很多人心中都有了一个疑问:那我一个搞Android开发的,我现在是不是以后就只用学Kotlin 就可以了?
一般有这个疑问或者想法的人都是外行人或者说是入行不久的人。
今天在这里就分享一个行业大佬的看法吧,本文也可以当做移动开发新手手册。希望本文对大家有帮助。
来自公众号:承香墨影
本文内容:
新语言有新特性,开发者还保持 Java 的编程习惯去写 Kotlin,也不是不行,但是总感觉差点意思。
最近公众号「谷歌开发者」连载了一个《实用 Kotlin 构建 Android 应用 | Kotlin 迁移指南》的系列文章,就举例了一些 Kotlin 编码的小技巧。
既然是一种指南性质的文章,自然在「多而广」的基础上,有意去省略一些细节,同时举例的场景,可能还有一些不恰当的地方。
这里我就来补齐这些细节,今天聊聊利用 Kotlin 的方法默认参数的特性,完成类似 Java 的方法重载的效果。完全解析这个特性的使用方式和原理,以及在使用过程中的一个深坑。
在 Java 中,我们可以在同一个类中,定义多个同名的方法,只需要保证每个方法具有不同的参数类型或参数个数,这就是 Java 的方法重载。
class Hello {
public static void hello() {
System.out.println("Hello, world!");
}
public static void hello(String name) {
System.out.println("Hello, "+ name +"!");
}
public static void hello(String name, int age) {
if (age > 0) {
System.out.println("Hello, "+ name + "(" +age +")!");
} else {
System.out.println("Hello, "+ name +"!");
}
}
}
在这个例子中,我们定义了三个同名的 hello()
方法,具有不同的逻辑细节。
在 Kotlin 中,因为它支持在同一个方法里,通过 「?」标出可空参数,以及通过「=」给出参数的默认值。那这三个方法就可以在 Kotlin 中,被柔和成一个方法。
object HelloDemo{
fun hello(name: String = "world", age: Int = 0) {
if (age > 0) {
System.out.println("Hello, ${name}(${age})!");
} else {
System.out.println("Hello, ${name}!");
}
}
}
在 Kotlin 类中调用,和前面 Java 实现的效果是一致的。
HelloDemo.hello()
HelloDemo.hello("承香墨影")
HelloDemo.hello("承香墨影", 16)
但是这个通过 Kotlin 方法参数默认值的特性申明的方法,在 Java 类中使用时,就有些区别了。因为 HelloDemo 类被声明为 object,所以在 Java 中需要使用 INSTANCE
来调用它的方法。
HelloDemo.INSTANCE.hello("承香墨影",16);
Kotlin 中调用 hello()
方法很方便,可以选择性的忽略参数,但是在 Java 中使用,必须全量的显式的去做参数赋值。
这就是使用了参数默认值的方法申明时,分别在 Kotlin 和 Java 中的使用方式,接下来我们看看原理。
Kotlin 编写的代码,之所以可以在 Java 系的虚拟机中运行,主要是因为它在编译的过程中,会被编译成虚拟机可识别的 Java 字节码。所以我们通过两次转换的方式(Show Kotlin Bytecode + Decompile),就可以得到 Kotlin 生成的对应 Java 代码了。
public final void hello(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
if (age > 0) {
System.out.println("Hello, " + name + '(' + age + ")!");
} else {
System.out.println("Hello, " + name + '!');
}
}
// $FF: synthetic method
public static void hello$default(HelloDemo var0, String var1, int var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var1 = "world";
}
if ((var3 & 2) != 0) {
var2 = 0;
}
var0.hello(var1, var2);
}
在这里会生成一个 hello()
方法,同时还会有一个合成方法(synthetic method)hello$default
,用来处理默认参数的问题。在 Kotlin 中调用 hello()
方法,会在编译期间,有选择性的自动替换成 hello()
的合成方法去调用。
// Kotlin 调用
HelloDemo.hello()
HelloDemo.hello("承香墨影")
HelloDemo.hello("承香墨影", 16)
// 编译后的 Java 代码
HelloDemo.hello$default(HelloDemo.INSTANCE, (String)null, 0, 3, (Object)null);
HelloDemo.hello$default(HelloDemo.INSTANCE, "承香墨影", 0, 2, (Object)null);
HelloDemo.INSTANCE.hello("承香墨影", 16);
注意看示例的末尾,当使用 hello(name,age)
这个方法重载时,其实与 Java 中的调用,是一致的,这没什么好说的。
这就是 Kotlin 方法重载时,使用指定默认参数的方式,省去多个方法重载代码的原理。
理解原理后,发现它确实减少了我们编写的代码量,但是有没有场景,是我们就需要显式的存在这几个方法的重载的?自然是有的,例如自定义 View 时。
再回到前面提到的谷歌开发者的《实用 Kotlin 构建 Android 应用 | Kotlin 迁移指南》系列文章中,举的例子其实很不恰当。
它这里的例子中,使用了 View 这个词,并且重载的几个方法,都是 View 的构造方法,我们在自定义 View 时,经常会和这三个方法打交道。
但是谷歌工程师在这里举的例子,很容易让人误会,实际上你如果在自定义 View 时,这么写一定是会报错的。
例如我们自定义一个 DemoView,它继承自 EditView。
class DemoView(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : EditText(context, attrs, defStyleAttr) {
}
这个自定义的 DemoView,当使用在 XML 布局中时,虽然编译不会出错,但是运行时,你会得到一个 NoSuchMethodException。
Caused by: java.lang.NoSuchMethodException: [class android.content.Context, interface android.util.AttributeSet]
什么问题呢?
在 LayoutInflater 创建控件时,找不到 DemoView(Context, AttributeSet)
这个重载方法,所以就报错了。
这其实很好理解,在前面说到 Kotlin 在使用带默认值的方法的原理,其实 Kotlin 最终会在编译后,额外生成一个合成方法
,来处理方法的参数默认值的情况,它和 Java 的方法重载还不一样,用它生成的方法,确实不会存在多个方法的重载。
所以要明白,Kotlin 的方法指定默认参数与 Java 的方法重载,并不等价。只能说它们在某些场景下,特性是类似的。
那么回到这里的问题,在自定义 View 或者其他需要保留 Java 方法重载的场景下,怎么让 Kotlin 在编译时,真实的去生成对应的重载方法?
这里就需要用到 @JvmOverloads
了。
当 Kotlin 使用了默认值的方法,被增加了 @JvmOverloads
注解后,它的含义就是在编译时,保持并暴露出该方法的多个重载方法。
其实当我们自定义 View 时,AS 已经给了我们充分的提示,它会自动帮我们生成带@JvmOverloads
构造方法。
AS 帮我们补全的代码如下:
class DemoView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatEditText(context, attrs, defStyleAttr) {
}
再用「Kotlin Bytecode + Decompile」查看一下编译后的代码,来验证 @JvmOverloads
的效果。
@JvmOverloads
public DemoView(@NotNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
Intrinsics.checkParameterIsNotNull(context, "context");
super(context, attrs, defStyleAttr);
}
// $FF: synthetic method
public DemoView(Context var1, AttributeSet var2, int var3, int var4, DefaultConstructorMarker var5) {
if ((var4 & 2) != 0) {
var2 = (AttributeSet)null;
}
if ((var4 & 4) != 0) {
var3 = 0;
}
this(var1, var2, var3);
}
@JvmOverloads
public DemoView(@NotNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0, 4, (DefaultConstructorMarker)null);
}
@JvmOverloads
public DemoView(@NotNull Context context) {
this(context, (AttributeSet)null, 0, 6, (DefaultConstructorMarker)null);
}
可以看到,@JvmOverloads
生效后,会按照我们的预期生成对应的重载方法,同时保留合成方法,完成在 Kotlin 中使用时,使用默认参数的需求。
是不是以为到这里就完了?并不是,如果你在自定义 View 时,完全按照 AS 给你的提示生成代码,虽然程序不会崩溃了,但你会得到一些未知的错误。
在自定义 View 时,依赖 AS 的提示生成代码,会遇到一些未知的错误。例如在本文的例子中,我们想要实现一个 EditView 的子类,用 AS 提示生成了代码。
会出现什么问题呢?
在 EditView 的场景下,你会发现焦点没有了,点击之后软键盘也不会自动弹出。
那为什么会出现这种问题?
原因就在 AS 在自动生成的代码时,对参数默认值的处理。
当在自定义 View 时,通过 AS 生成重载方法时,它对参数默认值的处理规则是这样的。
遇到对象,默认值为 null。
遇到基础数据类型,默认值为基本数据类型的默认值。例如 Int 就是 0,Boolean 就是 false。
而在这里的场景下, defStyleAttr
这个参数的类型为 Int,所以默认值会被赋值为 0,但是它并不是我们需要的。
在 Android 中,当 View 通过 XML 文件来布局使用时,会调用两个参数的构造方法(Context context, AttributeSet attrs)
,而它内部会调用三个参数的构造方法,并传递一个默认的 defStyleAttr
,注意它并不是 0。
既然找到了问题,就很好解决了。我们看看自定义 View 的父类中,两个参数的构造方法如何实现的,将 defStyleArrt
当默认值传递进去就好了。
那我们先看看 AppCompatEditText
中的实现。
public AppCompatEditText(Context context,
AttributeSet attrs) {
this(context, attrs, R.attr.editTextStyle);
}
再修改 DemoView 中对 defStyleAttr
默认值的指定即可。
class DemoView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.editTextStyle
) : AppCompatEditText(context, attrs, defStyleAttr) {
}
到这里,自定义 View 中,使用默认参数的构造方法重载问题,也解决了。
在自定义 View 的场景下,当然也可以通过重写多个 constructor
方法来实现类似的效果,但是既然已经明白了它的原理,那就放心大胆的使用吧。
到这里就弄清楚 Kotlin 中,使用默认参数来减少方法重载代码的使用技巧和原理,以及注意事项了。
弄清楚原理以及需要注意的点,可以帮助我们更好的使用 Kotlin 的特性。我们最后再总结一下本文的知识点:
Kotlin 可以通过对一个方法的参数,通过指定默认值的方式,来完成类似 Java 中「方法重载」的效果。
若想保留 Java 的重载方法,可以使用 @JvmOverloads
注解标记,它会自动生成该方法的全部重载方法。
在自定义 View 时,需要注意指定参数 defStyleAttr
的默认值,而不应该是 0。
上面已经对Kotlin可能带来的一个深坑进行了解析,我想大家心中对一开始的问题也有了答案。
移动开发,特别是Android 开发,现在依然是以Java语言为主(Java语言依然是最通用的),现在的Android 开发需要学习的东西主要是以下内容 。
目前Android APP开发主流语言就是Java语言,Java语言最大的特性就是提高了软件的交互可能性,可以说安卓手机几乎所有应用程序都是利用Java语言来进行编写的。
知识要点:
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
随着互联网企业的不断发展,产品项目中的模块越来越多,用户体验要求也越来越高,想实现小步快跑、快速迭代的目的越来越难,插件化技术应用而生。如果没有插件化技术,美团、淘宝这些集成了大量“app”的应用,可能会有几个g那么大。
所以,当今的Android移动开发,不会热修复、插件化、组件化,80%以上的面试都过不了。
知识要点:
1、热修复设计
2、插件化框架设计
3、组件化框架设计
4、图片加载框架
5、网络访问框架设计
6、RXJava响应式编程框架设计
在不同层次的开发工程师手里,因为技术水平的参差不齐,即使很多手机在跑分软件性能非常高,打开应用依然存在卡顿现象。
另外,随着产品内容迭代,功能越来越复杂,UI页面也越来越丰富,也成为流畅运行的一种阻碍。综上所述,对APP进行性能优化已成为开发者该有的一种综合素质,也是开发者能够完成高质量应用程序作品的保证。
知识要点:
1、设计思想与代码质量优化
2、程序性能优化
启动速度与执行效率优化
布局检测与优化
内存优化
耗电优化
网络传输与数据储存优化
APK大小优化3、开发效率优化
分布式版本控制系统Git
自动化构建系统Gradle4、项目实战
启动速度
流畅度
抖音在APK包大小资源优化的实践
优酷响应式布局技术全解析
网络优化
手机淘宝双十一性能优化项目揭秘
高德APP全链路源码依赖分析
彻底干掉OOM的实战经验分享
微信Android终端内存优化实践
Android框架体系架构(高级UI+FrameWork源码) 这块知识是现今使用者最多的,我们称之Android2013~2016年的技术。
Android开发者也往往因为网上Copy代码习惯了而导致对这块经常“使用”的代码熟悉而又陌生:熟悉的是几乎天天在和它们打交道, 天天在复制这些代码 ;陌生的是虽然天天和这些代码打交道,但是并没有深入研究过这些代码的原理,代码深处的内涵。
知识要点:
1、高级UI晋升
2、Android内核组件
3、大型项目必备IPC
4、数据持久与序列化
5、Framework内核解析**
NDK(Native Development Kit缩写)一种基于原生程序接口的软件开发工具包,可以让您在 Android 应用中利用 C 和 C++ 代码的工具。通过此工具开发的程序直接在本地运行,而不是虚拟机。
在Android中,NDK是一系列工具的集合,主要用于扩展Android SDK。NDK提供了一系列的工具可以帮助开发者快速的开发C或C++的动态库,并能自动将so和Java应用一起打包成apk。
知识要点:
1、NDK开发之C/C++入门
2、JNI模块开发
3、Linux编程
4、底层图片处理
5、音视频开发
6、机器学习
2020 年无疑是 Flutter 技术如火如荼发展的一年。现在这门技术也依然非常有价值。
每一个移动开发者都在为 Flutter 带来的“快速开发、富有表现力和灵活的 UI、原生性能”的特色和理念而痴狂,从超级 App 到独立应用,从纯 Flutter 到混合栈,开发者们在不同的场景下乐此不疲的探索和应用着 Flutter 技术,也在面临着各种各样不同的挑战。
知识要点:
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter Dart语言系统入门**
……
微信小程序作为现在比较火的编程开发应用场景之一,深受市场的青睐,这让不少开发者眼馋不已。但是对于初学者来说,就完全摸不着头脑了,不知道微信小程序开发制作需要学习那些知识,有需要的朋友可以参考本篇。
本篇知识要点:
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战
这里大家可以去看这一篇文章:牛掰!阿里P7大佬爆肝半个月,把安卓源码解析编成了508页的PDF。
文中完整版资料已经整理成为PDF文档,感兴趣的朋友可以点击此处,快速获取!希望能帮助到大家!
祝大家工作顺利!预祝新年快乐!
Android 开发之路,你我共勉!2021年,一起牛掰!