工廠模式和抽象工廠模式以及在Android中的應用
《Android原始碼設計模式解析與實戰》第5章第6章讀書筆記
工廠方法模式介紹
工廠方法模式(Factory Pattern
)建立型設計模式之一,在平時開發中或多或少都會使用它,如Android
中的Activity
裡的各個生命週期方法,以onCreate
方法為例,它就可以看作是一個工廠方法,在其中我們將構造的View
通過setContentView
返回給framework
處理,剩下的佈局就由系統去建立。總結來說,定義一個用於建立物件的介面,讓子類決定例項化哪個類
。它的使用場景是任何需要生成複雜物件的地方,都可以使用工廠方法模式。複雜物件使用工廠模式,用new就可以完成建立的物件無需使用工廠模式。
工廠方法模式uml類圖
工廠模式通用模板程式碼:
abstract class Product{
/**
* 抽象的產品方法,具體方法由子類去實現
*/
abstract fun method()
}
class ConcreteProductA : Product(){
override fun method() {
println("具體產品類A")
}
}
class ConcreteProductB : Product(){
override fun method() {
println("具體產品類B" )
}
}
/**
* 抽象工廠類,生產什麼產品由子類去實現
*/
abstract class Factory{
abstract fun createProduct(): Product
}
class ConcreteFactory: Factory(){
override fun createProduct(): Product = ConcreteProductA()
}
//Test
@Test
fun demo1(){
val factory = ConcreteFactory()
val product = factory.createProduct()
product.method() //具體產品類A
}
上述程式碼中構造的工廠物件,通過其生產的產品物件,得到的產品物件是ConcreteProductA
,如果想得到產品物件B,直接更換就好了。
還有一種比較常見的是利用反射更加簡潔的生產具體物件
/**
* 反射版本
*/
abstract class FactoryWithReflect{
abstract fun <T:Product> createProduct(clz: Class<T>): T?
}
class ConcreteFactoryWithReflect: FactoryWithReflect(){
override fun <T : Product> createProduct(clz: Class<T>): T? {
var product: T? = null
try {
product = Class.forName(clz.name).newInstance() as T
}catch (e:Exception){
e.printStackTrace()
}
return product
}
}
//test
@Test
fun demo2(){
val factory = ConcreteFactoryWithReflect()
val product = factory.createProduct(ConcreteProductA::class.java)
product?.method()
}
根據第二版本看出,想要什麼樣的例項,只需要傳入對應的class
就可以了,這種方法比較簡潔,動態。當然也可以為每一個產品建立一個具體的工廠。
工廠方法模式在android中的應用
拿在本文開頭onCreate
舉例,它是Activity
的一個入口點,接下來就看下如何在Android系統中呼叫的onCreate
方法。對於一個應用程式來說,其真正的入口是ActivityThread
類裡面的main
方法,ActivityThread
是一個final
類,不能被繼承,當Zygote程序孵化出一個新的應用程序之後,會執行ActivityThread
的main
方法。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
main
方法裡面做了一些常規邏輯,比如準備Looper
和訊息佇列,然後呼叫ActivityThread
的attach
方法將其繫結到ActivityManagerService
中,接著就是不斷的讀取訊息佇列中的訊息並分發訊息(事件驅動模型)
看下attach
方法
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
...
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}
...
}
通過system來區分是系統
還是普通
應用,我們只看普通應用,ActivityManager.getService()
返回的就是一個AMS
物件,由於AMS
是系統服務,所以它們之間需要程序通訊,接著呼叫attachApplication
,來將mAppThread
交給AMS
處理,看下attachApplication
方法
//AMS
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
接著轉向呼叫attachApplicationLocked
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
try {
...
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial); //1
...
} catch (Exception e) {
...
return false;
}
...
mProcessesOnHold.remove(app);
boolean badApp = false;
boolean didSomething = false;
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) { //2
didSomething = true;
}
} catch (Exception e) {
badApp = true;
}
}
...
return true;
}
這個方法邏輯很長,簡化了不必要的邏輯之後,只剩下兩個主要的方法bindApplication
和 attachApplicationLocked
,第一個方法引數很多,如其名字一樣,將ApplicationThread
物件繫結到AMS
中。而第二個方法mStackSupervisor指向一個ActivityStackSupervisor
,而attachApplicationLocked方法描述如下
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack)) {
continue;
}
stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
final ActivityRecord top = stack.topRunningActivityLocked();
final int size = mTmpActivityList.size();
for (int i = 0; i < size; i++) {
final ActivityRecord activity = mTmpActivityList.get(i);
if (activity.app == null && app.uid == activity.info.applicationInfo.uid
&& processName.equals(activity.processName)) {
try {
if (realStartActivityLocked(activity, app,
top == activity /* andResume */, true /* checkConfig */)) {
didSomething = true;
}
} catch (RemoteException e) {
...
throw e;
}
}
}
}
}
if (!didSomething) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
return didSomething;
}
方法的程式碼比較長,而主要邏輯是
if (realStartActivityLocked(activity, app, top == activity /*andResume */, true /* checkConfig */)) {
didSomething = true;
}
這裡就是真正啟動Activity的邏輯
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
...
//所有引數資訊準備好之後,就可以真正啟動Activity了
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, !andResume,
mService.isNextTransitionForward(), profilerInfo);
...
}
這個方法中會準備Activity
的引數資訊,準備完畢之後呼叫ApplicationThread
的scheduleLaunchActivity
方法啟動Activty
,
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
//將引數封裝到一個ActivityClientRecord物件中
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
這個方法中首先構造一個ActivityClientRecord
物件,設定相關引數,最後通過sendMessage
方法傳送一個啟動Activity
的訊息,由ActivityThread
的Handler
啟動,在ActivityThread
中維護了一個Handler
的例項H
,看下它是怎麼處理H.LAUNCH_ACTIVITY
這樣的標誌訊息的
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
...
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
} break;
}
}
它會呼叫handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
方法,而在這個方法中會呼叫performLaunchActivity
方法,這個方法裡面是處理具體Activity
的啟動邏輯
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//獲取ActivityInfo
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
//獲取packageInfo
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
//獲取ComponentName
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
ContextImpl appContext = createBaseContextForActivity(r);
//通過Instrumentation構造Activity物件並設定引數
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
//獲取Application物件
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
...
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
//構造window物件
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
//將相關引數繫結到activity
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
//呼叫Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); //1
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
return activity;
}
註釋一處會呼叫callActivityOnCreate
方法
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
而在performCreate
中最終會呼叫onCreate方法
final void performCreate(Bundle icicle) {
performCreate(icicle, null);
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
//最終執行onCreate方法的呼叫
onCreate(icicle);
}
...
}
到這裡,基本就明白了整個Activity
的啟動流程到onCreate
方法的呼叫,而對於外部呼叫者來說,裡面做的什麼操作基本都是不知道的,只是提供onCreate
這一系列生命週期方法,這就是典型的工廠模式設計模式
為了看得更清楚,畫了一個時序圖
,方便理解
總結來說,工廠設計模式可以簡化類的建立過程,但是也導致類的結構複雜化了,所以是否要選擇工廠設計模式,需要權衡利弊了。
抽象工廠模式介紹
抽象工廠模式(Abstract Factory Pattern
)也是建立型設計模式之一,起源於以前對不同作業系統圖形化解決方案,如不同作業系統對按鈕和文字控制元件的實現不同,展示效果也不同。
定義:為建立一組相關或者是相互依賴的物件提供一個介面,而不需要指定它們具體的類。
使用場景:一個物件族有相同的約束時可以使用抽象工廠模式。比如Android,IOS,Window Phone下都有簡訊軟體和撥號軟體,兩者都是相同的功能,但是在不同作業系統平臺下,其程式碼的實現邏輯也是不相同的,這時候就可以採用抽象工廠模式。還有一個就是換面板功能,一整套一起換
抽象工廠模式uml類圖
抽象工廠模式的模板程式碼
/**
* 抽象產品A
*/
abstract class AbstractProductA{
/**
* 抽象方法
*/
abstract fun method()
}
/**
* 具體產品A1
*/
class ConcreteProductA1: AbstractProductA(){
override fun method() {
println("具體產品A1")
}
}
/**
* 具體產品A2
*/
class ConcreteProductA2: AbstractProductA(){
override fun method() {
println("具體產品A2")
}
}
/**
* 抽象產品B
*/
abstract class AbstractProductB{
/**
* 抽象方法
*/
abstract fun method()
}
/**
* 具體產品B1
*/
class ConcreteProductB1: AbstractProductB(){
override fun method() {
println("具體產品B1")
}
}
/**
* 具體產品B2
*/
class ConcreteProductB2: AbstractProductB(){
override fun method() {
println("具體產品B2")
}
}
/**
* 抽象工廠
*/
abstract class AbstractFactory{
/**
* 生產產品A
*/
abstract fun createProductA(): AbstractProductA
/**
* 生產產品B
*/
abstract fun createProductB(): AbstractProductB
}
/**
* 具體工廠1
*/
class ConcreteFactory1: AbstractFactory(){
override fun createProductA(): AbstractProductA = ConcreteProductA1()
override fun createProductB(): AbstractProductB = ConcreteProductB1()
}
/**
* 具體工廠2
*/
class ConcreteFactory2: AbstractFactory(){
override fun createProductA(): AbstractProductA = ConcreteProductA2()
override fun createProductB(): AbstractProductB = ConcreteProductB2()
}
角色介紹:
AbstractProduct: 抽象產品角色,它為每種產品申明介面
ConcreteProduct:具體產品角色,它定義具體工廠生產的具體產品物件。
AbstractFactory:抽象工廠,負責宣告建立一種產品的方法。
ConcreteFactory:具體工廠,實現了抽象工廠中建立產品的方法。
抽象工廠模式在android中的應用
在平時的開發過程中很少用到抽象工廠模式,一個重要原因就是略顯複雜,對於Android
開發者來說,一個比較適合的應用就是主題的修改,下面就模擬一套亮色主題LightTheme
和暗色主題DarkTheme
,而在這兩種主題下有各自的UI
元素,這種時候就可以使用抽象工廠模式
/**
* 抽象主題按鈕
*/
abstract class ThemeButton(context: Context) : Button(context){
init {
initTextColor()
}
/**
* 初始化文字顏色
*/
abstract fun initTextColor()
}
/**
* 暗色按鈕
*/
class DarkButton(context: Context): ThemeButton(context){
override fun initTextColor() {
setTextColor(R.color.dartColor)
}
}
/**
* 亮色按鈕
*/
class LightButton(context: Context): ThemeButton(context){
override fun initTextColor() {
setTextColor(R.color.lightColor)
}
}
/**
* 抽象主題工廠類
*/
abstract class AbstractThemeFactory(val context: Context){
abstract fun createButton(): ThemeButton
}
/**
* 暗色按鈕工廠
*/
class DarkThemeFactory(context: Context): AbstractThemeFactory(context){
override fun createButton(): ThemeButton = DarkButton(context)
}
/**
* 亮色按鈕工廠
*/
class LightThemeFactory(context: Context): AbstractThemeFactory(context){
override fun createButton(): ThemeButton = LightButton(context)
}
//Test
*/
@RunWith(AndroidJUnit4::class)
class AbstractFactoryPatterThemeTest {
@Test
fun test() {
val factoryDark = DarkThemeFactory(InstrumentationRegistry.getTargetContext())
println("暗色:"+factoryDark.createButton())
val factoryLight = LightThemeFactory(InstrumentationRegistry.getTargetContext())
println("亮色:"+factoryLight.createButton())
}
}
result:
暗色:com.microcity.myapplication.DarkButton{8cd843f VFED..C.. ......I. 0,0-0,0}
亮色:com.microcity.myapplication.LightButton{5495f0d VFED..C.. ......I. 0,0-0,0}
在上面的例子中定義了一個主題按鈕ThemeButton
抽象類,接著實現了DarkButton
暗色和LightButton
亮色兩種按鈕。然後定義了一個生產ThemeButton
的工廠類,接著實現了DarkThemeFactory
和LightThemeFactory
兩種主題的工廠類,分別用來實現不同主題的ThemeButton
,這就是大致的邏輯。
注意:雖然這種模式簡化了類之間的分工合作,但是無疑增加了大量的類,是否要使用這種設計模式,還是要攢橫下利弊的,因為如果新增加了一個UI控制元件的時候,那麼抽象的類都需要修改,並且具體的實現類也需要修改,這樣修改起來還是很麻煩的。
抽象工廠模式的優缺點
- 優點:
分離介面與實現,客戶端使用抽象工廠來建立需要的物件,而不需要知道具體的實現是誰,只是面向產品的介面程式設計,使其從具體的產品實現中解耦,同時基於介面與實現的分離,使抽象該工廠方法模式在切換產品類時候更加靈活,容易。
- 缺點:
第一個就是類檔案的爆炸性增加,二是不容易擴充套件新的產品類,每當增加一個產品類就需要修改抽象工廠,那麼所有具體的工廠類均會被修改。
參考
1 .《Android原始碼設計模式解析與實戰》
2 . 抽象工廠模式