xray的博客


  • 首页

  • 归档

  • 关于

  • 分类

  • 标签

Hello World

发表于 2019-10-27

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

SharePreferences源码解读

发表于 2019-03-01

查看原文

设计模式之代理模式

发表于 2019-02-15

查看原文

RecyclerView局部刷新和Diff算法

发表于 2019-01-30

查看原文

Django环境配置

发表于 2017-06-09

前言

最近写android写的有点烦了.想搞点服务端的东西,刚开始考虑使用java的,但是java web学起来太麻烦了,而且也不怎么适合练手.正好之前学了些python的语法,而且最近python真的很火,什么大数据,机器学习,人工智能,到现在搞不清,机器学习和人工智能的区别.python有个很火的web框架Django.貌似用的人比较多(有利于收集学习资料),好,就拿你开刀了!

安装Django

我用的系统是ubuntu,下面所有的操作默认都是在ubuntu下完成的.

使用python的依赖管理工具pip安装,命令:

1
sudo pip install Django

检查一下下是否安装完成:

1
2
3
4
>>> import django
>>> django.VERSION
(1, 9, 6, 'final', 0)
>>>

搭建多个独立的开发环境

有些时候我们有多个不同的项目,并且每个项目使用的django版本是不一样的,那咋办,这不是坑爹吗,难道需要我们每打开一个项目,就重新安装卸载一次django.这是不可能的,程序猿哪有这么傻.那我们该咋办了.答案是使用virtualenv来管理多个开发环境.同时安装virtualenvwrapper,virtualenvwrapper是virtualenv的封装,可以方便的创建/删除/拷贝/切换不通的环境.
安装virtualenv和virtualenvwrapper

1
2
sudo pip install virtualenv
sudo pip install virtualenvwrapper

修改~/.bash_profile或其它环境变量相关文件(如 .bashrc 或用 ZSH 之后的 .zshrc),添加以下语句

1
2
3
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/workspace
source /usr/local/bin/virtualenvwrapper.sh

使用方法

1
2
3
mkvirtualenv env 创建虚拟环境
workon env 切换到env
deactivate 退出终端环境

还有其他的一些方法,有兴趣的可以自己去找.如果使用了ide PyCharm的话创建和使用VirtualEnv就更方便了.创建和查看VirtualEnv可以在

1
File > Settings > Project Interpreter

中设置

总结

Django环境的配置就介绍到这里,接下来,就到Django的内部去看看,它到底该咋用.

Android开发使用枚举

发表于 2017-04-12

用静态常量代替枚举

在Android的官方文档中,关于枚举的使用,给出的建议是枚举占用的内存是静态常量的两倍,所以建议不要在android中使用枚举。但是枚举有个很重要的优点就是,提供了类型的安全。那么使用静态常量怎么能保障类型的安全呢。使用@IntDef和@StringDef,用来提供编译期的类型检查,使用很简单

以IntDef为例

首先引入依赖:

1
2

compile 'com.android.support:support-annotations:22.0.0'

具体的使用代码

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

public class MainActivity extends AppCompatActivity {

/** 成功*/

private static final int SUCCESS = 1;

/** 失败*/

private static final int FAIL = 2;

/** 处理中*/

private static final int DEALING = 3;



@IntDef({SUCCESS, FAIL, DEALING})

@Retention(RetentionPolicy.SOURCE)

public @interface State{}



@State

private int mState;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);



}

}

这样在事例中给mState变量赋值时就只能限制在我们给出的静态常量了。

Android自定义编译时注解

发表于 2017-04-11

前言

Android 现在的开源库中大量的使用了注解,无论是运行时注解,还是编译时注解或者是标准的注解都被广泛的使用著名的使用注解的Andorid库有,Retrofit, BufferKnife,Dagger2等。

注解的定义

注解是Java中的一个特性,它是在源代码中插入标签,这些标签在之后的编译和运行中起到某种作用,注解的定义需要注解接口@interface创建,接口的方法对应着注解的元素。我们用一个简单例子,看看一个注解是怎么声明的:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Bind {
int value();
}
@interface 声明会创建一个实际的Java接口,与其他接口一样,注解也将会编译成.class文件。注解接口中的元素声明实际上是方法声明,注解接口中的方法没有参数,没有throws语句,也不能使用泛型。

标准注解

Java API中默认定义的注解我们称之为标准注解。它们定义在java.lang、java.lang.annotation和javax.annotation包中。按照使用场景不同,可以分为如下3类。

