Android中的单例模式使⽤场景
1.设计模式实例-单例模式
单例模式,故名思议,是指在⼀个类中通过设置静态使得其仅创造⼀个唯⼀的实例。这样设置的⽬的是满⾜开发者的希望??这个类只需要被实例化创建⼀次,同时因为其为静态的缘故,加载的速度也应该快于正常实例化⼀个类的速度(理论上)。
在Android开发中,当我们需要创建⼀个Fragment的时候常常会⽤到这样的模式,没有代码的学习是虚⽆的,接下来亮代码学习:
public class SelectFrame extends Fragment {
private final static String selectFrameKey = "SFKey";
private static SelectFrame mSelectFrame;
private ArrayList<String> frameList;
public static SelectFrame getInstance(ArrayList<String> frameList){
if (mSelectFrame == null) {
mSelectFrame = new SelectFrame();
}
Bundle bundle = new Bundle();
bundle.putStringArrayList(selectFrameKey, frameList);
mSelectFrame.setArguments(bundle);
return mSelectFrame;
}
......
这是我在⼀个Fragment类⾥⾯定义的其中⼀部分,⾸先必须要定义⼀个⾃⾝的静态mSelectFrame。然后通过⼀个静态⽅法
getInstance()来实例化这个mSelectFrame,很明显,这⾥通过判断其是否为空的⽅式,使得每⼀次我们使⽤getInstance的时候都返回的是同⼀个对象mSelectFrame。
getInstance⽅法中有时我们也会放⼊⼀些我们需要传递的参数,⽐如我这个⽅法中放⼊了⼀个List对象,然后直接在⾥⾯⽤Bundle装载这个List对象,原本我们可能是在外部来做这些操作的,然⽽现在却直接通过这个⽅法将数据传⼊的操作集成到了这个Fragment中,即减少了外部Activity的逻辑代码,也使得这个Fragment在复⽤的时候操作更⽅便。(再通过setArguments的⽅法保存数据)。
接下来看我们的onCreateView⽅法:
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.select_frame_fragment, container, false);
Bundle bundle = Arguments();
frameList = StringArrayList(selectFrameKey);
}
这⾥我们就将从前⾯getInstance中setArguments保存下来的数据通过Fragment的getArguments⽅法重新将数据读取出来。这样就完成了我们的⽬的。
接下来就是在Activity中创建我们这个Fragment对象了:
selectFrameFrag = Instance(nameFrameThumbnail);
fm.beginTransaction().add(R.id.fragment_frame_container, selectFrameFrag)mit();
(fm是FragmentManager..)
这样就OK了。
2.单例模式的分类
单例模式有两种创建⽅式,分别为饿汉式和懒汉式。
饿汉式,故名思议,很饿,在⼀开始就直接创建了这个对象。
懒汉式,顾名思义,很懒,只有在你要调⽤它的时候,通过⾃⼰写的⽅法⾥⾯来对他实例化。刚刚上⾯那个例⼦就是懒汉式的。
也许这么说你会有⼀点不明⽩(其实应该没有⼈不明⽩吧= =),然后其实只要看代码的这个地⽅:
private static SelectFrame mSelectFrame;
单例模式的几种实现方式上⾯这个代码,这⾥⼀开始定义⾃⾝的静态时,没有实例化它,那么就是个懒汉.然后只有在我调⽤getInstance⽅法的时候,才在⾥⾯对他进⾏实例化(new SelectFrame()).
然后我们再看看饿汉:
private static SelectFrame mSelectFrame = new SelectFrame();
在定义的时候就已经实例化了.
3.单例模式的线程安全问题
没错,单例模式毕竟就是像上⾯讲的这么简单。但需要注意的是,单例模式中的饿汉式是线程安全的,⽽懒汉式是线程不安全的,我们⼀般会再懒汉式的getInstance⽅法中通过synchronized上锁。类似于这样:
if (mSelectFrame == null) {
synchronized (SelectFrame.class) {
if (mSelectFrame == null) {
mSelectFrame = new SelectFrame();
}
}
}
有⼈说为什么要⽤两个if XX == null。这⾥是因为提⾼效率,只有⼀个也是可以的,但效率上⼤⼤减低,因为不是每⼀次调⽤的时候我们要判断他是否同步,应该是如果判断当前为null的话,我们就直接不让他进⼊了,不需要判断是否同步。只有满⾜第⼀个条件的时候,我们才需要⽤锁来判断它是否线程安全。(这⾥的意思也是说明if⼀条语句的判断速度当然⽐synchronized (SelectFrame.class)快。。)
另外,在我们使⽤单例模式的时候,有些⼈会去私有化这个类的构造⽅法,使得这个类只能通过⾃⼰写的getInstance()来创建。类似于这样:
private SelectFrame() {
// 并不需要做什么,只需要将这个外⾯的public改成private就好了=。=
}
嗯嗯,这么⼀来外部就只能按我要求的⽅法来创建这个对象了.
在这⾥也基本将单例模式的使⽤基本讲完了,接下来还会继续写其他的设计模式的使⽤,如果有涉及到Android上的都会尽⼒⽤Android上的例⼦来讲,加深印象。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
单例模式是最常⽤到的设计模式之⼀,熟悉设计模式的朋友对单例模式都不会陌⽣。⼀般介绍单例模式的书籍都会提到 饿汉式和 懒汉式这两种实现⽅式。但是除了这两种⽅式,本⽂还会介绍其他⼏种实现单例的⽅式,让我们来⼀起看看吧。
简介
单例模式是⼀种常⽤的软件设计模式,其定义是单例对象的类只能允许⼀个实例存在。
许多时候整个系统只需要拥有⼀个的全局对象,这样有利于我们协调系统整体的⾏为。⽐如在某个服务器程序中,该服务器的配置信息存放在⼀个⽂件中,这些配置数据由⼀个单例对象统⼀读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种⽅式简化了在复杂环境下的配置管理。
基本的实现思路
单例模式要求类能够有返回对象⼀个引⽤(永远是同⼀个)和⼀个获得该实例的⽅法(必须是静态⽅法,通常使⽤getInstance这个名称)。单例的实现主要是通过以下两个步骤:
1. 将该类的构造⽅法定义为私有⽅法,这样其他处的代码就⽆法通过调⽤该类的构造⽅法来实例化该类的对象,只有通过该类提供的静
态⽅法来得到该类的唯⼀实例;
2. 在该类内提供⼀个静态⽅法,当我们调⽤这个⽅法时,如果类持有的引⽤不为空就返回这个引⽤,如果类保持的引⽤为空就创建该类
的实例并将实例的引⽤赋予该类保持的引⽤。
注意事项
单例模式在多线程的应⽤场合下必须⼩⼼使⽤。如果当唯⼀实例尚未创建时,有两个线程同时调⽤创建⽅法,那么它们同时没有检测到唯⼀实例的存在,从⽽同时各⾃创建了⼀个实例,这样就有两个实例被构造出来,从⽽违反了单例模式中实例唯⼀的原则。 解决这个问题的办法是为指⽰类是否已经实例化的变量提供⼀个互斥锁(虽然这样会降低效率)。
单例模式的⼋种写法
1、饿汉式(静态常量)[可⽤]
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
优点:这种写法⽐较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始⾄终从未使⽤过这个实例,则会造成内存的浪费。
2、饿汉式(静态代码块)[可⽤]
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public Singleton getInstance() {
return instance;
}
}
这种⽅式和上⾯的⽅式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执⾏静态代码块中的代码,初始化类的实例。优缺点和上⾯是⼀样的。
3、懒汉式(线程不安全)[不可⽤]
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这种写法起到了Lazy Loading的效果,但是只能在单线程下使⽤。如果在多线程下,⼀个线程进⼊了if (singleton == null)判断语句块,还未来得及往下执⾏,另⼀个线程也通过了这个判断语句,这时便会产⽣多个实例。所以在多线程环境下不可使⽤这种⽅式。
4、懒汉式(线程安全,同步⽅法)[不推荐⽤]
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
解决上⾯第三种实现⽅式的线程不安全问题,做个线程同步就可以了,于是就对getInstance()⽅法进⾏了线程同步。
缺点:效率太低了,每个线程在想获得类的实例时候,执⾏getInstance()⽅法都要进⾏同步。⽽其实这个⽅法只执⾏⼀次实例化代码就够了,后⾯的想获得该类实例,直接return就⾏了。⽅法进⾏同步效
率太低要改进。
5、懒汉式(线程安全,同步代码块)[不可⽤]
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
由于第四种实现⽅式同步效率太低,所以摒弃同步⽅法,改为同步产⽣实例化的的代码块。但是这种同步并不能起到线程同步的作⽤。跟第3种实现⽅式遇到的情形⼀致,假如⼀个线程进⼊了if (singleton == null)判断语句块,还未来得及往下执⾏,另⼀个线程也通过了这个判断语句,这时便会产⽣多个实例。
6、双重检查[推荐⽤]
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
Double-Check概念对于多线程开发者来说不会陌⽣,如代码中所⽰,我们进⾏了两次if (singleton ==
null)检查,这样就可以保证线程安全了。这样,实例化代码只⽤执⾏⼀次,后⾯再次访问时,判断if (singleton == null),直接return实例化对象。
优点:线程安全;延迟加载;效率较⾼。
7、静态内部类[推荐⽤]
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论