Android设计模式:Singleton模式

什么是单例模式?

Singleton模式是一种软件设计模式 ,可确保一个类只有一个实例,并且该类提供了对其的全局访问点。 每当多个类或客户端请求该类时,它们都会获得该类的相同实例。 这个Singleton类可能负责实例化其自身,或者您可以将对象创建委托给factory类。

让我们以手机及其所有者为例。 一个电话通常由一个人拥有,而一个人可以拥有许多电话。 这些电话之一响起时,相同的所有者接听电话。

单例模式的好处

在典型的Android应用程序中,无论您是直接使用它还是直接将其传递给另一个类,对于许多对象我们只需要一个全局实例。 示例包括缓存, OkHttpClientHttpLoggingInterceptorRetrofitGsonSharedPreferences ,存储库类等。如果我们要实例化这些类型的对象中的一种以上, HttpLoggingInterceptor遇到诸如应用程序行为不正确,资源过度使用以及其他问题的问题。令人困惑的结果。

实作

实现这种模式非常容易。 以下代码段显示了如何创建Singleton。

public class Singleton  {

    private static Singleton INSTANCE = null;

    // other instance variables can be here
    
    private Singleton() {};

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return(INSTANCE);
    }
    
    // other instance methods can follow 
}

在上面的代码中,我们有一个静态变量INSTANCE来保存该类的实例。 我们也将构造函数设为私有,因为我们要强制执行非实例性-该类只能实例化其自身。 方法getInstance()保证该类被实例化(如果尚未实例化),并将其返回给调用者。

示例:创建改造的单个实例

在Android应用程序中,您将需要Retrofit对象的单个全局实例,以便应用程序的其他部分(例如UserProfileActivitySettingsActivity可以使用它来执行网络请求,而无需每次我们都需要创建实例它。 创建多个实例将使用未使用的改造对象污染我们的应用程序,从而在已经受到内存限制的移动设备上占用不必要的内存。

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
 
public class RetrofitClient {
 
    private static Retrofit retrofit = null;
 
    public static Retrofit getClient(String baseUrl) {
        if (retrofit==null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

因此,只要客户端A调用RetrofitClient.getClient() ,它就会创建实例(如果尚未创建),然后客户端B调用此方法时,它将检查Retrofit实例是否已存在。 如果是这样,它将实例返回给客户端B而不是创建新实例。

处理多线程

在Android系统中,您可以拆分多个线程来执行不同的任务。 这些线程最终可能同时执行同一代码块。 对于上面的Singleton类,这可能导致创建多个对象实例,这违反了Singleton的约定。 因此,我们的Singleton代码片段方法getInstance()不是线程安全的。 现在,我们将研究使其线程安全的方法。

同步getInstance()方法

使单例代码线程安全的方法之一是使方法getInstance() 同步的 。 这样做仅允许一个线程一次运行该方法,从而迫使其他所有线程处于等待或阻塞状态。

public class Singleton  {

    private static Singleton INSTANCE = null;

    // other instance variables can be here
    
    private Singleton() {};

    public static synchronized Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return(INSTANCE);
    }
    
    // other instance methods can follow 
}

这种方法使我们的代码线程安全,但是这是一个昂贵的操作。 换句话说,这会降低性能。 因此,您必须进行调查,看看性能成本是否值得在您的应用程序中使用。

急于创建实例

处理访问单例的多个线程的另一种方法是在加载或初始化类时立即创建Singleton实例(由Dalvik VM中的Android ClassLoader进行)。 这使代码线程安全。 然后,在任何线程访问INSTANCE变量之前,对象实例将已经可用。

public class Singleton  {

    private static Singleton INSTANCE = new Singleton();

    // other instance variables can be here
    
    private Singleton() {};

    public static Singleton getInstance() {
       return(INSTANCE);
    }
    
    // other instance methods can follow 
}

这种方法的缺点是您最终可能会创建一个永远不会使用的对象,从而占用了不必要的内存。 因此,通常只有在确定将访问单例时,才应使用此方法。

奖励:使用匕首2

依赖注入库(例如Dagger)可以帮助您连接对象依赖关系,并使用@Singleton批注创建单例。 这将确保在整个应用程序生命周期中仅将对象初始化一次。

@Module
public class NetworkModule {

    @Provides
    @Singleton
    public Gson gson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    public HttpLoggingInterceptor loggingInterceptor() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(
                message -> Timber.i(message));
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        return interceptor;
    }

    @Provides
    @Singleton
    public Cache cache(File cacheFile) {
        return new Cache(cacheFile, 10 * 1000 * 1000); //10MB Cache
    }

    @Provides
    @Singleton
    public File cacheFile(@ApplicationContext Context context) {
        return new File(context.getCacheDir(), "okhttp_cache");
    }

    @Provides
    @Singleton
    public OkHttpClient okHttpClient(HttpLoggingInterceptor loggingInterceptor, Cache cache) {
        return new OkHttpClient.Builder()
                .addInterceptor(loggingInterceptor)
                .cache(cache)
                .build();
    }

    @Provides
    @Singleton
    public Retrofit retrofit(OkHttpClient okHttpClient, Gson gson) {
        return new Retrofit.Builder()
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(okHttpClient)
                .baseUrl("you/base/url")
                .build();
    }
}

在上面的代码中,我们创建了GsonCacheFileOkHttpClient以及最后Retrofit类型的单个实例,这些实例将由Dagger生成的依赖关系图提供。

结论

在这个简短的教程中,您了解了Android中的Singleton模式:它的含义,使用它的好处,如何通过编写自己的模式来实现它以及处理多个线程的一些方法。 我还向您展示了如何使用第三方库,例如Dagger 2。

翻译自: https://code.tutsplus.com/tutorials/android-design-patterns-the-singleton-pattern--cms-29153

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值