编译相关注解

编译相关注解是给编译器使用的,有以下几种。

  • @Override: 编译器会检查被注解的方法是否真的重载了一个来自父类的方法,如果没有,编译器会给出错误的提示。
  • @Deprecated:用来修饰任何不再鼓励使用或已经弃用的属性和方法等。
  • @Suppress Warning:用于除了包之外的其他声明项中,用来抑制某种类型的警告。

元注解

元注解,顾名思义,就是用来定义和实现注解的注解,总用有如下五种:

  • @Target:这个注解的取值是一个ElementType类型的数组,用来指定注解所使用的对象范围,总共有十种不同的类型:
    元素类型 适用于

    • ANNOTATION_TYPE 注解类型声明
    • CONSTRUCTOR 构造函数
    • FIELD 实例变量
    • LOCAL_VARIABLE 局部变量
    • METHOD 方法
    • PACKAGE 包
    • PARAMETER 方法参数或构造函数的参数
    • TYPE 类(包含enum)和接口(包含注解类型)
    • TYPE_PARAMETER 类型参数
    • TYPE_USE 类型的用途
  • @Retention: 用来注明注解的访问范围,也就是在什么级别保留注解,有下面三种选择

    • @Retention(RetentionPolicy.SOURCE) ,源码级注解,该类型的注解只会保留在.java源码里,源码编译后,注解的信息会被丢弃,不会保留在.class文件中。
    • @Retention(RetentionPolicy.CLASS), 编译时注解,该类型的注解会被保留在.java和.class中,在执行的过程中会被Java虚拟机丢弃,不会加载到虚拟机中。
    • @Retention(RetentionPolicy.RUNTIME),运行时注解,java虚拟机在运行时也会保留的注解,可以通过反射机制读取注解的信息(.java源码、.class文件和执行的时候都有注解信息)
      未指定类型时,默认是CLASS类型
  • @Documented : 表示被修饰的注解应该被包含在被注解项的文档中

  • @Inherited : 表示该注解是可以被子类继承的。

运行时注解

运行时注解相对比较简单,一般和反射机制配合使用,相比编译时注解性能较低,但灵活性好,实现起来比较简单,可以参考其他文档。

编译时注解

编译时注解能够自动处理Java源文件并生成更多的源码、配置文件、脚本或其他可能想要生成的东西。这些操作都是通过注解处理器生成的。Java编译器集成了注解处理,通过在编译期调用javac -processor命令可以调起注解处理器,他能够允许我们实现编译时注解的功能。

定义注解处理器

在编译期间,编译器会定位到Java源文件的注解,注解处理器会对它感兴趣的注解进行处理。需要注意的是,一个处理器只能生成新的源文件,而不能修改已经存在的文件。注解处理器一般需要继承AbstractProcessor类并实现process方法,同时需要指定这个处理器能够支持的java版本语句如下:

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

