[Gradle] 如何在android项目中对纯Java module使用release/debug build并启用proguard

如何在android项目中对纯Java module使用release/debug build并启用proguard?这里提供一种方法,尽管不是很“好看”,但是可用。

假设app模块依赖lib模块,lib模块是一个纯Java模块(apply plugin: 'java')。整个过程可以分成两步。

引入Proguard

在lib模块的build.gradle里添加以下代码

这里做了几件事情:

  1. 定义proguard task,令jar这个java task依赖于它。即proguard task运行完毕后才运行jar。
  2. 使用文件替换的方式,在jar运行前替换掉class文件。
  3. 通过观察gradle console的输出,得知app在编译时会运行jar task。经过1.2.两步我们可以在正常编译
    流程中插入proguard处理后的class文件。

接下来看怎么样根据debug和release compile关闭或开启proguard

引入debug/release

在lib模块的build.gradle中加入如下代码:

然后在app的build.gradl中加入如下代码:

这段代码做了两个事情:

  1. 监听了task的添加流程,在’preReleaseBuild’和’preDebugBuild’的execute阶段添加了代码逻辑。
  2. 在’preReleaseBuild’和’preDebugBuild’执行完后,执行lib模块中的onRelease或者onDebug方法。

综上,实现了在android项目中对纯Java module使用release/debug build并启用proguard。

注:本文默认读者具有gradle基础知识。
注:附Java Plugin依赖图:
https://i1.wp.com/docs.gradle.org/3.3/userguide/img/javaPluginTasks.png?w=840&ssl=1

[Java] 不依赖Android环境的Java Handler

最近项目中遇到将android project转成java project的需求,要把项目中android相关的部分去掉。其中最纠结的就是如何替换handler。
handler多用于指定线程(Looper)上执行回调,还可以当成一个消息队列使用。通过sendMessageDelayed可以做到事件的延时执行、条件执行和去抖(debouncing)。
于是,我利用Java里面的ScheduledExecutorService写了一个简单的Handler,接口与原Handler基本一致,代码如下所示:

Message类:

Looper类:

Handler类:

使用方法如下所示:

[Android] 如何在 Activity 或者 Fragment 的生命周期结束时停止订阅 Observable

NavUtil

利用 NavUtil,你可以在 Activity 或 Fragment 的生命周期发生变化时,停止订阅你的 Observable。

Usage

在Activity 中,为你的 Observable 应用 compose 操作符,如下所示:

在 Fragment 中的用法如下所示:

目前支持的生命周期事件如下所示:

Gradle

Download

[Android] PermissionUtil

Usage

Gradle

Download

[Android] 在xml中“直接”使用px的小技巧和工具

注:这个idea出自知乎某个回答,但是来源找不到了。。。工具是自己写的。

在项目开发中,UI给过来的设计图上的尺寸标识,一般以px(像素)为单位。Android工程师需根据设计图的尺寸将px换成dp,填到layout.xml中。

有了这个工具,可以很方便地实现这一点。

首先,确认设计图属于下列哪种规格:

然后安装python,运行一个脚本:

然后在控制台中输入:

可得到:

将以上复制到你项目的dimens.xml中,以后可以直接按照设计图的px值,在layout.xml中写@dimen/_**px了。

[Android] PacketParser 二进制协议转换工具

这个工具通过自动生成解析类,实现了字节数组和对象之间的转换。

1. 0.40更新

版本0.40发布,更新内容如下所示:

  1. 支持继承
  2. 支持对象作为字段
  3. 支持条件解析字段
  4. 支持忽略字段
  5. 支持对象List

使用0.40版本,你可以做到:

以TLV格式的数据为例,首先定义一个基类:

再定义一个普通的TLV类:

对于任意一个TLV协议单元,可以定义一个类继承TLVHeaderObject:

留意代码中注释标注出的新特性。

编译项目后,可以通过如下测试:

2. 介绍

2.1. 使用@ParsePacket注解标注实体类

2.2. 框架自动生成解析类 <类名>PacketParser

注:需Builder一次项目,PacketParser才会生成。

2.3. 使用方法

Gradle

packetparser-compiler Download
packetparser-annotation Download

[Android] Dagger2 入门 2

上一篇文章介绍了Dagger2的基本用法,这篇文章主要说一下Dagger2中@Scope的用法和原理。

上一篇文章中提到:

如上面例子所示,如果要求D对象为单例,可以通过@Singleton注解来实现。首先我们需要在依赖图中声明对象是单例的:

