目的
android框架搭建的時(shí)候設(shè)計(jì)模式會(huì)起到很大的作用,在搭建初期的項(xiàng)目框架時(shí)飒赃,合理利用設(shè)計(jì)模式的一些理念和技巧,能讓你在構(gòu)建整個(gè)項(xiàng)目框架的時(shí)候更加游刃有余.學(xué)習(xí)設(shè)計(jì)模式的初衷不僅僅是能讓你的代碼結(jié)構(gòu)更加整潔易讀科侈,而且能降低各模塊之間的耦合性载佳,使你的框架有更好的可拓展性,這樣在后期的需求不斷變動(dòng)的時(shí)候臀栈,就不必對(duì)整個(gè)代碼結(jié)構(gòu)和框架做大的改動(dòng)蔫慧,當(dāng)然,隨著后期業(yè)務(wù)的增多权薯,相應(yīng)的代碼重構(gòu)還是要有姑躲,在此設(shè)計(jì)模式的學(xué)習(xí)及框架的搭建只是為了減少后期大幅度重構(gòu)代碼的工作,畢竟盟蚣,程序員黍析,都是懶人.
網(wǎng)上有很多關(guān)于設(shè)計(jì)模式的介紹,但是對(duì)于設(shè)計(jì)模式在項(xiàng)目中的運(yùn)用比較少屎开,尤其是對(duì)android項(xiàng)目的運(yùn)用阐枣,這次,我打算從頭梳理一遍設(shè)計(jì)模式奄抽,在梳理過(guò)程中蔼两,盡量貼合實(shí)際應(yīng)用需要,在項(xiàng)目中去闡述設(shè)計(jì)模式在各個(gè)地方和模塊的使用和技巧逞度,也算是重新學(xué)習(xí)一遍吧.
單例模式的定義
首先關(guān)于單例模式额划,定義相當(dāng)簡(jiǎn)單,單例模式即指創(chuàng)建單一的實(shí)例對(duì)象档泽,作為全局變量在各處引用.單例的意義在于俊戳,節(jié)省部分內(nèi)存開(kāi)支彬祖,相信在android開(kāi)發(fā)過(guò)程中大家都遇到過(guò)內(nèi)存溢出和泄露的問(wèn)題.使用單例模式就不得不說(shuō)一下內(nèi)存泄露和內(nèi)存溢出的問(wèn)題了.
關(guān)于內(nèi)存泄露與垃圾回收
所謂的內(nèi)存泄露是指什么呢?我們都知道品抽,java虛擬機(jī)中內(nèi)存是分為兩塊的(靜態(tài)的內(nèi)存不在這次考慮范圍內(nèi)储笑,只說(shuō)明問(wèn)題,后續(xù)可以討論java內(nèi)存構(gòu)成問(wèn)題)即堆內(nèi)存和棧內(nèi)存圆恤,堆內(nèi)存和棧內(nèi)存都為動(dòng)態(tài)分配地址突倍。我們通常所定義的變量,分為基本數(shù)據(jù)類型和引用變量盆昙,基本數(shù)據(jù)類型包括int羽历、long、double淡喜、boolean秕磷、double等常用的基本變量,這些包括他們的封包變量即Integer炼团、Double澎嚣、Boolean等在聲明及賦值時(shí),java會(huì)直接從棧內(nèi)直接分配內(nèi)存瘟芝,而像自定義的實(shí)體易桃,在聲明及通過(guò)關(guān)鍵子new來(lái)創(chuàng)建實(shí)例時(shí),會(huì)在內(nèi)存中分配兩個(gè)地址锌俱,棧內(nèi)會(huì)分配給引用變量一個(gè)地址作為聲明及指針存放的空間(指針概念請(qǐng)了解C/C++相關(guān)鏈表指針概念)晤郑,然后指針指向的位置即為通過(guò)關(guān)鍵字new 創(chuàng)建實(shí)例的具體數(shù)據(jù)在堆內(nèi)存的位置。再回到內(nèi)存泄露和內(nèi)存溢出的問(wèn)題上來(lái)講贸宏,內(nèi)存泄露即指在java虛擬機(jī)分配給你定義的變量?jī)?nèi)存后造寝,無(wú)法釋放你申請(qǐng)的這個(gè)內(nèi)存,簡(jiǎn)單來(lái)講就是吭练,你在創(chuàng)建引用變量的時(shí)候诫龙,并沒(méi)有在使用完成后釋放這個(gè)內(nèi)存,當(dāng)然你可能說(shuō)java 的gc回收機(jī)制會(huì)幫你完成线脚,但是赐稽,無(wú)疑這樣效率就會(huì)極端低下.單個(gè)內(nèi)存泄露可能效果不明顯,但是大量的內(nèi)存泄露就會(huì)導(dǎo)致內(nèi)存損耗嚴(yán)重浑侥,另外姊舵,gc垃圾回收時(shí)會(huì)阻塞主線程,這就造成了UI卡頓寓落,這也是一部分UI卡頓的原因.(具體java gc回收機(jī)制相對(duì)比較復(fù)雜括丁,在此我們不做更深入的了解,只做說(shuō)明伶选,如果想更深入了解java gc機(jī)制及過(guò)程建議查詢相關(guān)資料).至于內(nèi)存溢出史飞,相對(duì)而言更好理解尖昏,即指申請(qǐng)內(nèi)存的時(shí)候,系統(tǒng)分配的內(nèi)存不足就會(huì)引起內(nèi)存溢出.這種溢出從某種程度上講构资,也有一部分內(nèi)存泄露導(dǎo)致內(nèi)存不足的原因.所以抽诉,總結(jié)而言就是,過(guò)度的創(chuàng)建對(duì)象吐绵,定義不必要的變量會(huì)導(dǎo)致內(nèi)存消耗增加迹淌,大量的內(nèi)存資源被浪費(fèi),引起內(nèi)存泄露.而這也正是我們?yōu)槭裁匆脝卫J絹?lái)處理一些全局的變量和工具類的意義.當(dāng)然己单,單例模式只是從一定程度上避免內(nèi)存的問(wèn)題唉窃,并不是能從根本上解決問(wèn)題之道.
好了,扯了這么多終于又回歸到咱們的單例模式這個(gè)設(shè)計(jì)理念上纹笼,設(shè)計(jì)模式作為一個(gè)最經(jīng)典的設(shè)計(jì)模式.被大量的研究和探討.相繼也出現(xiàn)了各種版本纹份,在此就對(duì)各個(gè)版本做一下簡(jiǎn)單介紹.
經(jīng)典模式的單例(懶漢模式)
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
if(null==singleton) {
singleton = new Singleton();
}
return singleton;
}
}
經(jīng)典模式會(huì)將構(gòu)造器私有,防止外部通過(guò)new關(guān)鍵字穿件對(duì)象實(shí)體廷痘,對(duì)外提供靜態(tài)方法getInstantce來(lái)獲取單一實(shí)例蔓涧,調(diào)用方法時(shí)做非空判斷保證在調(diào)用時(shí)至少已經(jīng)創(chuàng)建出對(duì)象.
當(dāng)然,眾所周知的這種方式是有弊端的牍疏,弊端就在于多線程無(wú)法保證單例模式的唯一性蠢笋。
關(guān)于線程的cpu調(diào)度
在這里我們不得不提一下線程的本質(zhì),大家都知道線程是cpu調(diào)度的基本單位鳞陨,為什么這么說(shuō)呢,很簡(jiǎn)單瞻惋,線程的本質(zhì)就是cpu會(huì)分配一個(gè)最小執(zhí)行片段時(shí)間厦滤,采用輪詢的方式去不停的執(zhí)行線程,分配資源歼狼,從某種程度上來(lái)講掏导,單核cpu本質(zhì)上應(yīng)該是沒(méi)有真正并發(fā)執(zhí)行的過(guò)程,單核cpu只是通過(guò)將線程執(zhí)行時(shí)間碎片化羽峰,輪詢執(zhí)行調(diào)度趟咆,從用戶的角度來(lái)講,看上去是同時(shí)執(zhí)行的兩個(gè)線程其實(shí)都是有先后的梅屉。當(dāng)然多核cpu就會(huì)有真正的并發(fā)了.不過(guò)這些和我們說(shuō)的經(jīng)典單例的弊端并沒(méi)有啥米關(guān)系值纱,經(jīng)典單例的問(wèn)題在于數(shù)據(jù)同步.即兩個(gè)線程都經(jīng)過(guò)if判斷之后進(jìn)入方法體時(shí)依次執(zhí)行,這樣就會(huì)創(chuàng)建多次對(duì)象坯汤,即不能保證單例的唯一性.具體請(qǐng)參考下圖
改進(jìn)地方法想必大家都是到的虐唠,最簡(jiǎn)單的方式就是添加synchronized同步鎖(關(guān)于互斥鎖等線程同步相關(guān)我們后續(xù)再討論).而這也必將導(dǎo)致的一個(gè)問(wèn)題就是,效率的低下惰聂,一般來(lái)講疆偿,我們作為單例的變量很少涉及線程問(wèn)題咱筛,當(dāng)然秉持著嚴(yán)謹(jǐn)?shù)膽B(tài)度我們依然還是需要保證在多線程的情況下的問(wèn)題,這樣的話杆故,我們只能繼續(xù)修改單例的寫(xiě)法迅箩,以保證我們的需求.這樣我們就不得不介紹另一種單例模式,惡漢模式.
所謂惡漢模式处铛,即初始化的時(shí)候就已經(jīng)創(chuàng)建對(duì)象沙热,這樣就不需要坐非空判斷,則也不會(huì)出現(xiàn)線程同步問(wèn)題了.具體代碼如下.
惡漢模式
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
當(dāng)然罢缸,單例的其他寫(xiě)法網(wǎng)上也有也行篙贸,筆者個(gè)人認(rèn)為這三種方法較為常用,剩余其他的寫(xiě)法筆者未做深入研究,開(kāi)發(fā)過(guò)程中也比較少見(jiàn)枫疆,故此也不再討論.
項(xiàng)目中應(yīng)用
開(kāi)篇說(shuō)要將單例的設(shè)計(jì)模式運(yùn)用在框架搭建中爵川,筆者寫(xiě)了一段關(guān)于單例應(yīng)用于用戶管理工具的代碼,希望大家一塊學(xué)習(xí)息楔,有不足的地方希望大家批評(píng)指正.
附上筆者代碼:
packagecom.ss.single;
import java.io.Serializable;
import java.util.Map;
/**
* Created by Administrator on 2016/11/23.
* 用戶實(shí)體類
*/
public class User implements Serializable {
//用戶姓名
private String userName;
//用戶昵稱
private String nickName;
//用戶密碼
private String pwd;
//用戶權(quán)限
private String permission;
//用戶相關(guān)資料鍵值對(duì)
private Map<String,String> datas;
public void setUserName(String userName) {
this.userName= userName;
}
public void setNickName(String nickName) {
this.nickName= nickName;
}
public void setPwd(String pwd) {
this.pwd= pwd;
}
public void setPermission(String permission) {
this.permission= permission;
}
public void setDatas(Map datas) {
this.datas= datas;
}
public String getUserName() {
return userName;
}
public String getNickName() {
return nickName;
}
public String getPwd() {
return pwd;
}
public String getPermission() {
return permission;
}
public Map getDatas() {
return datas;
}
@Override
public String toString() {
return"User{"+
"userName='"+userName+'\''+
", nickName='"+nickName+'\''+
", pwd='"+pwd+'\''+
", permission='"+permission+'\''+
", datas="+datas+
'}';
}
}
用戶管理工具類.
packagecom.ss.single;
importandroid.support.annotation.NonNull;
/**
* Created by Administrator on 2016/11/23.
*/
public class UserHelper {
private static UserHelperhelper = new UserHelper();
private User user;
private UserHelper() {
}
/**
* 獲取當(dāng)前userhelper實(shí)例
*
*@return
*/
public static UserHelper getInstance() {
return helper;
}
/**
* 獲取當(dāng)前用戶
*
*@return
*/
public User getCurrentUser() {
return user;
}
/**
* 替換/初始化當(dāng)前用戶
*
*@param user
*/
public void replaceUser(User user) {
this.user= user;
}
/**
* 退出當(dāng)前登錄
*/
public void logout() {
user=null;
}
/**
* 判斷本地登錄是否成功
*
*@param userName
*@param pwd
*@return
*/
public boolean localLogin(@NonNull String userName,@NonNull String pwd) {
if(null!=user? userName.equals(user.getUserName()) && pwd.equals(user.getPwd()) :false)
return true;
return false;
}
}
參考書(shū)籍:HeadFirst設(shè)計(jì)模式.