/**
* Created by cfp on 17-3-29.
*/
@AutoService(Process.class)
public class ViewInjectProcessor extends AbstractProcessor {
private Messager mMessager;
private Elements mElementUtils;
private Filer mFiler;
private Map<String, ProxyInfo> mProxyMap = new HashMap<String, ProxyInfo>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv)
{
super.init(processingEnv);
mMessager = processingEnv.getMessager();
mMessager.printMessage(Diagnostic.Kind.NOTE , "init..............................");
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes()
{
HashSet<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(Bind.class.getCanonicalName());
return supportTypes;
}
@Override
public SourceVersion getSupportedSourceVersion()
{
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
{
mMessager.printMessage(Diagnostic.Kind.NOTE , "process..............................");
mProxyMap.clear();
Set<? extends Element> elesWithBind = roundEnv.getElementsAnnotatedWith(Bind.class);
for (Element element : elesWithBind)
{
checkAnnotationValid(element, Bind.class);
VariableElement variableElement = (VariableElement) element;
//class type
TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
//full class name
String fqClassName = classElement.getQualifiedName().toString();
ProxyInfo proxyInfo = mProxyMap.get(fqClassName);
if (proxyInfo == null)
{
proxyInfo = new ProxyInfo(mElementUtils, classElement);
mProxyMap.put(fqClassName, proxyInfo);
}
Bind bindAnnotation = variableElement.getAnnotation(Bind.class);
int id = bindAnnotation.value();
proxyInfo.injectVariables.put(id , variableElement);
}
for (String key : mProxyMap.keySet())
{
ProxyInfo proxyInfo = mProxyMap.get(key);
try
{
JavaFileObject jfo = mFiler.createSourceFile(
proxyInfo.getProxyClassFullName(),
proxyInfo.getTypeElement());
Writer writer = jfo.openWriter();
writer.write(proxyInfo.generateJavaCode());
writer.flush();
writer.close();
} catch (IOException e)
{
error(proxyInfo.getTypeElement(),
"Unable to write injector for type %s: %s",
proxyInfo.getTypeElement(), e.getMessage());
}
}
return true;
}
private boolean checkAnnotationValid(Element annotatedElement, Class clazz)
{
if (annotatedElement.getKind() != ElementKind.FIELD)
{
error(annotatedElement, "%s must be declared on field.", clazz.getSimpleName());
return false;
}
if (ClassValidator.isPrivate(annotatedElement))
{
error(annotatedElement, "%s() must can not be private.", annotatedElement.getSimpleName());
return false;
}
return true;
}
private void error(Element element, String message, Object... args)
{
if (args.length > 0)
{
message = String.format(message, args);
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message, element);
}
}

下面就以ViewInjectProcessor为例,介绍一下注解处理器中各个方法的意义。

void init(ProcessingEnvironment processingEnv):初始化方法被注解处理工具调用,并传入ProcessingEnvironment类型的参数,这个参数提供了很多的工具,如Elements、Messager、Filter等
getSupportedAnnotationTypes() 指定这个注解处理器能够处理的注解类型,返回一个注解类型的集合
getSupportedSourceVersion() 指定注解处理器使用的Java版本,通常返回 SourceVersion.latestSupported()即可。
process(Set annotations, RoundEnvironment roundEnv) 这个方法中实现了注解处理器的具体业务逻辑,根据输入的参数roundEnv可以得到包含特定注解的被注解元素,然后可以编写处理注解的代码和生成Java源码文件的代码等等。
在Java7中可以使用注解来代替上面的getSupportedAnnotationTypes()方法和getSupportedSourceVersion(),如下所示:

1
2
3
4
5
6
7
8

/**
* Created by cfp on 17-3-29.
*/
@SupportedAnnotationTypes({})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@AutoService(Process.class)
public class ViewInjectProcessor extends AbstractProcessor {

注册注解处理器

注解处理器定义好之后,为了让javac -processor能够进行处理,我们需要把注解处理器打包到一个jar包中,同时,需要在jar文件中添加一个名为javax.annotation.processing.Processor的文件,这个文件在/META-INF/services目录中。javax.annotation.processing.Processor文件中内容是注解处理器的全路径名,如果存在多个注解处理器,以换行进行分割。

另外这个编译的module需要是一个Java Library而不能是Android Library,因为AbsProcessor这个类是在Javax中的,不包含在Android Library的JDK中。

手动执行上面的注册过程其实是很麻烦的,因此google开源了一个库AutoService可以帮我们自动的注册只需要添加@AutoService(Process.class)即可。

android-apt插件

注解处理器所在的jar文件只是在编译的时候起作用,在应用运行的时候就没什么用了,因此在build.gradle中引入依赖时应该以provided方式而不是compile方式引入

provided ‘依赖’
compile ‘依赖’
当然,我们可以用另外一种方法,就是使用android-apt插件的方式。它是android-studio中使用的一个处理注解的一个插件,它的作用如下:

只在编译期间引入注解所在函数库的依赖,不会打包到最终的apk包中。
为注解处理器生成的源代码设置好路径,以便android studio能够正确找到。
android-apt的使用比较简单
在使用到注解处理器函数库的模块中引入插件

1
2

apply plugin: 'com.neenbedankt.android-apt'

然后在根目录的build.gradle添加

1
2
3
4

dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

最后
在dependencies中就可以添加依赖

1
2

apt project(':viewinject-compiler')

总结

现在Android许多的开源库中都使用了注解,特别是编译时注解,如果使用的恰当不仅使的api的调用更简单,有些情况会显著的提高应用的性能,具体可以参考ARouter这个项目。

工厂模式

发表于 2017-03-11

理财产品的创建

开篇先介绍一下我们公司的业务,我们公司是一家金融理财公司。在公司的业务中有很多打包好的理财产品,种类繁多。它们既有很多相似的特征,也有一些区别。不久前,公司的ceo觉得我们的这些理财产品的名称,以及里面包含的专业术语,对于普通的用户理解起来有很大的困难。于是乎,需求出来了,将所有之前的理财产品名称和一些描述,全部换掉。使用一些让用户户更容易理解的名称和描述。但是为了兼容旧的产品种类,我们不能在旧的种类上直接修改,需要将之前的种类,一对一的再新生成一套。面对这样的需求,就要用到我们介绍的工厂模式。

那我们的系统是怎么设计的呢?我们使用了工厂方法模式(factory method pattern),先看看我们的类图。



类图比较简单, AbsFinancialFactory是一个抽象类,定义了一个产生金融产品的工厂类,FinancialFactory为实现类,完成具体的任务-生成新的理财产品。FinancialProduct是我们的产品抽象类,定义了一些金融产品的共有属性和方法。ImproveSalaryProduct和NewUserFinancialProdcut是我公司旗下的两个具体的理财产品”旺薪宝”和”新手包”。Client则是场景类。

下面我们具体看看代码:

AbsFinancialFactory.java

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



/**

* 抽象金融产品工厂

* Created by cfp on 17-3-8.

*/

public abstract class AbsFinancialFactory {



/**

* 创建金融产品的工厂类

* @param c

* @param <T>

* @return

*/

public abstract <T extends FinancialProduct> T createProduct(Class<T> c);

}

AbsFinancialFactory是抽象工厂类,定义了创建产品的方法createProduct(Class c),参数为Class类型。在这里我们使用了泛型,通过定义泛型可以对createProduct的输入参数产生限制:必须是FinancialProduct的实现类,并且是Class类型。

FinancialFactory.java

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



/**

* 具体产品工厂类

* Created by cfp on 17-3-8.

*/

public class FinancialFactory extends AbsFinancialFactory{

@Override

public <T extends FinancialProduct> T createProduct(Class<T> c) {



FinancialProduct financialProduct = null;

try {

financialProduct = (FinancialProduct) Class.forName(c.getName()).newInstance();

} catch (Exception e) {

System.out.print("创建金融产品类失败");

}

return (T) financialProduct;

}

}

FinancialFactory是具体的实现类,这里使用了java的反射去创建具体的产品实例。

FinancialProduct.java

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146



/**

* 抽象金融产品类

* Created by cfp on 17-3-8.

*/

public abstract class FinancialProduct {



private String mName;

private float mRate;

private String mDate;

private float mTotalMoney;

private float mAddRate;





public String getmName() {

return mName;

}



public void setmName(String mName) {

this.mName = mName;

}



public float getmRate() {

return mRate;

}



public void setmRate(float mRate) {

this.mRate = mRate;

}



public String getmDate() {

return mDate;

}



public void setmDate(String mDate) {

this.mDate = mDate;

}



public float getmTotalMoney() {

return mTotalMoney;

}



public void setmTotalMoney(float mTotalMoney) {

this.mTotalMoney = mTotalMoney;

}



public float getmAddRate() {

return mAddRate;

}



public void setmAddRate(float mAddRate) {

this.mAddRate = mAddRate;

}



/**

* 显示改产品的信息

*/

public void showInfo(){



System.out.println( "FinancialProduct{" +

"mName='" + mName + '\'' +

", mRate='" + mRate + '\'' +

", mDate='" + mDate + '\'' +

", mTotalMoney=" + mTotalMoney +

", mAddRate=" + mAddRate +

'}');

}



/**

* 活动

*/

public abstract void activity();

}

FinancialProduct是抽象的产品类,定义了我们平台产品的一些共同属性和方法。

NewUserFinancialProduct.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/**

* Created by cfp on 17-3-8.

*/

public class NewUserFinancialProduct extends FinancialProduct{





@Override

public void activity() {

System.out.println( getmName() + "正在搞活动...");

}

}

NewUserFinancialProduct是具体的金融产品类”新手宝“

ImproveSalaryProduct.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

/**

* 旺薪宝

* Created by cfp on 17-3-8.

*/

public class ImproveSalaryProduct extends FinancialProduct{

@Override

public void activity() {

System.out.println( getmName() + "正在搞活动...");

}

}

ImproveSalaryProduct是具体的金融产品类”旺薪宝”

现在需要看看我们的场景类Client,怎么通过工厂去创建具体的金融产品的。直接上代码:

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
60
61
62

/**

* Created by cfp on 17-3-8.

*/

public class Client {





public static void main(String[] args){



FinancialFactory financialFactory = new FinancialFactory();



//新手宝

FinancialProduct newUserProduct = financialFactory.createProduct(NewUserFinancialProduct.class);

newUserProduct.setmName("新手宝");

newUserProduct.setmDate("365天");

newUserProduct.setmTotalMoney(1000000f);

newUserProduct.setmRate(10f);

newUserProduct.setmAddRate(0.5f);

newUserProduct.activity();

newUserProduct.showInfo();



//旺薪宝

FinancialProduct improveSalaryProduct = financialFactory.createProduct(ImproveSalaryProduct.class);

improveSalaryProduct.setmName("旺薪宝");

improveSalaryProduct.setmDate("360天");

improveSalaryProduct.setmTotalMoney(2000000f);

improveSalaryProduct.setmRate(8.0f);

improveSalaryProduct.setmAddRate(0.2f);

improveSalaryProduct.activity();

improveSalaryProduct.showInfo();

}

}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12

新手宝正在搞活动...

FinancialProduct{mName='新手宝', mRate='10.0', mDate='365天', mTotalMoney=1000000.0, mAddRate=0.5}

旺薪宝正在搞活动...

FinancialProduct{mName='旺薪宝', mRate='8.0', mDate='360天', mTotalMoney=2000000.0, mAddRate=0.2}



Process finished with exit code 0

通过结果了解到我们的产品已经成功的生产出来了。

工厂方法模式的定义

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

工厂方法的通用类图:

在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现事物最抽象的定义。Factory为抽象创建类,具体的实现在ConcreteFactory中完成。工厂方法的模式变种很多。我们来看个比较实用的通用代码。

抽象产品类

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

/**

* 抽象产品类

* Created by cfp on 17-3-8.

*/

public abstract class Product {



//产品类的公共方法

public void method1(){

//业务逻辑

}

//抽象方法

public abstract void method2();

}

具体产品类,可能有多个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/**

* 具体产品类

* Created by cfp on 17-3-8.

*/

public class ConcreteProduct1 extends Product{

@Override

public void method2() {

//业务逻辑

System.out.print("ConcreteProduct1 method2 execute...");

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/**

* 具体产品类

* Created by cfp on 17-3-8.

*/

public class ConcreteProduct2 extends Product{

@Override

public void method2() {

//业务逻辑

System.out.print("ConcreteProduct2 method2 execute...");

}

}

抽象工厂类

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



/**

* 抽象工厂类

* Created by cfp on 17-3-8.

*/

public abstract class Factory {



/**

* 创建一个产品对象,其输入参数类型就可以自行生成

* @param c

* @param <T>

* @return

*/

public abstract <T extends Product> T createProduct(Class<T> c);

}

具体工厂类

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

/**

* 具体工厂类

* Created by cfp on 17-3-8.

*/

public class ConcreteFactory extends Factory{

@Override

public <T extends Product> T createProduct(Class<T> c) {



Product product = null;

try {

product = (Product) Class.forName(c.getName()).newInstance();

} catch (Exception e) {

System.out.print("创建产品类失败");

}



return (T) product;

}

}

场景类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

/**

* Created by cfp on 17-3-8.

*/

public class Client {



public static void main(String[] args){



Factory factory = new ConcreteFactory();

Product product = factory.createProduct(ConcreteProduct1.class);

product.method2();

}

}

该通用代码是一个比较实用、易扩展的框架,当然也可以自行的进行扩展。

工厂方法模式的应用

工厂方法模式的优点

首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的过程,降低模块之间的耦合。

其次,工厂模式的扩展性非常的好。比如上面的理财产品,我们想再增加一个,只需要新建个具体的产品类就可以了。工厂类不需要任何的修改。

再次,屏蔽产品类。我们不需要关心产品类里的具体实现,只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不会方式变化。

工厂方法模式的缺点

首先,新加产品时需要添加具体的产品类,有时还需要添加具体的工厂类,系统中的类成对增长,增加了系统的复杂性。

其次,为了扩展性,定义了抽象层,增加了系统的理解难度.

工厂方法模式的扩展

工厂方法模式有很多的扩展,下面介绍几种常用到的扩展。

  • 简化为简单工厂模式

如果一个模块仅需要一个工程类的时候,就没必要把它生产出来,使用静态方法就可以了。根据这个要求,我们可以修改上面的例子为简单工厂模式,将AbsFinancialFactory去掉,修改一下FinancialFactory,类图如下

FinancialProduct类修改为:

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

/**

* 简单工厂金融产品类

* Created by cfp on 17-3-8.

*/

public class FinancialFactory{





public static <T extends FinancialProduct> T createProduct(Class<T> c){

FinancialProduct financialProduct = null;

try {

financialProduct = (FinancialProduct) Class.forName(c.getName()).newInstance();

} catch (Exception e) {

System.out.print("创建金融产品类失败");

}

return (T) financialProduct;



}

}

场景类

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



/**

* Created by cfp on 17-3-8.

*/

public class Client {



public static void main(String[] args) {



FinancialProduct newUserProduct = FinancialFactory.createProduct(NewUserFinancialProduct.class);

newUserProduct.setmName("新手宝");

newUserProduct.setmDate("365天");

newUserProduct.setmTotalMoney(1000000f);

newUserProduct.setmRate(10f);

newUserProduct.setmAddRate(0.5f);

newUserProduct.activity();

newUserProduct.showInfo();

}

}

运行结果:

1
2
3
4
5
6
7
8

新手宝正在搞活动...

FinancialProduct{mName='新手宝', mRate='10.0', mDate='365天', mTotalMoney=1000000.0, mAddRate=0.5}



Process finished with exit code 0

我们将之前的工厂方法模式简化成简单工厂模式,代码只需要两个地方发生变化。使的系统变的简单。但是它有个缺点就是工厂类不容易扩展了。

  • 升级为多个工厂类

当我们在做一个比较复杂的项目时,经常会遇到初始化一个对象很耗费精力的情况,所有的产品类都放到一个工厂方法中进行初始化会使代码结构不清晰。

考虑到需要清晰,我们就为每个产品定义一个工厂类。具体的类图如下:

每个产品都对应了一个工厂,每个工厂都独立负责创建对应的产品,非常的符合单一责任原则。按照这种模式我们看看代码发生了哪些变化

抽象工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**

* Created by cfp on 17-3-8.

*/

public abstract class AbsFinancialFactory {



public abstract FinancialProduct createProduct();

}

抽象工厂中的创建方法不再需要传递参数,因为具体的工厂只生产对应的产品。

”新手宝”的工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

/**

* 新手宝工厂类

* Created by cfp on 17-3-8.

*/

public class NewUserFinancialFactory extends AbsFinancialFactory{

@Override

public FinancialProduct createProduct() {

return new NewUserFinancialProduct();

}

}

“旺薪宝”的工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22



/**

* 旺薪宝工厂类

* Created by cfp on 17-3-8.

*/

public class ImproveSalaryFinancialFactory extends AbsFinancialFactory{

@Override

public FinancialProduct createProduct() {

return new ImproveSalaryProduct();

}

}

场景类

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

/**

* Created by cfp on 17-3-8.

*/

public class Client {



public static void main(String[] args) {



//新手宝

FinancialProduct newUserProduct = new NewUserFinancialFactory().createProduct();

newUserProduct.setmName("新手宝");

newUserProduct.setmDate("365天");

newUserProduct.setmTotalMoney(1000000f);

newUserProduct.setmRate(10f);

newUserProduct.setmAddRate(0.5f);

newUserProduct.activity();

newUserProduct.showInfo();



//旺薪宝

FinancialProduct improveSalaryProduct = new ImproveSalaryFinancialFactory().createProduct();

improveSalaryProduct.setmName("旺薪宝");

improveSalaryProduct.setmDate("360天");

improveSalaryProduct.setmTotalMoney(2000000f);

improveSalaryProduct.setmRate(8.0f);

improveSalaryProduct.setmAddRate(0.2f);

improveSalaryProduct.activity();

improveSalaryProduct.showInfo();



}

}

运行接口还和前面的一样。

我们回头看一下,每一个产品对应一个工厂,好处是创建的职责单一,代码清晰,但是不好的地方是使扩展变的麻烦,每增加一个产品类需要对应的生成一个工厂类,同时还的维护两个类之间的关系。

当然,在复制的应用中一般都使用多工厂的方法,然后增加一个协调类,避免调用者与各个子工厂交流。,协调类的作用是封装子工厂类,对高层模块暴露统一的接口。

  • 替代单例模式

单例模式其实就是内存中只存在一个对象,通过工厂方法模式也可以达到相同的目的。类图如下:

Singleton定义了一个private的无参构造函数。目的是不允许new对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

/**

* Created by cfp on 17-3-8.

*/

public class Singleton {



private Singleton(){

}

public void doSomething(){

//业务处理

System.out.println("我是实例 = " + this);

}

}

Singleton不能通过new来创建一个对象,那么SingletonFactory怎么创建Singleton的对象呢,答案是反射。

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
60
61
62

/**

* 单例生成工厂

* Created by cfp on 17-3-8.

*/

public class SingletonFactory {



private static Singleton singleton;



static {



try {

Class cls = Class.forName(Singleton.class.getName());

//获取无参构造

Constructor constructor = cls.getDeclaredConstructor();

//设置无参构造是可访问的

constructor.setAccessible(true);

singleton = (Singleton) constructor.newInstance();



} catch (Exception e) {

System.out.println("创建单例失败");

}

}



/**

* 获取单例

* @return

*/

public static Singleton getSingleton(){

return singleton;

}

}

这样只要在团队内约定好,只能通过SingletonFactory去创建Singleton对象,而不允许其他人在其他类中使用反射获取Singleton的对象,我们就能保证Singleton的实例是唯一的。

现在,在场景类中测试一下最后的结果

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

/**

* Created by cfp on 17-3-9.

*/

public class Client {



public static void main(String[] args) {

Singleton singleton = SingletonFactory.getSingleton();

singleton.doSomething();

Singleton singleton2 = SingletonFactory.getSingleton();

singleton.doSomething();

Singleton singleton3 = SingletonFactory.getSingleton();

singleton.doSomething();

}

}

运行结果显示,我么获得确实是单例

1
2
3
4
5
6
7
8
9
10

我是实例 = com.xray.singleton.Singleton@11c10834

我是实例 = com.xray.singleton.Singleton@11c10834

我是实例 = com.xray.singleton.Singleton@11c10834



Process finished with exit code 0

总结

工厂方法模式适用情况包括:一个类不知道它所需要的对象的类;一个类通过其子类来指定创建哪个对象;将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。

Android防止抓包

发表于 2017-03-01

1.证书相关玩意

2.android网络安全配置

3.Android HTTPS SSL双向验证

4.Android N HTTPS

5.Java和HTTP的那些事 HTTPS和证书

概述

现在大量的app的api都采用了https传输协议,但是只采用https也不能完全的保障数据的安全,比如使用Fiddler,charles等工具,很容易可以进行中间人攻击,Android N (sdk > =24)在Https的安全性,防止中间人攻击做了加强.

主要的功能如下:

  • 自定义信任锚:针对应用的安全连接自定义哪些证书颁发机构 (CA) 值得信任。例如,信任特定的自签署证书或限制应用信任的公共 CA 集。

  • 仅调试重写:在应用中以安全方式调试安全连接,而不会增加已安装用户的风险。

  • 明文通信选择退出:防止应用意外使用明文通信。

  • 证书固定:将应用的安全连接限制为特定的证书。

其中第四条就可以在一定的程度上防止中间人攻击,比如防止Fiddler,charles通过导入自己的证书而进行抓包。具体的配置可以看上面的[参考2].但是对于sdk<24的版本怎么办呢,我可以在代码中固定证书。

android-async-http设置自定义证书

下面以android-async-http 为例,介绍一下给AsyncHttpClient设置自定义证书验证。直接上代码

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
60
61
62
63
64
65
66
67
68
69
70
71
72

/** 协议*/

private static final String X509 = "X.509";

/** 证书类型*/

private static final String PKCS12 = "PKCS12";

private static final String BC = "BC";

private static final String TRUST = "trust";





/**

* 获取固定签名的httpclient

* @return

*/

private static AsyncHttpClient getCustomHttpsClient(){

InputStream ins = null;

String result = "";

AsyncHttpClient asyncHttpClient = null;

try {

ins = WCGApplicationLike.getInstance().getApplication().getApplicationContext().getResources().openRawResource(R.raw.app);

CertificateFactory cerFactory = CertificateFactory

.getInstance(X509);

Certificate cer = cerFactory.generateCertificate(ins);

KeyStore keyStore = KeyStore.getInstance(PKCS12, BC);

keyStore.load(null, null);

keyStore.setCertificateEntry(TRUST, cer);

org.apache.http.conn.ssl.SSLSocketFactory ssl = new org.apache.http.conn.ssl.SSLSocketFactory(keyStore);

ssl.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);//记得设置主机为严格认证,这样才会起作用,证书不对时会直接返回请求失败

SchemeRegistry schReg = new SchemeRegistry();

schReg.register(new Scheme("http", ssl, 80));

schReg.register(new Scheme("https", ssl, 443));

asyncHttpClient = new AsyncHttpClient(schReg);

} catch (Exception e) {

asyncHttpClient = new AsyncHttpClient(true,80,443);

}finally {

return asyncHttpClient;

}

}

Jenkins+Gradle搭建Android持续集成编译环境

发表于 2017-02-14

什么是持续集成

持续集成是一种软件开发实践,即开发者多次的将代码集成到主干中,通过持续的编译,能够及时的发现代码库中存在的错误,并且支持测试和产品及时的取包进行测试。

集成条件

  • 一台持续集成服务器:这台服务器的任务是从代码托管服务器自动拉取最新的代码,并且进行代码的编译和打包输出app的安装包,同时发邮件提示团队的其他成员。一般情况下移动端产品都有Android和IOS两个平台,为了兼顾,会选择Max OSX系统。但本文介绍的话,选择了我自己用的Ubuntu系统,并且只搭建Android端的环境。

  • 另外就是需要一个集成工具jenkins

Jenkins的安装

Jenkins的安装有两种方法

  1. 在Jenkins的官网 下载最新的war包。直接在终端中执行下面的命令就可以了(前提安装了jdk并且配置了相关的环境变量)
1
2

cfp@cfp:~$ java -jar jenkins.war

这样Jenkins就启动了,war包中自带jetty服务器,安装现在就完成了。第一次启动Jenkins时,出于安全考虑,Jenkins会自动生成一个随机的按照口令。复制下来一会访问页面的时候需要验证。

  1. 通过tomcat部署jenkins,在Ubuntu上安装Tomcat也很简单,一行命令
1
2

cfp@cfp:~$ sudo apt-get install tomcat7

安装成功tomcat后,将第一种方法下载的jenkins的war包放到tomcat的webapps目录下就可以了,具体的位置在

1
2

/var/lib/tomcat7/webapps

下面看看安装的成果:

第一种方法访问

1
2

http://localhost:8080/

第二种方法访问

1
2

http://localhost:8080/jenkins/

在按提示验证过后即可打开jenkins的首页

Jenkins插件的安装

Jenkins构建Android Studio工程需要安装下面的插件

  • Gradle plugin: 支持执行gradle构建脚本

  • Git plugin: 代码仓库是基于git的话,用于支持Jenkins拉取远程代码。

  • SSH Credentials Plugin: SSH 证书插件,用于支持Jenkins支持本地存储SSH证书。

点击Jenkins的系统管理->插件管理->可选插件,里面可以搜到所需要的插件。

tip:如果在下载插件中出现了失败,也可以点到插件详情里,手动的下载下来,然后在插件管理->高级中手动的安装。

Jenkins全局配置

点击Jenkins首页, 系统管理->全局工具配置,可以进入jenkins的全局配置页面。这个页面中进行jdk,git和gradle的配置

Android项目构建配置

  • 在Jenkins首页“新建” -> “构建一个自由风格的软件项目”并输入Item名称

  • 这样在Jenkins首页就看到这个项目了,点击进入项目详情页,点击左侧的配置,我们用git仓库为例配置代码库,找到“源码管理” ->选择git,如果使用HTTP协议直接配置HTTP地址即可,如果使用了SSH方式访问git,需要配置Credentials,点击“Add”->jenkins, 在Kind中选择“SSH Username with private key”, Username 选择服务器主机用户,例如:root, Private Key 选择 Enter directly,将你服务器的ssh私钥复制过来,然后保存,这样在Credentials中选择该用户就可以了。

接下来配置你构建的分支

  • 构建

“添加构建步骤” -> “invoke Gradle script” -> “invoke Gradle” 选择我们在全局工具配置中配置的gradle版本

Tasks配置任务

  • 构建后操作

获取构建成功后的apk文件,然后发送邮件给相关人员

  • 点击立即构建,如果没问题的话一会就有构建成功的提示。
123<i class="fa fa-angle-right"></i>

chengfangpeng

冲出地球,移民宇宙

24 日志
8 标签
© 2019 chengfangpeng
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4