Android中的Singleton


74

我已遵循此链接,并成功在Android中制作了singleton类。 http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/

问题是我想要一个对象。像我有活动A和活动B。在活动AI中,从Singleton访问对象class。我使用该对象并对它进行了一些更改。

当我移至活动B并从Singleton类访问该对象时,它给了我初始化的对象,并且不保留我在活动A中所做的更改。是否还有其他方法可以保存更改?请高手帮帮我。这是MainActivity

public class MainActivity extends Activity {
    protected MyApplication app;        
    private OnClickListener btn2=new OnClickListener() {    
        @Override
        public void onClick(View arg0) {
            Intent intent=new Intent(MainActivity.this,NextActivity.class);
            startActivity(intent);              
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get the application instance
        app = (MyApplication)getApplication();

        // Call a custom application method
        app.customAppMethod();

        // Call a custom method in MySingleton
        Singleton.getInstance().customSingletonMethod();

        Singleton.getInstance();
        // Read the value of a variable in MySingleton
        String singletonVar = Singleton.customVar;

        Log.d("Test",singletonVar);
        singletonVar="World";
        Log.d("Test",singletonVar);

        Button btn=(Button)findViewById(R.id.button1);
        btn.setOnClickListener(btn2);
    }

}

这是 NextActivity

public class NextActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_next);

            String singletonVar = Singleton.customVar;

            Log.d("Test",singletonVar);
        }
  }

Singleton

public class Singleton
{
    private static Singleton instance;

    public static String customVar="Hello";

    public static void initInstance()
    {
    if (instance == null)
    {
      // Create the instance
      instance = new Singleton();
    }
    }

    public static Singleton getInstance()
    {
     // Return the instance
     return instance;
     }

     private Singleton()
     {
     // Constructor hidden because this is a singleton
     }

     public void customSingletonMethod()
     {
     // Custom method
     }
 }

MyApplication

public class MyApplication extends Application
    {
    @Override
    public void onCreate()
    {
    super.onCreate();

     // Initialize the singletons so their instances
     // are bound to the application process.
     initSingletons();
     }

     protected void initSingletons()
     {
     // Initialize the instance of MySingleton
     Singleton.initInstance();
     }

     public void customAppMethod()
     {
     // Custom application method
    }
}

当我运行此代码时,我得到了Hello,它已在我初始化的SingletonWorld中初始化,然后MainActivity再次NextActivity在logcat中显示Hello 。我希望它再次显示世界NextActivity。请帮助我纠正此问题。


在MainActivity中-为什么要两次调用Singleton.getInstance()?
IgorGanapolsky 2014年

1
这主要是因为调用String singletonVar = Singleton.customVar;导致。在Java中,此分配操作不会在内存中分配对Singleton.customVar的引用,它只是创建一个称为singletonVar的新变量,因此,当您更改SingltonVar时,您只需更改局部变量的值而不是singleton变量。
M_AWADI

旧的,但是String singletonVar = Singleton.customVar; 字符串是原始类型,因此它不是对象,您只需将当前值从单例复制到loca变量,然后修改局部变量的值
Pecana

链接有误
Slaknation

Answers:


47

编辑:

在Android中实现Singleton并不是“安全的”(请参阅此处),您应该使用专用于这种模式的库(例如Dagger或其他DI库)来管理生命周期和注入。


您可以从代码中发布示例吗?

看看这个要点:https : //gist.github.com/Akayh/5566992

它可以工作,但是很快就完成了:

MyActivity:首次设置单例+在私有构造函数中初始化mString属性(“ Hello”)并显示值(“ Hello”)

将新值设置为mString:“ Singleton”

启动activityB并显示mString值。出现“ Singleton” ...


这是活动之间单例的示例代码。如果您需要更多帮助,请发布代码(或简单的示例)以查看问题
Akayh,2013年

3
这仅仅是因为字符串是在singletonVar中复制的,而不是对其的引用。如果直接按如下方式更改静态变量:Singleton.customVar =“ World”; 是工作。
Akayh

6
请注意,这不是线程安全的!永远记住墨菲定律。使线程安全的一种方法是使用方法中的synchronized密钥getInstance()
dazito

4
@Akayh您能解释一下为什么Singleton在Android上不安全吗?它是仅对活动不安全还是对应用程序也不安全的安全?
萨巴岛

1
“ Android中的Singleton不安全”-需要参考和理想的解释。
noelicus

61

提示:创建单例类在Android Studio中,右键单击您的项目并打开菜单:

New -> Java Class -> Choose Singleton from dropdown menu

在此处输入图片说明


