1. 单例设计模式?
保证整个程序中只有一个实例对象,使用场景就是一些特殊的类,比如:老板、管理层、管理的类(比如Activity的管理、皮肤的管理);
特殊情况,有时候可以写一个 XXXUtils工具类,让其只有一个实例,也是可以的;
套路:
0>:私有的、静态的、volitale、mInstance对象;
1>:私有的构造方法,防止别人去new 对象;
2>:共有的、静态的、getInstance(),让外部调用;
2. volatile关键字好处?
1>:防止重新排序:
如下图所示:
一般都是第一种的1、2、3,也就是说先开辟内存,然后初始化对象,最后赋值给mInstance,如果不加volatile关键字的话,可能会出现第二种的1、2、3;
加volatile目的就是:不要出现第二种情况的1、2、3;
2>:线程可见性:
某一个线程修改了公用对象(变量),短时间内另一个线程可能是不可见的,因为每一个线程都有自己的缓存区(线程工作区)
加volatile目的就是:让每一个线程都变得可见;
volatile关键字能够保证可见性,被volatile修饰的变量,在一个线程中被改变时会立刻同步到主内存中,而另一个线程在操作这个变量时都会先从主内存更新这个变量的值。
代码已上传至github:
https://github.com/shuai999/Architect_day8.git
2. 概述
上篇文章讲解了单例设计模式的套路及volatile作用,那么这篇文章就记录下单例设计模式常见的几种写法。
最常见的有3种:
1>: 单例 - 懒汉式(同步锁:DCL)
只有在使用的时候,才会去new 对象;
DCL定义:就是单例设计模式的懒汉式的同步锁方式
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 11:15
* Version 1.0
* Params:
* Description: 单例 - 懒汉式: (同步锁:DCL)
* 只有在使用的时候,才会去new 对象
* DCL定义:就是单例设计模式懒汉式同步锁的方式
*/
public class SingleTon3 {
// 只有在使用的时候,才会去 new 对象,比较高效
// 但是会有问题? 多线程并发的问题,如果多线程调用还是会存在多个实例的
// 加上 volatile 好处是什么?
// 1.防止重新排序
private static volatile SingleTon3 mInstance ;
private SingleTon3(){
}
/**
* 双重校验加同步锁 synchronized:
* 既保证线程安全,同时也保证效率比较高
* 但是可能还会有问题,
*/
public static SingleTon3 getInstance(){
if (mInstance == null){
synchronized (SingleTon3.class){ // 这个if只能进来一次,第二次就不会进来了
if (mInstance == null){
mInstance = new SingleTon3() ;
}
}
}
return mInstance ;
}
}
2>:单例 - 静态内部类
- 保证线程的安全;
- 用到的时候才会去 new SingleTon()对象
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 12:10
* Version 1.0
* Params:
* Description: 单例 - 静态内部类(比较常用)
* 1\. 保证线程的安全;
* 2\. 用到的时候才会 new SingleTon() 对象;
*/
public class SingleTon {
private SingleTon(){
}
public static SingleTon getInstance(){
return SingleTonHolder.mInstance ;
}
public static class SingleTonHolder{
private static volatile SingleTon mInstance = new SingleTon() ;
}
}
3>:单例 - 容器管理(系统服务就用的这种 getSystemService())
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 12:10
* Version 1.0
* Params:
* Description: 单例 - 容器管理(系统服务里边用的这种 getSystemService())
*/
public class SingleTon {
private static Map<String , Object> mSingleMap = new HashMap<>() ;
private SingleTon(){
}
/**
* 用静态的方式把 SingleTon对象初始化好了
*/
static {
mSingleMap.put("activity_manager" , new SingleTon()) ;
}
public static Object getService(String serviceName){
return mSingleMap.get(serviceName) ;
}
}
几种不常用的单例写法:
1>:单例 - 懒汉式: 随着类的启动就已经 new 了 mInstance对象了
一般不会这么用,不太好;
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 11:10
* Version 1.0
* Params:
* Description: 单例 - 饿汉式:随着类的启动就已经 new 了一个对象了 (一般不会这么用,不太好)
*/
public class SingleTon {
// 随着类的启动就已经 new 了一个对象了,但是可能我们只是想在用的时候再去 new 对象
private static SingleTon mInstance = new SingleTon();
private SingleTon(){
}
public static SingleTon getInstance(){
return mInstance ;
}
}
2>:单例 - 懒汉式:只有在使用的时候,才会去new 对象
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 11:15
* Version 1.0
* Params:
* Description: 单例 - 懒汉式:只有在使用的时候,才会去new 对象
*/
public class SingleTon1 {
// 只有在使用的时候,才会去 new 对象,比较高效
// 但是会有问题? 多线程并发的问题,如果多线程调用还是会存在多个实例的
private static SingleTon1 mInstance ;
private SingleTon1(){
}
/**
* 加同步锁 synchronized:
* 虽然解决了线程安全的问题,但是率可能会比较低,
* 因为每次获取mInstance对象都要经过同步锁的判断;
*/
public static synchronized SingleTon1 getInstance(){
if (mInstance == null){
mInstance = new SingleTon1() ;
}
return mInstance ;
}
}
3>:单例 - 懒汉式:只有在使用的时候,才会去new 对象
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 11:15
* Version 1.0
* Params:
* Description: 单例 - 懒汉式:只有在使用的时候,才会去new 对象
*/
public class SingleTon2 {
// 只有在使用的时候,才会去 new 对象,比较高效
// 但是会有问题? 多线程并发的问题,如果多线程调用还是会存在多个实例的
private static SingleTon2 mInstance ;
private SingleTon2(){
}
/**
* 双重校验加同步锁 synchronized:
* 既保证线程安全,同时也保证效率比较高
* 但是可能还会有问题,
*/
public static SingleTon2 getInstance(){
if (mInstance == null){
synchronized (SingleTon2.class){ // 这个if只能进来一次,第二次就不会进来了
if (mInstance == null){
mInstance = new SingleTon2() ;
}
}
}
return mInstance ;
}
}
4>:单例 - 自成一派写法
其实只要满足整个程序中只有一个实例对象就可以,代码可以随便写
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 12:25
* Version 1.0
* Params:
* Description: 单例 - 自成一派
*/
public class SingleTon {
private static SingleTon mInstance ;
static {
mInstance = new SingleTon() ;
}
private SingleTon(){
}
public static SingleTon getInstance(){
return mInstance ;
}
}
代码已上传至github:
https://github.com/shuai999/Architect_day8.git
1. 概述
前两篇文章已经讲解了单例设计模式的定义、volatile关键字的好处、及常见单例设计模式的几种写法,那么这篇文章就记录下单例设计模式使用场景 —— 强大的Activity的管理。
2. 单例使用场景
如上图所示
场景一:
1>:点击收藏,如果没有登录,先跳转到登录页面,如果没有注册,就去注册;
2>:注册成功并且登录成功后,然后保存用户信息;
3>:关闭注册页面及登录页面;
场景二:
单点登录
如果后台通知,该账号已经被挤下线了,那么不管你在哪个页面,都需要弹出dialog,而且必须是Activity的上下文,是否需要在每个Activity中写代码,或者说直接写到 BaseActivity中即可;
以上都是单例设计模式的使用场景。
对于场景1:定义一个ActivityManager管理类,将其写成单例设计模式,并且用Stack栈,类型是Activity,管理整个Activity,然后在每个Activity的onCreate()中调用 ActivityManager的 attach()方法,把Activity添加到栈中,在每个Activity的onDestroy()方法中,调用 detach()方法,把Activity从栈中移除,代码如下;
以下场景是:从MainActivity跳转到 LoginActivity,然后跳转到RegisterActivity,最终关闭RegisterActivity和LoginActivity,回到MainActivity
1>:ActivityManager管理类代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 16:30
* Version 1.0
* Params:
* Description: Activity的栈管理
*/
public class ActivityManager {
private static volatile ActivityManager mInstance;
// 集合 有 List LinkedList Stack ?
// 由于删除和添加比较多,所以可以使用 Stack栈
private Stack<Activity> mActivities;
private ActivityManager(){
mActivities = new Stack<>();
}
// 双重校验
public static ActivityManager getInstance() {
if (mInstance == null) {
synchronized (ActivityManager.class) {
if (mInstance == null) {
mInstance = new ActivityManager();
}
}
}
return mInstance;
}
/**
* 添加统一管理
*/
public void attach(Activity activity){
mActivities.add(activity);
}
/**
* 移除解绑 - 防止内存泄漏
*/
public void detach(Activity detachActivity){
// for循环 一边循环一边移除会出问题 ,
/*for (Activity activity : mActivities) {
if(activity == detachActivity){
mActivities.remove(activity);
}
}*/
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
if (activity == detachActivity) {
mActivities.remove(i);
i--;
size--;
}
}
}
/**
* 关闭当前的 Activity
*/
public void finish(Activity finishActivity){
// for循环 一边循环一边移除会出问题 ,
/*for (Activity activity : mActivities) {
if(activity == finishActivity){
mActivities.remove(activity);
activity.finish();
}
}
*/
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
if (activity == finishActivity) {
mActivities.remove(i);
activity.finish();
i--;
size--;
}
}
}
/**
* 根据Activity的类名关闭 Activity
*/
public void finish(Class<? extends Activity> activityClass){
// for循环 一边循环一边移除会出问题 ,
/*for (Activity activity : mActivities) {
if(activity.getClass().getCanonicalName().equals(activityClass.getCanonicalName())){
mActivities.remove(activity);
activity.finish();
}
}*/
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
if (activity.getClass().getCanonicalName().equals(activityClass.getCanonicalName())) {
mActivities.remove(i);
activity.finish();
i--;
size--;
}
}
}
/**
* 退出整个应用
*/
public void exitApplication(){
}
/**
* 获取当前的Activity(最前面)
*/
public Activity currentActivity(){
return mActivities.lastElement();
}
}
2>:MainActivity代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityManager.getInstance().attach(this);
setContentView(R.layout.activity_main);
setTitle("MainActivity");
}
public void click(View view){
Intent intent = new Intent(this,LoginActivity.class);
startActivity(intent);
}
@Override
protected void onDestroy() {
ActivityManager.getInstance().detach(this);
super.onDestroy();
}
}
3>:LoginActivity代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 16:59
* Version 1.0
* Params:
* Description:
*/
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityManager.getInstance().attach(this);
setContentView(R.layout.activity_main);
setTitle("LoginActivity");
}
public void click(View view){
Intent intent = new Intent(this,RegisterActivity.class);
startActivity(intent);
}
@Override
protected void onDestroy() {
ActivityManager.getInstance().detach(this);
super.onDestroy();
}
}
4>:RegisterActivity代码如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/6 16:47
* Version 1.0
* Params:
* Description:
*/
public class RegisterActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityManager.getInstance().attach(this);
setContentView(R.layout.activity_main);
setTitle("RegisterActivity");
}
public void click(View view){
// 不光要关闭自己还要关闭 LoginActivity 让其回到主页
ActivityManager.getInstance().finish(this);
ActivityManager.getInstance().finish(LoginActivity.class);
}
@Override
public void onDestroy() {
ActivityManager.getInstance().detach(this);
super.onDestroy();
}
}
代码已上传至github:
https://github.com/shuai999/Architect_day8.git
使用单例模式,小心内存泄漏了喔~
单例模式的静态特性导致它的对象的生命周期是和应用一样的,如果不注意这一点就可能导致内存泄漏。下面看看常见的2种情况
Context的泄漏
//SingleInstance.class
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}
public static SingleInstance getInstance(Context context) {
if (mSingleInstance == null) {
synchronized (SingleInstance.class) {
if (mSingleInstance == null) {
mSingleInstance = new SingleInstance(context);
}
}
}
return mSingleInstance;
}
//MyActivity
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这样就容易出问题了
SingleInstance singleInstance = SingleInstance.getInstance(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
如上面那样直接传入MyActivity的引用,如果当前MyActivity退出了,但应用还没有退出,singleInstance一直持有MyActivity的引用,MyActivity就不能被回收了。
解决方法也很简单,传入ApplicationContext就可以了。
SingleInstance singleInstance = SingleInstance.getInstance(getApplicationContext());
View的泄漏
如果单例模式的类中有跟View相关的属性,就需要注意了。搞不好也会导致内存泄漏,原因和上面分析的原因一样。
//SingleInstance.class
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}
public static SingleInstance getInstance(Context context) {
if (mSingleInstance == null) {
synchronized (SingleInstance.class) {
if (mSingleInstance == null) {
mSingleInstance = new SingleInstance(context);
}
}
}
return mSingleInstance;
}
//单例模式中这样持有View的引用会导致内存泄漏
private View myView = null;
public void setMyView(View myView) {
this.myView = myView;
}
解决方案是采用弱引用
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}
public static SingleInstance getInstance(Context context) {
if (mSingleInstance == null) {
synchronized (SingleInstance.class) {
if (mSingleInstance == null) {
mSingleInstance = new SingleInstance(context);
}
}
}
return mSingleInstance;
}
// private View myView = null;
// public void setMyView(View myView) {
// this.myView = myView;
// }
//用弱引用
private WeakReference<View> myView = null;
public void setMyView(View myView) {
this.myView = new WeakReference<View>(myView);
}
很多东西虽然简单,还是有我们需要注意的地方。这就需要我们理解它们的特性了。比如上面用了弱引用来解决内存泄漏的问题,那我们就需要明白弱引用的特点,需要注意使用弱引用的变量可能为空的问题
被弱引用关联的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象