Android开发中常见的设计模式

Posted 安卓大神

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发中常见的设计模式相关的知识,希望对你有一定的参考价值。

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次。而在android开发中,必要的了解一些设计模式又是非常有必要的。对于想系统的学习设计模式的同学,这里推荐2本书。一本是Head First系列的Head Hirst Design Pattern,英文好的可以看英文,可以多读几遍。另外一本是大话设计模式。

单例模式

首先了解一些单例模式的概念。

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

这样做有以下几个优点

  • 对于那些比较耗内存的类,只实例化一次可以大大提高性能,尤其是在移动开发中。
  • 保持程序运行的时候该中始终只有一个实例存在内存中

其实单例有很多种实现方式,但是个人比较倾向于其中1种。可以见单例模式

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
private static volatile Singleton instance = null;

private Singleton(){
}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

要保证单例,需要做一下几步

  • 必须防止外部可以调用构造函数进行实例化,因此构造函数必须私有化。
  • 必须定义一个静态函数获得该单例
  • 单例使用volatile修饰
  • 使用synchronized 进行同步处理,并且双重判断是否为null,我们看到synchronized (Singleton.class)里面又进行了是否为null的判断,这是因为一个线程进入了该代码,如果另一个线程在等待,这时候前一个线程创建了一个实例出来完毕后,另一个线程获得锁进入该同步代码,实例已经存在,没必要再次创建,因此这个判断是否是null还是必须的。

至于单例的并发测试,可以使用CountDownLatch,使用await()等待锁释放,使用countDown()释放锁从而达到并发的效果。可以见下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(1);
int threadCount = 1000;
for (int i = 0; i < threadCount; i++) {
new Thread() {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Singleton.getInstance().hashCode());
}
}.start();
}
latch.countDown();
}

看看打印出来的hashCode会不会出现不一样即可,理论上是全部都一样的。

而在Android中,很多地方用到了单例。

比如Android-Universal-Image-Loader中的单例

1
2
3
4
5
6
7
8
9
10
11
12
private volatile static ImageLoader instance;
/** Returns singleton class instance */
public static ImageLoader getInstance() {
if (instance == null) {
synchronized (ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}

比如EventBus中的单例

1
2
3
4
5
6
7
8
9
10
11
private static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}

上面的单例都是比较规规矩矩的,当然实际上有很多单例都是变了一个样子,单本质还是单例。

如InputMethodManager 中的单例

1
2
3
4
5
6
7
8
9
10
11
static InputMethodManager sInstance;
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
return sInstance;
}
}

AccessibilityManager 中的单例,看代码这么长,其实就是进行了一些判断,还是一个单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static AccessibilityManager sInstance;
public static AccessibilityManager getInstance(Context context) {
synchronized (sInstanceSync) {
if (sInstance == null) {
final int userId;
if (Binder.getCallingUid() == Process.SYSTEM_UID
|| context.checkCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS)
== PackageManager.PERMISSION_GRANTED
|| context.checkCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL)
== PackageManager.PERMISSION_GRANTED) {
userId = UserHandle.USER_CURRENT;
} else {
userId = UserHandle.myUserId();
}
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
sInstance = new AccessibilityManager(context, service, userId);
}
}
return sInstance;
}

当然单例还有很多种写法,比如恶汉式,有兴趣的自己去了解就好了。

最后,我们应用一下单例模式。典型的一个应用就是管理我们的Activity,下面这个可以作为一个工具类,代码也很简单,也不做什么解释了。

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
public class ActivityManager {

private static volatile ActivityManager instance;
private Stack<Activity> mActivityStack = new Stack<Activity>();

private ActivityManager(){

}

public static ActivityManager getInstance(){
if (instance == null) {
synchronized (ActivityManager.class) {
if (instance == null) {
instance = new ActivityManager();
}
}
return instance;
}

public void addActicity(Activity act){
mActivityStack.push(act);
}

public void removeActivity(Activity act){
mActivityStack.remove(act);
}

public void killMyProcess(){
int nCount = mActivityStack.size();
for (int i = nCount - 1; i >= 0; i--) {
Activity activity = mActivityStack.get(i);
activity.finish();
}

mActivityStack.clear();
android.os.Process.killProcess(android.os.Process.myPid());
}
}

这个类可以在开源中国的几个客户端中找到类似的源码

以上两个类是一样的,没区别。

Build模式

了解了单例模式,接下来介绍另一个常见的模式——Builder模式。

那么什么是Builder模式呢。你通过搜索,会发现大部分网上的定义都是

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

但是看完这个定义,并没有什么卵用,你依然不知道什么是Builder设计模式。在此个人的态度是学习设计模式这种东西,不要过度在意其定义,定义往往是比较抽象的,学习它最好的例子就是通过样例代码。

我们通过一个例子来引出Builder模式。假设有一个Person类,我们通过该Person类来构建一大批人,这个Person类里有很多属性,最常见的比如name,age,weight,height等等,并且我们允许这些值不被设置,也就是允许为null,该类的定义如下。

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
public class Person {
private String name;
private int age;
private double height;
private double weight;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public double getHeight() {
return height;
}

public void setHeight(double height) {
this.height = height;
}

public double getWeight() {
return weight;
}

public void setWeight(double weight) {
this.weight = weight;
}
}

然后我们为了方便可能会定义一个构造方法。

1
2
3
4
5
6
public Person(String name, int age, double height, double weight) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}

