[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并保持引用。

[Android] Dagger2 入门 1

这篇文章主要谈一下本人在学习Dagger2的心得,如有错漏,敬请谅解。

什么是依赖注入

依赖注入就是把下面这样的代码:

变成:

这种把对象之间的依赖生成的责任交给外界的做法,叫做依赖注入。

如何更方便地进行依赖注入

我们有类和它们之间的依赖关系,便很自然地会用图来表示这种状态。如上例子所示,可用下面这样一个图来表示:

箭头表示依赖的对象。

我们想要这样的一种依赖注入框架:当我们需要一个B对象时,框架按照依赖遍历这个图,生成A,然后将其注入B,最后返回一个已经生成好的B对象。大概是:

B b = Injector.create(B.class)

另外,如果要求A对象是单例(这里不解释什么是单例)或对象的生成符合某种指定的规则,框架应自动识别并作出处理。

设计框架

我们面对两个主要问题:如何表示依赖图和如何生成对象。

依赖图的表示

我们需定义一种声明依赖的方法。可以用xml,json,甚至DSL来完成这个任务。这里我们采用比较流行和简便的注解(annotation)来表示依赖关系。

假设我们要的效果如下所示:

可以看到,我们用@dependence注解来表示上面例图中的箭头。各个类之间的依赖关系十分清晰。

如果要求A是单例,我们可以这样:

对象生成

建立了依赖图以后,需要通过某种方式生成我们需要的对象。我们希望是这样的:

或者通过注解实现自动注入

Dagger2

我们来看一下Dagger2是如何实现上述两个目标的。

依赖图的表示

Dagger2中,是通过@Inject注解或者@Module和@Provide这两个注解建立依赖图,如下所示:

首先定义好类:

然后我们用第一种方法来声明依赖:

可以看到我们为每一个类的方法添加了@Inject声明,表示该类是依赖图中的一个节点。如果该初始化方法含有参数,那么这些从参数也应是依赖图中的节点。

第二种方法是通过一个module类来声明依赖,如下所示:

@Module注解表示这个ABCModule的作用是声明“依赖图”的。@Provides注解表示当前方法的返回值是图中的一个节点,方法的参数是依赖的对象,即前文中箭头指向的目标。

再强调一次,Dagger要求图中的每一个节点都要声明,即每一个节点都要在module中有@Provides注解的方法或者@Inject注解的初始化方法。

可以看到第二种方式(module)无需修改原来的对象。为了让模块尽量少地依赖第三方库,一般采用第二种方式来声明依赖图。

对象生成

Dagger2中,从依赖图中获取对象需通过component。component是依赖图和被注入对象之间的桥梁。如下所示:

@Component注解表示ABCComponent这个接口是一个Component。Component的方法隐含着如下两条规则:

  1. 不带参数的方法为“provider”方法,该方法的返回值是从依赖图中取得的对象。如下所示(伪代码):

    class Main {

    }

  2. 带参数的方法,参数为“注入对象”。通常于@Inject标签同时使用。如下所示(伪代码):

    class Main {

    }

即调用Component.inject(foorbar)的时候,框架自动为用@Inject标签标注的属性注入依赖。要求@Inject的属性的类必须是依赖图中的节点。

注意:component的方法必需至少符合以上两条规则中的一条。

注意:provider方法的名字一般为“provider”,inject方法的名字一般为“inject”,但名字不影响这两个方法的功能。

当Component声明好以后,框架会在编译时生成一个DaggerComponent名字的类,我们可以用它来实施依赖注入,如下所示:

或者:

Component标签的module属性可以是一个数组,即一个Component实施多个module的注入。引入类D和DModule:

修改ABCComponent,如下:

如上即可实现D对象的注入。

Component之间的依赖

真正实施工程的时候,会将对象以功能分类。例如network相关,DB相关,Util相关的类集中在一起管理。Dagger2为方便我们达到这一个目的,在component中引入了dependence这个功能。

例如我们有如下component

假设DComponent负责提供一个对象D。这种能力是项目无关的,我们把这个Component独立出来。然后我们可以通过@Component的dependence属性来为其他Component引入DComponent的能力。例如:

可以看到,声明了dependencies=DComponent.class以后,provideD方法可以顺利拿到D对象。inject方法也可以注入D对象。

DComponent不知道ABCComponent的存在,故可以像普通Component那样子使用。但在使用ABCComponent时,我们需要显式地为ABCComponent注入DComponent对象:

@Singleton

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

DComponent接口也需要声明:

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

总结

这篇文章只是简单地介绍了Dagger2的基本用法,下一篇打算讲Dagger2中的Scope和Subcomponent还在一些别的东西,敬请期待。