自定义 Gradle 插件开发(一)

Gradle 插件简介 Gradle 插件是一个能够将 Gradle 的构建逻辑(build logic)和构建任务(build task)打包到一起,以便在多个项目的构建脚本(build.gradle)中应用(apply)的工具。 例如,build.gradle 构建脚本文件内 apply plugin: 'java' 、apply plugin: 'com.android.application' 中的 java、com.android.application 就是官方提供的 Gradle 插件,通过应用这些插件,可以丰富项目的构建任务与构建逻辑。 除官方提供的插件外,Gradle 还允许开发者定义自己的 Gradle 插件。开发者可以根据实际需求定义自己的构建逻辑和构建任务,将其打包为 Gradle 插件,从而在多个项目的构建脚本中复用。此外,还可以将自定义的 Gradle 插件发布到 plugin portal 或其他仓库中,更为方便的分享给他人使用。 Gradle 插件对编程语言没有太多限制,只要是能够被编译为 JVM 字节码的编程语言,都能用来编写 Gradle 插件。Gradle-API 的被设计为对 Groovy、Java、Koltin 友好的,通常情况下,使用 Java 或 Kotlin 这类静态类型语言实现的 Gradle 插件的性能要比使用 Groovy 实现的相同常见的性能更好。 开始之前 Gradle 作为一个普通的构建工具,本身并不依赖任何可视化 GUI 工具。为简化步骤,本文将采用命令行方式来完成自定义 Gradle 插件的演示。在开始之前,需先将 gradle 命令添加到系统环境变量中。 若读者在 IDEA / Android Studio 使用过 gradle ,则可在当前用户目录下找到 gradle 命令,具体路径如下 Mac:/Users/当前用户名/.gradle/wrapper/dists/gradle版本/沙盒路径/gradle版本/bin Win:C:\Users\当前用户名\....

September 7, 2019

Android中同时finish多个Activity - 一次内存泄露的小记