或许为了方便new对象,你还会定义一个空的构造方法

1
2
public Person() {
}

甚至有时候你很懒,只想传部分参数,你还会定义如下类似的构造方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Person(String name) {
this.name = name;
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}

于是你就可以这样创建各个需要的对象

1
2
3
4
5
Person p1=new Person();
Person p2=new Person("张三");
Person p3=new Person("李四",18);
Person p4=new Person("王五",21,180);
Person p5=new Person("赵六",17,170,65.4);

可以想象一下这样创建的坏处,最直观的就是四个参数的构造函数的最后面的两个参数到底是什么意思,可读性不怎么好,如果不点击看源码,鬼知道哪个是weight哪个是height。还有一个问题就是当有很多参数时,编写这个构造函数就会显得异常麻烦,这时候如果换一个角度,试试Builder模式,你会发现代码的可读性一下子就上去了。

我们给Person增加一个静态内部类Builder类,并修改Person类的构造函数,代码如下。

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
public class Person {
private String name;
private int age;
private double height;
private double weight;

privatePerson(Builder builder) {
this.name=builder.name;
this.age=builder.age;
this.height=builder.height;
this.weight=builder.weight;
}
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public double getHeight() {
return height;
}

public void setHeight(double height) {
this.height = height;
}

public double getWeight() {
return weight;
}

public void setWeight(double weight) {
this.weight = weight;
}

static class Builder{
private String name;
private int age;
private double height;
private double weight;
public Builder name(String name){
this.name=name;
return this;
}
public Builder age(int age){
this.age=age;
return this;
}
public Builder height(double height){
this.height=height;
return this;
}

public Builder weight(double weight){
this.weight=weight;
return this;
}

public Person build(){
return new Person(this);
}
}
}

从上面的代码中我们可以看到,我们在Builder类里定义了一份与Person类一模一样的变量,通过一系列的成员函数进行设置属性值,但是返回值都是this,也就是都是Builder对象,最后提供了一个build函数用于创建Person对象,返回的是Person对象,对应的构造函数在Person类中进行定义,也就是构造函数的入参是Builder对象,然后依次对自己的成员变量进行赋值,对应的值都是Builder对象中的值。此外Builder类中的成员函数返回Builder对象自身的另一个作用就是让它支持链式调用,使代码可读性大大增强。

于是我们就可以这样创建Person类。

1
2
3
4
5
6
7
Person.Builder builder=new Person.Builder();
Person person=builder
.name("张三")
.age(18)
.height(178.5)
.weight(67.4)
.build();

有没有觉得创建过程一下子就变得那么清晰了。对应的值是什么属性一目了然,可读性大大增强。

其实在Android中, Builder模式也是被大量的运用。比如常见的对话框的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AlertDialog.Builder builder=new AlertDialog.Builder(this);
AlertDialog dialog=builder.setTitle("标题")
.setIcon(android.R.drawable.ic_dialog_alert)
.setView(R.layout.myview)
.setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
})
.setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
})
.create();
dialog.show();

其实在java中有两个常见的类也是Builder模式,那就是StringBuilder和StringBuffer,只不过其实现过程简化了一点罢了。

我们再找找Builder模式在各个框架中的应用。

如Gson中的GsonBuilder,代码太长了,就不贴了,有兴趣自己去看源码,这里只贴出其Builder的使用方法。

1
2
3
4
5
6
GsonBuilder builder=new GsonBuilder();
Gson gson=builder.setPrettyPrinting()
.disablehtmlEscaping()
.generateNonExecutableJson()
.serializeNulls()
.create();

还有EventBus中也有一个Builder,只不过这个Builder外部访问不到而已,因为它的构造函数不是public的,但是你可以在EventBus这个类中看到他的应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static EventBusBuilder builder以上是关于Android开发中常见的设计模式的主要内容,如果未能解决你的问题,请参考以下文章

Android开发中常见的设计模式——单例模式

Android开发——UI_片段

是否有在单个活动中处理多个片段的 Android 设计模式?

Android 实用代码片段

Android 实用代码片段

Android四种常见设计模式说明

(c)2006-2024 SYSTEM All Rights Reserved IT常识