单例模式android应用场景,应用最广的模式-单例模式(结合Android源码)
談起設計模式估計大家都不會陌生,一個項目中至少會用到其中的一種模式,今天要說的主角就是單列,我了大致總結了它的幾種用法同時也結合了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對象指向分配的內存空間;
演示圖.png
這種寫法也并不是保證完全100%的可靠,由于java編譯器允許執行無序,并且jdk1.5之前的jvm(java內存模型)中的Cache,寄存器到主內存的回寫順序規定,第二個和第三個執行是無法保證按順序執行的,也就是說有可能1-2-3也有可能是1-3-2; 這時假如有A和B兩條線程,A線程執行到3的步驟,但是未執行2,這時候B線程來了搶了權限,直接取走instance這時候就有可能報錯,同時我也放了一張內存模型,幫助大家更好的去理解,如圖;
內存模型.png
簡單總結就是說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 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)一下,看看他到底有哪些子類,看下圖;
Paste_Image.png
我擦,是不是搞事情,這么多類怎么找,一個類一類的去翻嗎?既然不能從這個地方下手,那就只能改走其他的道路,那我就從入口函數開始,也就是我們熟悉的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的對象,他們的關系圖如下;
@62[RUXWSUUF5}IJ86]_GL8.png
接著調用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 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 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);
}
這個方法準備了要啟動的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> SYSTEM_SERVICE_FETCHERS =new HashMap>();
原來就是HashMap存貯,也就是我上邊寫的最后一種單列方式容器存貯,其實寫到這里還并沒有寫完了,既然我們是直接獲取的也并沒有自己進行注入,那么你想過沒有那么到底系統是啥時候給我們注入的了,帶這個問題,我們在翻翻源碼,瞧瞧,別怕,有我在嘿嘿.....繼續接著擼起;
SystemServiceRegistry類:
我們看看這個HashMap到底是啥時候注入的只關心這個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);
}
原來在這地方調用,那到底是啥時候調用的這個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);
}});
......省略
重點在這,這不是就是我們獲取的東西嗎;
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
..省略..........
總結:
原來我們app已啟動的時候下邊已經多了大量的工作,在第一次加載類的時候就會注冊各種ServiceFetcher,將這些以鍵值對的形式存進去,需要用到的時候在通過key去取值,到此現在這個流程基本上明白了,那我就用一個流程圖來再一次的回頭整理下圖,只是貼出了一些重要的方法以便回顧之前看的;
啟動流程圖.png
其實分析了這么多的源碼,說到底就是一個核心原理就是構造私有,并且通過靜態方法獲取一個實例,在這個過程中必須保證線程的安全性;如果覺得寫的不錯的給個贊哦,寫的有問題的地方希望大家能給你糾正,謝謝!
總結
以上是生活随笔為你收集整理的单例模式android应用场景,应用最广的模式-单例模式(结合Android源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超声探头阻抗分析
- 下一篇: CSS中如何使用 Positioning