Android开发中,有时需要同时finish多个Activity,避免用户点击后退按钮时回退到不该呈现的Activity。对于这样的问题,常见的方式是维护一个单例ActivityList,将多个Activity置入此单例列表,需要关闭多个Activity时可遍历该List依次执行finish操作。 代码如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class ActivityUtil { private static List<Activity> activityList; private static ActivityUtil instance; private ActivityUtil() { } public static ActivityUtil getInstance() { return null == instance ?...

July 17, 2018

Android MVP架构解析

前言 Android Architecture Blueprints 是谷歌官方开源的一组 Android 架构方案,项目以多个分支分别采用 MVP,MVVM 的概念以及 dagger、rxjava、databinding、livedata 等工具库,演示了一个简单的 TodoApp 在不同架构模式下的代码组织方式。这个项目中的代码非常规范,架构模式也非常有借鉴意义,是一个很有价值的学习素材。 本文将通过 项目组织、架构组织与通信、设计原则 这三个层次对项目中的todo-mvp分支进行解析,从而达到学习的目的。 项目概览 在分析之前,我们先通过截图来了解一下TodoApp的功能。 TodoApp 用来跳转列表页与统计页的抽屉窗口 统计页 列表页 详情页 编辑页 知晓了项目功能后,再来看项目的组织。 项目组织 在开始之前推荐大家先了解一下IDEA符号表,熟悉符号表对理解项目组织有很大帮助。 总览 app/src 除了 androidTest、main、test 这三个常见的模板目录外,还有androidTestMock、mock与prod 目录。androidTestMock 虽不常见,但通过名称可推断它是一个测试相关的目录,另外还有 mock、prod 这两个目录,我们稍后会讲到。 ## UI模块划分 app/src/main/java 下的 addedittask、statistics、taskdetail、tasks 这四个目录分别对应 App 内编辑页、统计页、详情页、列表页这四个模块,每个目录都有自己的 Activity、Fragment与Presenter,分别表示 MVP 中的 **View** 层和 **Presenter** 层,Contract 由字面意义推断为契约接口,此处暂且不表,稍后会在源码中了解其实际内涵。 ## Model层 app/src/main/java 下的 data 目录表示 **Model** 层,Task 是经final修饰的实体类模型,data/source 下有表示数据源接口的 TasksDataSource 与表示数据仓库类的 TasksRepository,data/source/local 与 data/source/remote 分别表示本地数据源与远程数据源,由ToDoDatabase及TastsDao可推断其local部分以数据库的形式实现。 最下方的 BasePresenter、BaseView 分别表示Presenter与View的基类,剩下的utils包用到时再进行查阅即可。...

July 8, 2018

IntelliJ IDEA 断点调试时查看所有变量

在看Java HashMap工作原理及实现这篇文章时,发现博主在断点模式下来观察hash冲突的效果挺好的,随后便想在IntelliJ IDEA中也试下。 我们先来看下HashMap.Node的代码,有4个成员变量。 1 2 3 4 5 6 7 static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; //省略后续内容 } 如下图所示,对于HashMap.Node类型的对象,IDEA默认只显示了key,value这两个成员变量。 原因在于IDEA默认将HashMap.Node视为了Map.Entry,那么如何在Debug时以HashMap.Node的类型来查看该对象其他变量呢? 方法有二: 一. 在debugger的Variables面板中,右键该对象,找到View As选项,选择其中的Object,然后就可以看到该对象的所有属性了。 二. 采用第一种方式时,每次Debug都需要手动指定其类型,较为繁琐。对于常用的类,我们也可以在View as时点击Create按钮,为其新建类型。这样的话以后每次Debug时,无需任何设置就能在Variables面板内直接看到其准确类型的所有属性。 Create过程也很简单,在Java Type Renders窗口内,设置HashMap$Node对应的完整类名即可,如下图所示。 最后看一下,在View as中Create了HashMap及HashMap.Node后的效果。 现在已经能看到类中的所有成员变量了,那么对于静态变量该如何查看呢?这个也很简单,同样在此处右键,点击Customize Data Views… 然后在Static fields和Static final fields的选项前打勾即可~ 以HashMap为例,在选中了Static fields和Static final fields的选项后,所有的静态变量也显示出来了 :)

June 7, 2018

objc/runtime 探索(四)

前言 在这一篇中,我们来聊一聊runtime中method 定义 先来看一下method相关的定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef struct objc_method *Method; typedef struct objc_selector *SEL; typedef void (*IMP)(void /* id, SEL, ... */ ); //方法描述 struct objc_method_description { SEL name; //方法名称 char *types; //参数类型字符串 }; //以下代码是 ObjC2.0 之前method的定义 struct objc_method { SEL method_name; char *method_types; IMP method_imp; } 里边有三个类型别名,在这儿先解释一下...

August 19, 2014

objc/runtime 探索(三)

前言 续前,本文主要谈论类中的变量和属性 1.Ivar 1.1 Ivar的类型 typedef objc_ivar * Ivar; 1 2 3 4 5 6 7 8 struct objc_ivar { char *ivar_name; //ivar名称 char *ivar_type; //ivar类型 int ivar_offset; //ivar偏移量 #ifdef __LP64__ int space; #endif }//ObjC2.0 已过时 Ivar是objc_ivar的指针,包含变量名称,变量类型等成员. 1.2 为类添加Ivar 运行时规定,只能在objc_allocateClassPair与objc_registerClassPair两个函数之间为类添加变量 如下所示: 1 2 3 4 5 6 7 8 //额外空间 未知,通常设置为 0 Class clazz = objc_allocateClassPair(父类class,类名,额外空间); //以NSString*为例 //变量size sizeof(NSString) //对齐 指针类型的为log2(sizeof(NSString*)) //类型 @encode(NSString*) BOOL flag = class_addIvar(clazz,变量名,变量size,对齐,类型); objc_registerClassPair(clazz); 1....

August 18, 2014

objc/runtime 探索(二)

前言 接上次的话题,继续就objc/runtime进行讨论. 经过今晚的探究,基本掌握了运行时的函数规则,总的有如下几种函数前缀. 在后边的文章中我会就一些问题再次讨论,本文权当做API的速查手册使用. objc_ class_ object_ method_ property_ protocol_ ivar_ ,sel_ ,imp_ 1.objc_xxx 系列函数 函数名称 函数作用 objc_getClass 获取Class对象 objc_getMetaClass 获取MetaClass对象 objc_allocateClassPair 分配空间,创建类(仅在 创建之后,注册之前 能够添加成员变量) objc_registerClassPair 注册一个类(注册后方可使用该类创建对象) objc_disposeClassPair 注销某个类 objc_allocateProtocol 开辟空间创建协议 objc_registerProtocol 注册一个协议 objc_constructInstance 构造一个实例对象(ARC下无效) objc_destructInstance 析构一个实例对象(ARC下无效) objc_setAssociatedObject 为实例对象关联对象 objc_getAssociatedObje*ct 获取实例对象的关联对象 objc_removeAssociatedObjects 清空实例对象的所有关联对象 objc_msgSend 发送ObjC消息 objc_系列函数关注于宏观使用,如类与协议的空间分配,注册,注销等操作...

August 17, 2014

objc/runtime 探索(一)

前言 最近闲来无事,打算对objc/runtime进行一番研究,今晚把API翻了一遍,拿出了如下的一些有趣代码,本代码需事先导入部分objc/runtime中的头文件,如下所示 1 2 #import <objc/runtime.h> #import <objc/message.h> 动态创建类 类的创建分为两步,添加成员变量需要在这两步操作之间,添加成员方法则无此要求 Class objc_allocateClassPair(Class superClass,const char* className,size_t extraBytes); void objc_registerClassPair(Class cls); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //开始类的定义 Class Test= objc_allocateClassPair([NSObject class], "Test", 0); //为类添加变量 class_addIvar(Test, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*)); //为类添加方法 //1.注册名为 test: 的方法 SEL s = sel_registerName("test:"); //2.定义函数实现,此处的IMP是函数指针,原型为 typedef id (*IMP)(id, SEL, ....

August 15, 2014

变长参数的讨论

前言 一直以来对变长参数都比较好奇,索性花时间解解痒. 先从数组指针来看 void test1(int* arr,int count) { int sum = 0; for (int *p = arr; p < arr + count; p++) { sum = sum+ *p; } printf("%d\n",sum ); } 代码较为简单,此处不再展开. 接下来是可变参数 可变参数需要使用 <stdarg.h> 头文件,请提前导入 什么是可变参数? 可变参数是函数或方法中形如int show(int a,...)这样的的参数 可变参数的相关规定 ... 不能单独出现,且只能放在参数列表中的最后 变长参数的类型没有限定,使用时最好统一类型,避免类型安全问题 变长参数的个数没有限定,调用时一般以0x00即 NULL/nil结尾 void test2(int a,...) { int sum = a; //声明变长参数列表vl va_list vl; //va_start :将变长参数的前一个参数的地址存入vl va_start(vl,a); int next; //va_arg :将vl向后偏移sizeof(int),并返回偏移后长度为sizeof(int)的数据 //判断 next 是否为 0x00 ,如果不为 0x00 则继续遍历 while( (next = va_arg(vl,int)) ) { sum+= next; } //释放变长参数列表vl va_end(vl); printf("%d\n",sum ); } 调用形式 int main(int argc,char * args[]) { int arr[] = {1,2,3,4,5}; //数组指针 test1(arr,5); printf("---------\n"); //变长参数 test2(1,2,3,4,5,6,NULL); return 0; } 输出结果 15 --------- 21 小结 ....

August 15, 2014

数据结构--栈

栈 栈是一种数据结构,其特点是后进先出(LIFO).计算机中有很多操作都采用了栈的数据结构,如浏览器的后退,文本编辑的撤销操作等. 栈的定义 栈的额定大小 栈的实际大小 栈的数据 栈的操作 入栈 出栈 清空栈 初始化栈 输出栈内容 栈的应用 中缀转后缀 从左到右遍历中缀表达式: 遇到数值时,直接输出; 遇到操作符时: 如果栈为空或操作符为(,则操作符入栈 如果操作符为),则一直弹栈到与其匹配的(为止 如果栈不为空,则将栈顶操作符与当前操作符进行比较: 如果当前操作符的优先级小于等于栈顶符号的优先级,则将栈顶操作符出栈输出,然后再次拿栈顶符号与当前操作符进行比较; 如果当前操作符的优先级大于栈顶操作符的优先级,则将当前操作符入栈; 直到表达式结束 后缀表达式的计算: 从左到右遍历表达式: 遇到数值直接进栈; 遇到操作符时,取出栈顶的两个元素进行计算,将计算结果入栈 直到表达式结束

July 28, 2014