談起設計模式估計大家都不會陌生,一個項目中至少會用到其中的一種模式,今天要說的主角就是單列,我了大致總結了它的幾種用法同時也結合了Android的源碼進行單列的分析;
好了正題開始了,其實個人總結了下自我學習的方法,在學習任何一個新的事物的時候,不能盲目的去干,而應適當的采取一定的技巧性東西,OK;
我大致分了三大步:
1:要知道這個東西是個什么玩意,這個東西有啥用,一般用在啥地方;
2:這個東西該怎么用了,我平時有沒有遇到類似的用法;
3:熟悉了用法之后,總結下為什么別人那樣去寫,這樣寫的優缺點是什么,我能不能仿寫下或者能不能改寫下別人的代碼,進行深度的總結下,然后用于到實踐中,記住,看完了,千萬不要就丟掉了,東西太多了,也許今天記住了,明天就會忘記,所以最好寫幾個案列實踐下;實踐是檢驗真理的位移標準,不是嗎;
1: 單列模式的定義以及應用場景
1.1 定義:
確保這個類在內存中只會存在一個對象,而且自行實例化并向整個系統提供這個實例;
1.2 場景:
一般創建一個對象需要消耗過多的資源,如:訪問I0和數據庫等資源或者有很多個地方都用到了這個實例;
2:單列模式的幾種基本寫法:
2.1 最常見的寫法:(懶漢式和餓漢式)
2.1.1 餓漢式
public class Singleton {
private static final Singleton INSTANCE=new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return INSTANCE;
}
}
2.1.1 懶漢式
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
上邊的兩種是最常見的,顧名思義懶漢式和餓漢式,一個是拿時間換空間,一個是拿空間換時間,懶漢式只有我需要他的時候才去加載它,懶加載機制,餓漢式不管需不需要我先加載了再說,先在內存中開辟一塊空間,占用一塊地方,等用到了直接就拿來用.這兩種是最基本的單列模式,
2.1.2.1懶漢式:
懶漢式缺點:效率低,第一次加載需要實例化,反應稍慢。每次調用getInstance方法都會進行同步,消耗不必要的資源。
2.1.1.1餓漢式:
缺點:不需要的時候就加載了,造成資源浪費.
2.2 雙重檢查單列(DCL實現單列)
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
這種寫法估計是我們在開發中最常用的,這次代碼的亮點是是在getInstance()方法中進行了雙重的判斷,第一層判斷的主要避免了不必要的同步,第二層判斷是為了在null的情況下再去創建實例;舉個簡單的列子:假如現在有多個線程同時觸發這個方法: 線程A執行到nstance = new Singleton(),它大致的做了三件事:
(1):給Singleton實例分配內存,將函數壓棧,并且申明變量類型;
(2):初始化構造函數以及里面的字段,在堆內存開辟空間;
(3):將instance對象指向分配的內存空間;
這種寫法也并不是保證完全100%的可靠,由于java編譯器允許執行無序,并且jdk1.5之前的jvm(java內存模型)中的Cache,寄存器到主內存的回寫順序規定,第二個和第三個執行是無法保證按順序執行的,也就是說有可能1-2-3也有可能是1-3-2; 這時假如有A和B兩條線程,A線程執行到3的步驟,但是未執行2,這時候B線程來了搶了權限,直接取走instance這時候就有可能報錯,同時我也放了一張內存模型,幫助大家更好的去理解,如圖;
簡單總結就是說jdk1.5之前會造成兩個問題
1:線程間共享變量不可見性;
2:無序性(執行順序無法保證);
當然這個bug已經修復了,SUN官方調整了JVM,具體了Volatile關鍵字,因此在jdk1.5之前只需要寫成這樣既可, private Volatitle static Singleton instance; 這樣就可以保證每次都是從主內存中取,當然這樣寫或多或少的回影響性能,但是為了安全起見,這點性能犧牲還是值得;
雙重檢查單列(DCL)
優點:資源利用率高,第一次執行方法是單列對象才會被實例化;
缺點:第一次加載時會稍慢,jdk1.5之之前有可能會加載會失敗;
Android常用的框架:Eventbus(DCL雙重檢查)
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
2.3 靜態內部內實現單列
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
這種方式不僅確保了線程的安全性,也能夠保證對象的唯一性,同時也是延遲加載,很多技術大牛也是這樣推薦書寫;
2.4 枚舉實現單列
public enum SingletonEnum {
INSTANCE;
public void doSomething() {
}
}
優點:相對于其他單列來說枚舉寫法最簡單,并且任何情況下都是單列的,JDK1.5之后才有的;
2.5 使用容器單列
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();
private SingletonManager() {
}
public static void putObject(String key, Object instance){
if(!objMap.containsKey(key)){
objMap.put(key, instance);
}
}
public static Object getObject(String key){
return objMap.get(key);
}
}
在程序開始的時候將單列類型注入到一個容器之中,也就是單列ManagerClass,在使用的時候再根據key值獲取對應的實例,這種方式可以使我們很方便的管理很多單列對象,也對用戶隱藏了具體實現類,降低了耦合度;但是為了避免造成內存泄漏,所以我們一般在生命周期銷毀的時候也要去銷毀它;
Android源碼分析:
說了這么多,也寫了這么多,摩拳擦掌,讓我們深吸一口氣,準備好,老司機發車了,上車了,一起來看看Android源碼中是如何實現單列的,今天的的重點就是LayoutInflater這個類;
LayoutInflater的單列模式實現:
基本用法:LayoutInflater mInflater = LayoutInflater.from(this);
上邊的寫法估計沒有人會陌生,獲取LayoutInflater 的實例,我們一起看看具體的源碼實現:
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
//調用Context getSystemService()方法獲取;
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
Context 類
public abstract Object getSystemService(@ServiceName @NonNull String name);
我們知道Context是一個抽象類,那么到底是那個類具體實現的了,我們C+H(window)一下,看看他到底有哪些子類,看下圖;
我擦,是不是搞事情,這么多類怎么找,一個類一類的去翻嗎?既然不能從這個地方下手,那就只能改走其他的道路,那我就從入口函數開始,也就是我們熟悉的main函數 在Android中ActivityThread類中,看主要的方法和類;
### ActivityThread thread = new ActivityThread();
主要看thread的attach(false)方法:
public static void main(String[] args) {
省略.......
//初始化thread
ActivityThread thread = new ActivityThread();
thread.attach(false);
省略.......
}
private void attach(boolean system) {
//是不是系統應用:傳遞的false
if (!system) {
省略......
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
省略......
}
在main方法中初始化ActivityThread的實例,并且調用了attach方法 傳入false,證明不是系統應用,緊接著獲取了IActivityManager 實例,其實也就是ActivityManagerService的對象,他們的關系圖如下;
接著調用attachApplication(mAppThread);綁定當前的ApplicationThread;接著往下走,看看attachApplication(mAppThread)方法,還是一樣的抓住核心,只看重點;
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
這個方法邏輯就很簡單了鎖定當前的thread和pid 接著繼續往下走;
ActivityManagerService
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
省略.......
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
//省略........
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
//下邊主要是介紹了些receiver和broadcast 這些都不是重點主要看和app有關的, 所以就省略掉了;
這個方法代碼很長但是邏輯并不是很復雜,有兩個重要的方法, thread.bindApplication()和attachApplicationLocked(app);bindApplication見名之意,將thread綁定到ActivityManagerService中,那我們來看看attachApplicationLocked(app)這個方法,
ActivityStackSupervisor
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 (!isFrontStack(stack)) {
continue;
}
//返回當前棧頂的activity
ActivityRecord hr = stack.topRunningActivityLocked(null);
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
//真正的開啟activity;原來找了半天就是這個家伙;
if (realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ hr.intent.getComponent().flattenToShortString(), e);
throw e;
}
}
}
}
}
}
一看這個名字就知道肯定和Activity的任務棧有關的,當前內部持有一個ActivityStack,相當于ActivityStack的輔助管理類;realStartActivityLocked(hr, app, true, 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, new Configuration(mService.mConfiguration),
new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
//省略...........
}
重點的東西來了既然這個方法是用來開啟activity的我猜想他肯定和Context有關,既然和Context有關那么也就能找到Context的子類帶這個目標我們出發了,我已饑渴難耐了;
ActivityThread
@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 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);
}
這個方法準備了要啟動的activity的一些信息,重要的一點他利用Handler發送了一個消息, sendMessage(H.LAUNCH_ACTIVITY, r);我們來找找這個接收消息的地方;
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
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);
}省略..............
}
我們接著具體看看 handleLaunchActivity(r, null);這個方法到底做了什么東西;
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//省略.......
Activity a = performLaunchActivity(r, customIntent); .
//返回Activity對象;我們經常用到Context的時候就傳入this,我猜想Activity的創建和Context應該是少不了的關聯,沒辦法只能接著找;
}
省略........
performLaunchActivity 代碼太多我本來想只是截取一部分,可是看了好久感覺還是貼出來一大部分吧,畢竟都是比較重要的,
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
/省略......
//終于找到了activity的創建了;你用類加載器采用反射機制;
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.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);
}
}
...........
//初始化Application
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
//獲取當前activity的Context 終于還是給我找到了...
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
.........
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);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
...........
撐住 撐住 就到了....
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
//省略........
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
//省略....還好代碼不多;感謝老鐵 這個不就是我要找到的他的實現類嗎.....趕緊看看,對了之前的方法可別忘了,
ContextImpl類:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
一步一步點下去
/**
* Gets a system service from a given context. 注釋寫的多清楚,哈哈
*/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
終于沒了吧
看看真面目
HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =new HashMap<String, ServiceFetcher<?>>();
原來就是HashMap存貯,也就是我上邊寫的最后一種單列方式容器存貯,其實寫到這里還并沒有寫完了,既然我們是直接獲取的也并沒有自己進行注入,那么你想過沒有那么到底系統是啥時候給我們注入的了,帶這個問題,我們在翻翻源碼,瞧瞧,別怕,有我在嘿嘿.....繼續接著擼起;
SystemServiceRegistry類:
我們看看這個HashMap到底是啥時候注入的只關心這個map集合就好了,搜索一下;
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
注釋還是注釋寫的真的太清楚了,雖然我英語沒過好多級,這些還是一看就明白的
*/
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
.........
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
原來在這地方調用,那到底是啥時候調用的這個registerService還是需要搜索一下,這就比之前簡單多了,
static {
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
new CachedServiceFetcher<CaptioningManager>() {
@Override
public CaptioningManager createService(ContextImpl ctx) {
return new CaptioningManager(ctx);
}});
......省略
重點在這,這不是就是我們獲取的東西嗎;
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
..省略..........
總結:
原來我們app已啟動的時候下邊已經多了大量的工作,在第一次加載類的時候就會注冊各種ServiceFetcher,將這些以鍵值對的形式存進去,需要用到的時候在通過key去取值,到此現在這個流程基本上明白了,那我就用一個流程圖來再一次的回頭整理下圖,只是貼出了一些重要的方法以便回顧之前看的;
其實分析了這么多的源碼,說到底就是一個核心原理就是構造私有,并且通過靜態方法獲取一個實例,在這個過程中必須保證線程的安全性;如果覺得寫的不錯的給個贊哦,寫的有問題的地方希望大家能給你糾正,謝謝!