3
不再可用
Thecave3'4

32

很简单,作为Java,Android也支持单例。--

Singleton是“四人帮”设计模式的一部分,并且归类于创新设计模式。

->静态成员:它包含单例类的实例。

->私有构造函数:这将阻止其他人实例化Singleton类。

->静态公共方法:这提供对Singleton对象的全局访问点,并将实例返回给客户端调用类。

  1. 创建私有实例
  2. 创建私有构造函数
  3. 使用Singleton类的getInstance()

    public class Logger{
    private static Logger   objLogger;
    private Logger(){
    
            //ToDo here
    
    }
    public static Logger getInstance()
    {
        if (objLogger == null)
       {
          objLogger = new Logger();
       }
       return objLogger;
       }
    
    }
    

在使用单例时-

Logger.getInstance();

这与发布的用户最初的单例实现有何不同?
IgorGanapolsky

18

rakesh建议的答案很好,但仍带有一些描述性含义Android中的Singleton与Java中的Singleton相同:Singleton设计模式解决了所有这些问题。使用Singleton设计模式,您可以:

1)确保仅创建一个类的实例

2)提供对对象的全局访问点

3)将来允许多个实例,而不会影响单例类的客户端

一个基本的Singleton类示例:

public class MySingleton
{
    private static MySingleton _instance;

    private MySingleton()
    {

    }

    public static MySingleton getInstance()
    {
        if (_instance == null)
        {
            _instance = new MySingleton();
        }
        return _instance;
    }
}

1
绝对这是最好的方法,因为它构造了一个惰性构造函数,换句话说,它仅getInstance()在首次调用时被实例化。
恩戈洛·波洛托(Angelo Polotto)'18

14

如@Lazy在此答案中所述,您可以从Android Studio中的模板创建单例。值得注意的是,由于ourInstance首先初始化了静态变量,因此无需检查实例是否为null 。结果,Android Studio创建的单例类实现就像下面的代码一样简单:

public class MySingleton {
    private static MySingleton ourInstance = new MySingleton();

    public static MySingleton getInstance() {
        return ourInstance;
    }

    private MySingleton() {
    }
}

1
这是最简单,最快的解决方案。如果您在循环中多次调用getInstance(),则其他检查为null的答案将明显变慢。
BitByteDog

5

您要将单例的复制customVarsingletonVar变量中,并且更改该变量不会影响单例中的原始值。

// This does not update singleton variable
// It just assigns value of your local variable
Log.d("Test",singletonVar);
singletonVar="World";
Log.d("Test",singletonVar);

// This actually assigns value of variable in singleton
Singleton.customVar = singletonVar;

将customVar设为私有静态变量并为其创建访问器方法会更安全吗?
IgorGanapolsky

2

我将我的Singleton版本放在下面:

public class SingletonDemo {
    private static SingletonDemo instance = null;
    private static Context context;

    /**
     * To initialize the class. It must be called before call the method getInstance()
     * @param ctx The Context used

     */
    public static void initialize(Context ctx) {
     context = ctx;
    }

    /**
     * Check if the class has been initialized
     * @return true  if the class has been initialized
     *         false Otherwise
     */
    public static boolean hasBeenInitialized() {
     return context != null;

    }

    /**
    * The private constructor. Here you can use the context to initialize your variables.
    */
    private SingletonDemo() {
        // Use context to initialize the variables.
    }

    /**
    * The main method used to get the instance
    */
    public static synchronized SingletonDemo getInstance() {
     if (context == null) {
      throw new IllegalArgumentException("Impossible to get the instance. This class must be initialized before");
     }

     if (instance == null) {
      instance = new SingletonDemo();
     }

     return instance;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Clone is not allowed.");
    }
}

注意,可以在主类(Splash)中调用initialize方法,而从其他类中调用getInstance方法。当调用方类需要单例但没有上下文时,这将解决问题。

最后,方法hasBeenInitialized用于检查类是否已初始化。这样可以避免不同的实例具有不同的上下文。


Android Studio将此问题视为一个问题-内存泄漏-因为您在另一个类的静态“实例”中设置了Context。现在,这种模式已无用了
narkis

@narkis有趣。如果这是真的,那么当人们在CustomApplication内部拥有Context的静态实例时,也会产生内存泄漏。您应该为我们
加油

1

在Android中使用单例的最干净,最现代的方法就是使用称为Dagger 2依赖注入框架。在这里,您可以解释可能使用的范围。单例是这些范围之一。依赖注入不是那么容易,但是您应该花一些时间来理解它。这也使测试更加容易。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.