标签:Android
记录一个LifeCycle 多线程使用导致的崩溃
关键字
- lifecycle
- 多线程
- java.lang.IllegalArgumentException
- bug
- android
- androidx
问题描述
在调用 getLifecycle().addObserver()
的时候报出这样的错误
java.lang.IllegalArgumentException
at androidx.lifecycle.LifecycleRegistry.upEvent(SourceFile:279)
at androidx.lifecycle.LifecycleRegistry.forwardPass(SourceFile:293)
at androidx.lifecycle.LifecycleRegistry.sync(SourceFile:333)
at androidx.lifecycle.LifecycleRegistry.addObserver(SourceFile:189)
ButterKnife 替代计划
为什么要换掉 ButterKnift
- 库中使用的是
R2
,R2
点击不能跳转到具体的布局,对于问题定位有很大的干扰 - 不支持增量构建
对比一览
条目 | ViewBinding | DataBinding |
---|---|---|
定位 | 代替 findViewById | 作为数据到界面显示的桥梁 |
是否需要更改布局 | 不需要 | 需要在最外围加上<layout> (可以自动生成) |
能否进行数据绑定 | 不能 | 可以 |
能否和LiveData ,ViewModel ,LifeCycle 联动 |
不能 | 可以 |
ViewBinding 和 DataBinding
共同点
开启方法基本一致
都是在 gradle 配置开关
ViewBinding
android{
viewBinding {
enabled = true
}
}
DataBinding
android{
dataBinding {
enabled = true
}
}
对 View 操作基本一致
都是拿到 binding 对象,然后通过 binding
对象对 View
进行操作
private lateinit var binding: ResultProfileBinding
@Override
fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
setContentView(binding.root)
}
都是为了安全
- Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
- 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
不同点
定位不同
- ViewBinding 定位代替 findViewById
- DataBinding 定位是数据到界面展示的桥梁
可以直接看成 ViewBinding 是 DataBinding 的子集
对于 xml 要求不同
ViewBinding
不需要对 xml 进行更改DataBinding
需要在 xml 最外层 加上<layout>
这个根
DataBinding 多出来的功能
- 布局中数据绑定
- 能绑定
LiveData
和ViewModel
进行联动(生命周期监听) - 能直接扩展
View
的 XML 属性 - 能直接绑定动作,例如
onClick
,onLongClick
这些
记一次 Debug android 编译慢的过程
公司因为扩展业务,新建了一个 android 项目,但是这个安卓项目明明比我们的主项目代码量少那么多,但是编译时间却比我们主项目时间长非常多。让我每次编译的时候都思考人生。
项目基本情况
我们主项目是一个 80M 大小的App,dex 包都有 30M
,更改一行代码的时间实际上只需要 30s
不到的时间,但是新项目 apk 大小都没有10M
编译时间却要 一分多钟,有时候还需要 3分钟。
调试方法
在 gradle
4.3 版本之后,gradle
提供了一个新的方法去扫描编译过程,就是
buildScan
功能,简单一点讲就是这个东西会把task
运行过程很多东西记录下来,图形化展示这些数据,让你更好的发现问题所在。
基于 GooglePlayService 的短信验证码自动填写
问题描述
如何才能不申请读取短信的权限,却又能够拿到自己发送的短信验证吗?
解决方案
在Google Play Service V2 中,谷歌提供了这样的方法。具体流程如下
- 获取用户电话号码
- 客户端启动SMS检索器(SmsRetrieverClient)
- 客户端将电话号码发送到您的服务器,请求下发验证码
- 服务端生成特定格式的短信发送给用户
- 手机收到短信后 GooglePlayService 使用 BroadcaseReceiver 发送给你应用
- 你从短信中提取 code,填写到验证码上
上传 Android aar 到 nexus 上
在新版本的Gradle
中,提供了publish
这个操作,简化了整个上传的流程,详细 API在这个文档下,比如如果我需要上传 jar 到 maven上,只需要这样写
group = 'org.example'
version = '1.0'
publishing {
publications {
myLibrary(MavenPublication) {
from components.java
}
}
repositories {
maven {
name = 'myRepo'
url = "file://${buildDir}/repo"
}
}
}
不过这只是Java的, Android的压根没用,所以我又找到一个插件来适配安卓的
提速Android Gradle 构建
提速法则一览
手段 | 全量构建 | Java增量构建 | 资源增量构建 |
---|---|---|---|
升级 android gradle tools 到3.0 | -15s(-25%) | -10(-38%) | -2.5(-16%) |
避免使用遗留的Multidex | -5.5s(-12%) | -8(53%) | same |
debug 环境关闭multi-APK | -4.8s(-12%) | -0.5s(-6%) | -3s(-26%) |
设置包含最少的资源 | -6s(-17%) | -1.5s(-24%) | -2s(21%) |
关闭 png crunching | -9s(-33%) | same | same |
使用Instance Run | +7s(+37%) | -3s(-54%) | -3s(-42%) |
避免不注意的改变 | - | - | - |
不要使用动态版本号 | - | - | - |
注意分配 gradle 内存 | - | - | - |
开启 Gradle Caching | -7s(-25%) | same | +0.5s(+12%) |
使用implementation 或者 api 代替 compile |
- | - | - |
以上优化方案基于android gradle tools 3.0-alpha
- 9 个模块,包括Wear
- 500 多个Java文件
- 1700 个XML 文件,3500张PNG
- Multi-dex
- 没有 annotation processors
- APK大小接近60MB
这个项目可以在Google Github 帐号中找到
Activity 启动模式
启动模式分为4种,分别为
- Standrad
- SingleTop
- SingleTask
- SingleIntance
Standrad
就是默认的模式,启动多少个就是多少个
SingleTop
SingleTop 需要这样理解,SingleOnTop,当栈顶是我的时候,就不再创建新的实例。
例如,ABCD,启动D,还是ABCD。如果是ABDC,启动D,最后就变成ABDCD。
SingleTask
SingleOnTask的意思,我在这个任务栈是唯一的。需要注意的是,这货启动是默认自带clearTop效果的,也就是会把在它之后的Activity都清楚掉。
例如,任务栈中有ABDC,启动D,将会变成ABD,C将会被自动推出。
SingleIntance
这个可以看做是SingleTask加强版,使用这个属性的Activity,将会被单独放在一个任务栈中,然后这个Activity在进程中都是唯一个的
配置 Groovy 写Android JUnit
为什么使用Groovy写JUnit
Groovy
是动态语言,动态语言干测试其实非常好用。比如,我要创建一个多种类型的数组,只要这样写def array = [12,12,'1212']
这样我就很简单的创建了多种类型的数组了。如果是用java代码的话,你可以尝试一下需要多少行。groovy的优点不只是这些,详情请看Groovy的特性
Android中的配置方法
在Android中需要使用Groovy需要使用这个开源项目,里面有详细的配置说明。但是我按那个配置,老是编译不过,改了一下就可以了,就写下自己的办法吧。
配置buildScript
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-beta7'
//groovy插件依赖
classpath 'org.codehaus.groovy:groovy-android-gradle-plugin:1.2.0'
}
}
在需要使用的项目中,加入插件
apply plugin: 'groovyx.android'
添加groovy依赖
我这里只是使用Groovy 作为测试使用,所以我是用的是
testImplementation
,如果要在开发中使用Groovy,请改为implementation
就可以了
dependencies {
//使用官方推荐的反而不行
// testImplementation 'org.codehaus.groovy:groovy:2.4.11:grooid'
//我该为了java使用的Groovy就可以了
testImplementation 'org.codehaus.groovy:groovy-all:2.4.12'
testImplementation 'junit:junit:4.12'
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
exclude group: 'com.android.support', module: 'support-annotations'
})
}
开始开发
在test/groovy
文件夹中建立你的Groovy 脚本,开始开发吧
Android Studio 3.0 中支持的Java8 特性
集成方法
在Android Studio 3.0 中其实已经支持了Java8 的部分特性,其中最多人用的就是Lambda 表达式也在列。
在AS3.0中使用lambda 其实也很简单
- 升级Android Plugin到3.0.0-alpha1 或者以上
-
在build.gradle 中加入一下代码
android { ...
// Configure only for each module that uses Java 8
// language features (either in its source code or
// through dependencies).
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
就这样两部可以了。
如果你以前使用了jack或者me.tatarka.retrolambda,只需要移除相关的代码就可以了.
AS3.0 中java8 支持的特性和Api等级
Java 8 Language Feature | 最低兼容的API等级 |
---|---|
Lambda expressions | 任意API等级。但是,只有当lambda所捕获的所有值都是可序列化的时候才支持lambda序列化。 |
Method References | 任意API等级. |
Type Annotations | 任意API等级。但是,类型注释信息在编译时可用,但在运行时不可用。此外,TYPE 在API级别24及受以下,但不支持ElementType.TYPE_USE 或ElementType.TYPE_PARAMETER 。 |
Default and static interface methods | 任意API等级。 |
Repeating annotations | 任意API等级。 |
Java 8 Language API | 最低兼容的API等级 |
---|---|
java.lang.annotation.Repeatable |
API level 24 or higher. |
AnnotatedElement.getAnnotationsByType(Class) |
API level 24 or higher. |
java.util.stream |
API level 24 or higher. |
java.lang.FunctionalInterface |
API level 24 or higher. |
java.lang.reflect.Method.isDefault() |
API level 24 or higher. |
java.util.function |
API level 24 or higher. |