隱藏在 SDK 中的單例類模板
阿新 • • 發佈:2019-01-04
原始碼分享,如需轉載,請註明作者:Yuloran (t.cn/EGU6c76)
前言
單例類寫法,網上有諸多介紹,此處不再贅述。不過仍有一點需要注意一下:從程式設計規範角度來講,單例類應當是不可繼承和構造器私有的,即:
public final class ActivityMgrService
{
private ActivityMgrService()
{
}
}
複製程式碼
Kotlin 在這方面做的很好,對程式設計安全做了很多優化。比如,類預設不可繼承,如果需要被繼承,需顯式使用 open
關鍵字。
單例模板
雖然單例寫法很多,但是同一個專案不可能允許那麼多的單例類寫法同時存在。所以,我們需要一個單例模板。其實,Android SDK 早就為我們提供了這樣一個模板,可能很多人不知道,因為它是 @hide
Singleton 原始碼
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
/**
* Singleton helper class for lazily initialization.
*
* Modeled after frameworks/base/include/utils/Singleton.h
*
* @hide
*/
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
複製程式碼
在 Android Studio 中雙擊 Shift,輸入 Singleton,即可檢索到此類。因為是 @hide
,所以需要手動複製到自己的專案中。
使用示例
public final class ActivityMgrService
{
private static final String TAG = "ActivityMgrService";
private static final Singleton<ActivityMgrService> INSTANCE = new Singleton<ActivityMgrService>()
{
@Override
protected ActivityMgrService create()
{
return new ActivityMgrService();
}
};
private ActivityMgrService()
{
}
public static ActivityMgrService getInstance()
{
return INSTANCE.get();
}
...
}
複製程式碼
ActivityMgrService 是筆者寫的一個監控專案中所有 Activity 生命週期的工具類,對此類感興趣可以點選連結自行獲取。
Live Template
可以新建一個 Live Template 來一鍵生成以上程式碼:
1. 新建 Template Group
2. 新建 Template
3. 手動編寫 Template
下面是筆者的單例生成模板:
private static final Singleton<$class$> INSTANCE = new Singleton<$class$>() {
@Override
protected $class$ create() {
return new $class$();
}
};
private $class$() {
}
public static $class$ getInstance() {
return INSTANCE.get();
}
複製程式碼
模板變數配置:
4. 匯入 Template
也可以直接匯入筆者的單例模板:
<template name="sigl" value="private static final Singleton<$class$> INSTANCE = new Singleton<$class$>() { @Override protected $class$ create() { return new $class$(); } }; private $class$() { } public static $class$ getInstance() { return INSTANCE.get(); }" description="singleton" toReformat="true" toShortenFQNames="true">
<variable name="class" expression="className()" defaultValue="" alwaysStopAt="false" />
<context>
<option name="JAVA_CODE" value="true" />
</context>
</template>
複製程式碼
複製以上程式碼,在 Template Group 上右鍵貼上即可:
單例模板擴充套件
SDK 原始碼提供的單例不帶引數,所以我們可以自行擴充套件一個帶引數的單例模板:
/*
* Copyright (C) 2018 Yuloran(https://github.com/Yuloran)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yuloran.lib_core.template;
/**
* [帶一個引數的單例模板類]
* <p>
* Author: Yuloran
* Date Added: 2018/12/16 12:13
*
* @since 1.0.0
*/
public abstract class Singleton1<T, P>
{
private T mInstance;
protected abstract T create(P arg);
public final T get(P arg)
{
synchronized (this)
{
if (mInstance == null)
{
mInstance = create(arg);
}
return mInstance;
}
}
}
複製程式碼
用法與 Singleton 類似,不再介紹。
單例寫法之 CAS
我們可以藉助 AtomicReference 的 compareAndSet() 來實現單例:
package com.yuloran.lib_core.template;
import java.util.concurrent.atomic.AtomicReference;
/**
* [CAS 實現的單例類]
* <p>
* Author: Yuloran
* Date Added: 2019/1/3 12:13
*
* @since 1.0.0
*/
public final class SingletonCAS
{
private static final AtomicReference<SingletonCAS> INSTANCE = new AtomicReference<>();
private SingletonCAS()
{
}
public static SingletonCAS getInstance()
{
for (; ; )
{
SingletonCAS instance = INSTANCE.get();
if (instance != null)
{
return instance;
}
// 多執行緒併發訪問時,此處可能建立多個例項
instance = new SingletonCAS();
if (INSTANCE.compareAndSet(null, instance))
{
return instance;
}
}
}
}
複製程式碼
這種寫法可以保證單例,但是有個致命的問題:for 迴圈中可能建立多個例項!
結語
所以還是使用單例模板類比較好,即安全又省事。如果你是 Kotlin 開發者,你也可以使用 object 或 companion 實現單例。