What are common design patterns in android ?, Android Design Mode
Builder Mode
The most obvious sign of the builder mode is the Build class, and the most common in Android is the construction of Dialog, and the construction of Notification is also the Standard builder mode.
The Builder mode is easy to understand. If the construction of a class requires many parameters and these parameters are not all necessary, it is more suitable for Builder in this case.
For example, to create an AlertDialog, the title, content, cancel button, OK button, and neutral button, you may only need to set several attributes separately. In my OkHttpPlus project, this is also true for constructing an Http request. You may only need to set the URL, or add request parameters and Http headers. The Builder mode is also appropriate.
Singleton Mode
Singleton is often used in Android development, but its forms may not be the same.
For system services such as ActivityManager, a single instance is implemented in the form of a static code block. When a class file is loaded for the first time, a single instance object is generated and stored in the Cache, later use is obtained directly from the Cache.
class ContextImpl extends Context { static { registerService(ACTIVITY_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); }}); }}
Of course, there are more obvious examples. For example, AccessibilityManager ensures that the singleton object is obtained by getInstance.
public static AccessibilityManager getInstance(Context context) { synchronized (sInstanceSync) { if (sInstance == null) { ...... IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); IAccessibilityManager service = iBinder == null ? null : IAccessibilityManager.Stub.asInterface(iBinder); sInstance = new AccessibilityManager(context, service, userId); } } return sInstance; }
In addition, there are some pseudo Singleton instances, such as Application. By default, only one instance exists in a process, but the Application cannot be considered a singleton because its constructor is not private, you can generate multiple Application instances, but it is useless. You didn't bind related information through attach (), and there is no context.
public Application() { super(null); }
The use case of a single instance is also very simple, that is, an App only needs to have one class instance, or the class initialization operation is resource-consuming. In many open-source frameworks, we only need one object to complete the work, such as various network frameworks and image loading libraries.
In addition, there are many implementation methods for Singleton, such as lazy, hungry, static internal, dual lock check, enumeration, etc, therefore, you must be clear about the main features and Use Cases of each implementation method.
Prototype
The prototype is not used much in development, but is reflected in the source code.
This document introduces the prototype mode with Intent, which is implemented through the Cloneable interface.
public class Intent implements Parcelable, Cloneable { @Override public Object clone() { return new Intent(this); } }
In this case, the prototype is easy to understand, that is, if you want to get an object with the same attributes faster, you can use the prototype, for example, an Intent object is obtained here. The properties in the Intent object are the same as those of the cloned object, but they are not associated and can be used independently.
Besides implementing the Cloneable interface, you can define a method to obtain an object. Here I will introduce PhoneLayoutInflater as an example.
PhoneLayoutInflater is a subclass of LayoutInflater. If we obtain LayoutInflate in the Activity, we use the following method:
@Override public Object getSystemService(String name) { if (LAYOUT_INFLATER_SERVICE.equals(name)) { if (mInflater == null) { mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); } return mInflater; } return getBaseContext().getSystemService(name); }
We can see that if it is null, cloneInContext () will be called. This method is an abstract method in LayoutInflate. The specific implementation is in PhoneLayoutInflater.
public LayoutInflater cloneInContext(Context newContext) { return new PhoneLayoutInflater(this, newContext); }
We can see that this is also a prototype, so we should not be too entangled in the form, but more importantly, understand the benefits of doing so.
In addition to the prototype in the source code, you can also see the prototype in the open-source framework. For example, the following method exists in OkHttpClient:
/** Returns a shallow copy of this OkHttpClient. */ @Override public OkHttpClient clone() { return new OkHttpClient(this); }
We can see that the implementation is exactly the same as the previous one, and it is also a new object return. Because the construction process of OkHttpClient is complicated and there are many parameters, this method is used to directly generate new objects, the cost is low, and the parameter settings of the previous object can be retained.
Factory method mode
In the book, a novel idea about the factory method mode is that Activity. onCreate () can be seen as the factory method mode to generate different View object filling interfaces.
However, I disagree with this statement for two reasons: first, this form does not conform to the factory method, is not abstract, is not implemented, is not in the general format, and is not a static method, it cannot be regarded as a static factory method. Second, it does not take the generated object as the result, that is, it is not the return view to generate the object, but the property is set through setContentView. This is like setting a background color for an Activity. Of course, there is a person's opinion on the design pattern.
The obvious example of the static factory method in Android is BitmapFactory. Bitmap objects can be obtained from different channels through various decodeXXX.
Rule Mode
In the book, The rule mode is very good. Combined with the use of the animation interpolation tool, we can understand the form and usage of the Rule mode.
In my opinion, the Strategy Mode is equivalent to a DVD player. You can insert any plate into it to release any movies.
Similarly, in the OkHttpPlus encapsulation, I used the policy mode to parse the network return value. Of course, when I write code, I still don't know the policy mode. After I write the code, I suddenly think that this is the policy mode!
The essence of the policy mode is that you pass in a class, and the subsequent processing can follow the implementation of this class. Taking animation as an example, you can set different interpolation objects to get different change curves. Taking the returned value parsing as an example, what type of parser is input, you can convert binary data into data in what format, such as String, Json, and XML.
Responsibility Chain Model
In the book, the example of selecting the responsibility chain mode is very representative, that is, the Android touch mechanism, which allows me to understand the touch event transfer in Android from another dimension.
I mentioned this mode here. I don't want to talk too much about it. I just recommend you read the content of this chapter. I believe you will gain some benefits.
Observer Mode
The observer mode in Android should be a very frequent mode. For this mode, you can use the following sentence: You want to receive a notification immediately when an object changes.
The book introduces the observer mode using the ListView Adapter as an example. I previously learned that the Adapter belongs to the Adapter mode and that there is also the observer mode here.
Various listeners in Android also belong to the observer mode, such as touch, click, and button. The ContentProvider and broadcast receiver also have the observer mode, which can be said to be ubiquitous.
In addition, many third-party frameworks based on the observer mode, such as EventBus and RxJava, are used in depth for the observer mode. If you are interested, you can study it.
Template Method Mode
This mode is rarely seen before, but after understanding it, it will be found that this mode is very simple.
In my opinion, the use of the template method mode is also a sentence: the process is determined, and the specific implementation details are completed by sub-classes.
Here, let's take a look at the keyword "process". Any abstract class meets the requirement of "specific implementation details are completed by sub-classes". The key is whether there is a process or a process, it is called the template method mode. There is no process, that is, the implementation of abstract classes.
In the book, this mode uses AsyncTask, And the execution process between methods is consistent. The specific implementation is done by us, which is very classic.
In addition, the life cycle method of an Activity can be seen as the template method mode. Each Life Cycle method has a sequence. The specific implementation can be rewritten. Is it very consistent with the previous requirements? For more information, see my article: Understanding the lifecycle of an Activity.
In addition to the template method mode in Android, this mode is also used in other open source projects. For example, the OkHttp-Utils project of Hongyang is a typical Implementation of the template method mode. The process of an Http request is divided into several parts, such as the steps for obtaining the URL, obtaining the request header, and splicing the request information. These steps are sequential before, so we can do this.
Agent Mode and decorator Mode
The reason for putting the two together is that the two models are very similar, so here we will briefly introduce the differences between them, mainly including two points.
These two sentences may not be easy to understand. It doesn't matter. Let's take a look at the example below.
The proxy mode holds the instance of the proxy object, and this instance is generally directly stored in the proxy class as a member variable, that is, no additional value assignment is required.
For example, WindowManagerImpl is a proxy class. Although its name does not look like it, it acts as a proxy for WindowManagerGlobal objects. We can see from the following code.
public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); private final Display mDisplay; private final Window mParentWindow; ...... @Override public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); } @Override public void updateViewLayout(View view, ViewGroup.LayoutParams params) { mGlobal.updateViewLayout(view, params); } @Override public void removeView(View view) { mGlobal.removeView(view, false); } @Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); } @Override public Display getDefaultDisplay() { return mDisplay; }}
From the code above, we can see that most WindowManagerImpl methods are implemented through WindowManagerGlobal, and WindowManagerGlobal objects do not need to be assigned any value, so they exist in WindowManagerImpl. In addition, WindowManagerGlobal has a large number of methods, but it is not exposed after being passed through the WindowManagerImpl proxy, which is transparent to developers.
Let's take a look at the decoration device mode. The purpose of the decorator mode is not to control access, but to expand the function. Compared with inheriting the base class to expand the function, the decorator mode is more flexible.
The book is explained by Context and its packaging class ContextWrapper, which is also very typical. I will not repeat it here, and I will post some code to illustrate the form of the decorator mode.
public class ContextWrapper extends Context { Context mBase; public ContextWrapper(Context base) { mBase = base; }}
However, there is another problem: In ContextWrapper, the implementation of all methods is implemented through mBase, and the form is correct. What about the extended functions?
Function extensions are actually in ContextThemeWrapper, a subclass of ContextWrapper.
In ContextWrapper, obtaining system services is done directly through mBase.
@Override public Object getSystemService(String name) { return mBase.getSystemService(name); }
However, in ContextThemeWrapper, this method is rewritten to complete function expansion.
@Override public Object getSystemService(String name) { if (LAYOUT_INFLATER_SERVICE.equals(name)) { if (mInflater == null) { mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); } return mInflater; } return getBaseContext().getSystemService(name); }
Of course, if there is no function extension, isn't it the decorator mode? In fact, the design pattern is a matter of saying "benevolent sees benevolence, wise sees wisdom", as long as you can understand this meaning.
Appearance Mode
The appearance mode may be relatively small, but you actually use it inadvertently.
Here, let's talk about KLog, an open-source project of mine. When I first wrote this class, only KLog was used to complete the basic Log printing function, later, I added functions such as JSON parsing, XML parsing, and Log information storage. At this time, a class is not suitable, so I extracted the Code related to JSON, XML, and FILE operations into a separate class, such as the code printed in JSON.
public class JsonLog { public static void printJson(String tag, String msg, String headString) { String message; try { if (msg.startsWith("{")) { JSONObject jsonObject = new JSONObject(msg); message = jsonObject.toString(KLog.JSON_INDENT); } else if (msg.startsWith("[")) { JSONArray jsonArray = new JSONArray(msg); message = jsonArray.toString(KLog.JSON_INDENT); } else { message = msg; } } catch (JSONException e) { message = msg; } Util.printLine(tag, true); message = headString + KLog.LINE_SEPARATOR + message; String[] lines = message.split(KLog.LINE_SEPARATOR); for (String line : lines) { Log.d(tag, "║ " + line); } Util.printLine(tag, false); }}
The code is very simple. It is just a method. However, no matter which format is printed, it is used in this way.
// Print common KLog. d (LOG_MSG); // print KLog. JSON (json) in JSON format; // print KLog. XML (xml) in XML format );
We can see that although the functions are different, they are all encapsulated by the KLog class. You only know that the KLog class can meet all requirements, you do not need to know that code implementation is completed by several classes.
In fact, in KLog, multiple classes work together to complete the printing function.
private static void printLog(int type, String tagStr, Object... objects) { if (!IS_SHOW_LOG) { return; } String[] contents = wrapperContent(tagStr, objects); String tag = contents[0]; String msg = contents[1]; String headString = contents[2]; switch (type) { case V: case D: case I: case W: case E: case A: BaseLog.printDefault(type, tag, headString + msg); break; case JSON: JsonLog.printJson(tag, msg, headString); break; case XML: XmlLog.printXml(tag, msg, headString); break; } }
However, in the appearance mode, these details are hidden from users. In this way, if I want to change the JSON parsing mode in the future, the user's Code does not need to be changed. This is also the advantage of this design mode.
Summary
After reading this article, you will find that the design patterns in Android do exist everywhere, instead, they lack a pair of discovery eyes.
Of course, the design model is proposed to solve specific problems. When we encounter similar problems, we can think about and solve the problems from the perspective of the design model. This should be my biggest achievement.