记录一个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)  

继续阅读“记录一个LifeCycle 多线程使用导致的崩溃”

ButterKnife 替代计划

为什么要换掉 ButterKnift

  • 库中使用的是 R2R2 点击不能跳转到具体的布局,对于问题定位有很大的干扰
  • 不支持增量构建

对比一览

条目 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 多出来的功能

  • 布局中数据绑定
  • 能绑定 LiveDataViewModel 进行联动(生命周期监听)
  • 能直接扩展 View 的 XML 属性
  • 能直接绑定动作,例如 onClick,onLongClick 这些

记一次 Debug android 编译慢的过程

公司因为扩展业务,新建了一个 android 项目,但是这个安卓项目明明比我们的主项目代码量少那么多,但是编译时间却比我们主项目时间长非常多。让我每次编译的时候都思考人生。

项目基本情况

我们主项目是一个 80M 大小的App,dex 包都有 30M,更改一行代码的时间实际上只需要 30s 不到的时间,但是新项目 apk 大小都没有10M编译时间却要 一分多钟,有时候还需要 3分钟

调试方法

gradle 4.3 版本之后,gradle 提供了一个新的方法去扫描编译过程,就是
buildScan 功能,简单一点讲就是这个东西会把task运行过程很多东西记录下来,图形化展示这些数据,让你更好的发现问题所在。

继续阅读“记一次 Debug android 编译慢的过程”

基于 GooglePlayService 的短信验证码自动填写

问题描述

如何才能不申请读取短信的权限,却又能够拿到自己发送的短信验证吗?

解决方案

在Google Play Service V2 中,谷歌提供了这样的方法。具体流程如下

  1. 获取用户电话号码
  2. 客户端启动SMS检索器(SmsRetrieverClient)
  3. 客户端将电话号码发送到您的服务器,请求下发验证码
  4. 服务端生成特定格式的短信发送给用户
  5. 手机收到短信后 GooglePlayService 使用 BroadcaseReceiver 发送给你应用
  6. 你从短信中提取 code,填写到验证码上

继续阅读“基于 GooglePlayService 的短信验证码自动填写”

上传 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 aar 到 nexus 上”

提速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

关于Santa Tracker Project

  • 9 个模块,包括Wear
  • 500 多个Java文件
  • 1700 个XML 文件,3500张PNG
  • Multi-dex
  • 没有 annotation processors
  • APK大小接近60MB

这个项目可以在Google Github 帐号中找到

继续阅读“提速Android Gradle 构建”

Activity 启动模式

启动模式分为4种,分别为

  1. Standrad
  2. SingleTop
  3. SingleTask
  4. SingleIntance

Standrad

就是默认的模式,启动多少个就是多少个

SingleTop

SingleTop 需要这样理解,SingleOnTop,当栈顶是我的时候,就不再创建新的实例。
例如,ABCD,启动D,还是ABCD。如果是ABDC,启动D,最后就变成ABDCD。

SingleTask

SingleOnTask的意思,我在这个任务栈是唯一的。需要注意的是,这货启动是默认自带clearTop效果的,也就是会把在它之后的Activity都清楚掉。
例如,任务栈中有ABDC,启动D,将会变成ABD,C将会被自动推出。

SingleIntance

这个可以看做是SingleTask加强版,使用这个属性的Activity,将会被单独放在一个任务栈中,然后这个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 其实也很简单

  1. 升级Android Plugin到3.0.0-alpha1 或者以上
  2. 在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_USEElementType.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.