DComponent接口也需要声明:

如此,当我们注入D对象时,可保证每次注入的是同一个D对象:

在我们看来,只是多加了一个注解而已,便实现了单例模式。要知道其原理,要从Dagger2生成的源码入手。

Dagger2生成的源码

以如下例子为例:

  1. 定义类:

  2. 定义Module

  3. 定义Component接口:

  4. 依赖注入:

    public class Main {

    }

编译工程,Dagger2在项目路径下生成了如下文件:

(利用这个工具生成了文件结构图)

注意,生成的文件在关联的类相同路径下。如DaggerABCComponent类生成在ABCComponent路径下。

我们先来看看实际接触到的DaggerABCComponent类:

来看几个关键点:

  1. DaggerABCComponent继承于ABCComponent。所以我们可以直接调用ABCComponent的方法。

  2. DaggerABCComponent需要Builder来进行初始化。Builder的作用是提供对象的module。

  3. 对象通过Provider从依赖图中取出。Provider由Factory生成时会有类似依赖注入的操作。

  4. 通过MembersInjector进行依赖注入。

这几个关键类的关系可用下图表示:

其中最最关键的是Factory和Provider。以B类为例,从依赖图中取出B对象,需要经过如下代码:

其中ABCModule_ProvideBFactory的源码如下所示:

Factory和Provider接口如下所示:

从使用者的角度看,无需关心对象是如何生成的,只需调用provider的get方法即可获得对象。而且对象应该是符合既定的规则并且初始化好可以马上用的。

从ABCModule_ProvideBFactory(或者某个Provider)的角度看,在初始化方法里就明确了自己所需依赖的对象(这里是ProviderA)。在get方法的实现里,只需关心B对象的生成。当需要A对象时,直接从外部“注入”的providerA取出即可。

再来看一看Main_MembersInjector的实现:

Dagger2在编译时会分析module中inject方法的参数的类型(这里是Main类),记录下用@Inject注解标注的成员,然后生成对应的Injector。

理解Injector的关键在理解它的构造方法和injectMembers方法。instance.c = cProvider.get();一句实施了依赖注入。

@Singleton

修改ABCModule如下所示:

修改ABCComponent如下所示:

我们来看看Dagger2生成的代码有什么不同:

@Generated(“dagger.internal.codegen.ComponentProcessor”)
public final class DaggerABCComponent implements ABCComponent {

private Provider provideBProvider;

private void initialize(final Builder builder) {

this.provideBProvider = ScopedProvider.create(ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider));

}

@Override
public B provideB() {
return provideBProvider.get();
}

}

可以看到唯一的不同是用ScopedProvider将ABCModule_ProvideBFactory包裹起来。来看一下ScopedProvider的源码:

理解上面的代码关键在于:

  1. ScopedProvider在dagger.internal下,非Dagger2自动生成。
  2. ScopedProvider也是一个Provider
  3. 利用double-check,在instance上实现了单例模式。也就是说,在ScopedProvider的生命周期内,get返回的都是同一个对象。

以上3点实现了无入侵式的Singleton模式。但其实ScopedProvider并不是专为Singleton模式设计的,Singleton模式只是Dagger2中Scope功能的效果。

@Scope

@Singleton注解的源码如下所示:

可以看出,@Singleton只是一个标记,表明这是一个Scope。那么Scope是什么呢?源代码中有如此注释:

A scope annotation applies to a class
containing an injectable constructor and governs how the injector reuses
instances of the type. By default, if no scope annotation is present, the
injector creates an instance (by injecting the type’s constructor), uses
the instance for one injection, and then forgets it. If a scope annotation
is present, the injector may retain the instance for possible reuse in a
later injection.

简单地说,@Scope决定了注射器从依赖图中取出对象的行为。如果节点有Scope标签,那么注入时将重用上次生成的对象。

依赖图中某个节点标注了@Scope后,便拥有了与当前Component相同的生命周期。也就是说,如果要实现全局(Application范围内)的Singleton,必需要有全局的Component。这就是为什么许多其他关于Dagger2的例子中,要在Application中保持ApplicationComponent的引用的原因。

至于在许多例子中看到的@PerActivity(对象在当前Activity的生命周期内唯一),@PerUser(对象在当前用户态销毁前唯一),它们的实现也是依赖于Component的生命周期。所以需要在Activity的onCreate中新建SomeActivityComponent并保持引用,在UserManager的login中新建UserComponent并保持引用。