单例模式android应用场景,应用最广的模式-单例模式(结合Android源码)
談起設(shè)計模式估計大家都不會陌生,一個項目中至少會用到其中的一種模式,今天要說的主角就是單列,我了大致總結(jié)了它的幾種用法同時也結(jié)合了Android的源碼進(jìn)行單列的分析;
好了正題開始了,其實(shí)個人總結(jié)了下自我學(xué)習(xí)的方法,在學(xué)習(xí)任何一個新的事物的時候,不能盲目的去干,而應(yīng)適當(dāng)?shù)牟扇∫欢ǖ募记尚詵|西,OK;
我大致分了三大步:
1:要知道這個東西是個什么玩意,這個東西有啥用,一般用在啥地方;
2:這個東西該怎么用了,我平時有沒有遇到類似的用法;
3:熟悉了用法之后,總結(jié)下為什么別人那樣去寫,這樣寫的優(yōu)缺點(diǎn)是什么,我能不能仿寫下或者能不能改寫下別人的代碼,進(jìn)行深度的總結(jié)下,然后用于到實(shí)踐中,記住,看完了,千萬不要就丟掉了,東西太多了,也許今天記住了,明天就會忘記,所以最好寫幾個案列實(shí)踐下;實(shí)踐是檢驗真理的位移標(biāo)準(zhǔn),不是嗎;
1: 單列模式的定義以及應(yīng)用場景
1.1 定義:
確保這個類在內(nèi)存中只會存在一個對象,而且自行實(shí)例化并向整個系統(tǒng)提供這個實(shí)例;
1.2 場景:
一般創(chuàng)建一個對象需要消耗過多的資源,如:訪問I0和數(shù)據(jù)庫等資源或者有很多個地方都用到了這個實(shí)例;
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;
}
}
上邊的兩種是最常見的,顧名思義懶漢式和餓漢式,一個是拿時間換空間,一個是拿空間換時間,懶漢式只有我需要他的時候才去加載它,懶加載機(jī)制,餓漢式不管需不需要我先加載了再說,先在內(nèi)存中開辟一塊空間,占用一塊地方,等用到了直接就拿來用.這兩種是最基本的單列模式,
2.1.2.1懶漢式:
懶漢式缺點(diǎn):效率低,第一次加載需要實(shí)例化,反應(yīng)稍慢。每次調(diào)用getInstance方法都會進(jìn)行同步,消耗不必要的資源。
2.1.1.1餓漢式:
缺點(diǎn):不需要的時候就加載了,造成資源浪費(fèi).
2.2 雙重檢查單列(DCL實(shí)現(xiàn)單列)
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;
}
}
這種寫法估計是我們在開發(fā)中最常用的,這次代碼的亮點(diǎn)是是在getInstance()方法中進(jìn)行了雙重的判斷,第一層判斷的主要避免了不必要的同步,第二層判斷是為了在null的情況下再去創(chuàng)建實(shí)例;舉個簡單的列子:假如現(xiàn)在有多個線程同時觸發(fā)這個方法: 線程A執(zhí)行到nstance = new Singleton(),它大致的做了三件事:
(1):給Singleton實(shí)例分配內(nèi)存,將函數(shù)壓棧,并且申明變量類型;
(2):初始化構(gòu)造函數(shù)以及里面的字段,在堆內(nèi)存開辟空間;
(3):將instance對象指向分配的內(nèi)存空間;
演示圖.png
這種寫法也并不是保證完全100%的可靠,由于java編譯器允許執(zhí)行無序,并且jdk1.5之前的jvm(java內(nèi)存模型)中的Cache,寄存器到主內(nèi)存的回寫順序規(guī)定,第二個和第三個執(zhí)行是無法保證按順序執(zhí)行的,也就是說有可能1-2-3也有可能是1-3-2; 這時假如有A和B兩條線程,A線程執(zhí)行到3的步驟,但是未執(zhí)行2,這時候B線程來了搶了權(quán)限,直接取走instance這時候就有可能報錯,同時我也放了一張內(nèi)存模型,幫助大家更好的去理解,如圖;
內(nèi)存模型.png
簡單總結(jié)就是說jdk1.5之前會造成兩個問題
1:線程間共享變量不可見性;
2:無序性(執(zhí)行順序無法保證);
當(dāng)然這個bug已經(jīng)修復(fù)了,SUN官方調(diào)整了JVM,具體了Volatile關(guān)鍵字,因此在jdk1.5之前只需要寫成這樣既可, private Volatitle static Singleton instance; 這樣就可以保證每次都是從主內(nèi)存中取,當(dāng)然這樣寫或多或少的回影響性能,但是為了安全起見,這點(diǎn)性能犧牲還是值得;
雙重檢查單列(DCL)
優(yōu)點(diǎn):資源利用率高,第一次執(zhí)行方法是單列對象才會被實(shí)例化;
缺點(diǎn):第一次加載時會稍慢,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 靜態(tài)內(nèi)部內(nèi)實(shí)現(xiàn)單列
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;
}
}
這種方式不僅確保了線程的安全性,也能夠保證對象的唯一性,同時也是延遲加載,很多技術(shù)大牛也是這樣推薦書寫;
2.4 枚舉實(shí)現(xiàn)單列
public enum SingletonEnum {
INSTANCE;
public void doSomething() {
}
}
優(yōu)點(diǎn):相對于其他單列來說枚舉寫法最簡單,并且任何情況下都是單列的,JDK1.5之后才有的;
2.5 使用容器單列
public class SingletonManager {
private static Map 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);
}
}
在程序開始的時候?qū)瘟蓄愋妥⑷氲揭粋€容器之中,也就是單列ManagerClass,在使用的時候再根據(jù)key值獲取對應(yīng)的實(shí)例,這種方式可以使我們很方便的管理很多單列對象,也對用戶隱藏了具體實(shí)現(xiàn)類,降低了耦合度;但是為了避免造成內(nèi)存泄漏,所以我們一般在生命周期銷毀的時候也要去銷毀它;
Android源碼分析:
說了這么多,也寫了這么多,摩拳擦掌,讓我們深吸一口氣,準(zhǔn)備好,老司機(jī)發(fā)車了,上車了,一起來看看Android源碼中是如何實(shí)現(xiàn)單列的,今天的的重點(diǎn)就是LayoutInflater這個類;
LayoutInflater的單列模式實(shí)現(xiàn):
基本用法:LayoutInflater mInflater = LayoutInflater.from(this);
上邊的寫法估計沒有人會陌生,獲取LayoutInflater 的實(shí)例,我們一起看看具體的源碼實(shí)現(xiàn):
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
//調(diào)用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是一個抽象類,那么到底是那個類具體實(shí)現(xiàn)的了,我們C+H(window)一下,看看他到底有哪些子類,看下圖;
Paste_Image.png
我擦,是不是搞事情,這么多類怎么找,一個類一類的去翻嗎?既然不能從這個地方下手,那就只能改走其他的道路,那我就從入口函數(shù)開始,也就是我們熟悉的main函數(shù) 在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) {
//是不是系統(tǒng)應(yīng)用:傳遞的false
if (!system) {
省略......
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
省略......
}
在main方法中初始化ActivityThread的實(shí)例,并且調(diào)用了attach方法 傳入false,證明不是系統(tǒng)應(yīng)用,緊接著獲取了IActivityManager 實(shí)例,其實(shí)也就是ActivityManagerService的對象,他們的關(guān)系圖如下;
@62[RUXWSUUF5}IJ86]_GL8.png
接著調(diào)用attachApplication(mAppThread);綁定當(dāng)前的ApplicationThread;接著往下走,看看attachApplication(mAppThread)方法,還是一樣的抓住核心,只看重點(diǎn);
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
這個方法邏輯就很簡單了鎖定當(dāng)前的thread和pid 接著繼續(xù)往下走;
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 這些都不是重點(diǎn)主要看和app有關(guān)的, 所以就省略掉了;
這個方法代碼很長但是邏輯并不是很復(fù)雜,有兩個重要的方法, 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 stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFrontStack(stack)) {
continue;
}
//返回當(dāng)前棧頂?shù)腶ctivity
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的任務(wù)棧有關(guān)的,當(dāng)前內(nèi)部持有一個ActivityStack,相當(dāng)于ActivityStack的輔助管理類;realStartActivityLocked(hr, app, true, true)而這個方法是真正的去開啟activity的
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
//省略..主要是檢查一些配置信息和設(shè)置相關(guān)的參數(shù)等等........
//參數(shù)設(shè)置完畢終于準(zhǔn)備啟動activity了,發(fā)車了;
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);
//省略...........
}
重點(diǎn)的東西來了既然這個方法是用來開啟activity的我猜想他肯定和Context有關(guān),既然和Context有關(guān)那么也就能找到Context的子類帶這個目標(biāo)我們出發(fā)了,我已饑渴難耐了;
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 pendingResults, List 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);
}
這個方法準(zhǔn)備了要啟動的activity的一些信息,重要的一點(diǎn)他利用Handler發(fā)送了一個消息, 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);
//最終調(diào)用
handleLaunchActivity(r, null);
}省略..............
}
我們接著具體看看 handleLaunchActivity(r, null);這個方法到底做了什么東西;
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//省略.......
Activity a = performLaunchActivity(r, customIntent); .
//返回Activity對象;我們經(jīng)常用到Context的時候就傳入this,我猜想Activity的創(chuàng)建和Context應(yīng)該是少不了的關(guān)聯(lián),沒辦法只能接著找;
}
省略........
performLaunchActivity 代碼太多我本來想只是截取一部分,可是看了好久感覺還是貼出來一大部分吧,畢竟都是比較重要的,
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
/省略......
//終于找到了activity的創(chuàng)建了;你用類加載器采用反射機(jī)制;
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) {
//獲取當(dāng)前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;
//省略....還好代碼不多;感謝老鐵 這個不就是我要找到的他的實(shí)現(xiàn)類嗎.....趕緊看看,對了之前的方法可別忘了,
ContextImpl類:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
一步一步點(diǎn)下去
/**
* 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> SYSTEM_SERVICE_FETCHERS =new HashMap>();
原來就是HashMap存貯,也就是我上邊寫的最后一種單列方式容器存貯,其實(shí)寫到這里還并沒有寫完了,既然我們是直接獲取的也并沒有自己進(jìn)行注入,那么你想過沒有那么到底系統(tǒng)是啥時候給我們注入的了,帶這個問題,我們在翻翻源碼,瞧瞧,別怕,有我在嘿嘿.....繼續(xù)接著擼起;
SystemServiceRegistry類:
我們看看這個HashMap到底是啥時候注入的只關(guān)心這個map集合就好了,搜索一下;
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
注釋還是注釋寫的真的太清楚了,雖然我英語沒過好多級,這些還是一看就明白的
*/
private static void registerService(String serviceName, Class serviceClass,
ServiceFetcher serviceFetcher) {
.........
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
原來在這地方調(diào)用,那到底是啥時候調(diào)用的這個registerService還是需要搜索一下,這就比之前簡單多了,
static {
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
new CachedServiceFetcher() {
@Override
public CaptioningManager createService(ContextImpl ctx) {
return new CaptioningManager(ctx);
}});
......省略
重點(diǎn)在這,這不是就是我們獲取的東西嗎;
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
..省略..........
總結(jié):
原來我們app已啟動的時候下邊已經(jīng)多了大量的工作,在第一次加載類的時候就會注冊各種ServiceFetcher,將這些以鍵值對的形式存進(jìn)去,需要用到的時候在通過key去取值,到此現(xiàn)在這個流程基本上明白了,那我就用一個流程圖來再一次的回頭整理下圖,只是貼出了一些重要的方法以便回顧之前看的;
啟動流程圖.png
其實(shí)分析了這么多的源碼,說到底就是一個核心原理就是構(gòu)造私有,并且通過靜態(tài)方法獲取一個實(shí)例,在這個過程中必須保證線程的安全性;如果覺得寫的不錯的給個贊哦,寫的有問題的地方希望大家能給你糾正,謝謝!
總結(jié)
以上是生活随笔為你收集整理的单例模式android应用场景,应用最广的模式-单例模式(结合Android源码)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超声探头阻抗分析
- 下一篇: android sina oauth2.