我有一个名为SyncPrefs
的单例SharedPreferences
助手类。
public class SyncPrefs {
public static final String TAG = SyncPrefs.class.getSimpleName();
private static final String HrEmployeeSyncFinished = "hrEmployeeSyncFinished";
private SharedPreferences mPrefs;
private SyncFinishedListener mSyncFinishedListener;
private static SyncPrefs sSyncPrefs;
private SyncPrefs(Context context, final Employees employees) {
mPrefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
mSyncFinishedListener = new SyncFinishedListener() {
@Override
public void onSyncFinished() {
employees.mSyncFinishedListener.onSyncFinished();
}
};
// This call start sync & make isSyncFinished() getting called
SyncUtils.get(context).requestSync(HrEmployee.AUTHORITY);
}
public static SyncPrefs getInstance(Context context, final Employees employees) {
if (sSyncPrefs == null) {
sSyncPrefs = new SyncPrefs(context, employees);
Log.e(TAG, "getInstance(Context, Employees) called");
Log.e(TAG, "sSyncPrefs initialized at: " + sSyncPrefs);
}
return sSyncPrefs;
}
public static SyncPrefs getInstance() {
Log.e(TAG, "getInstance() called");
Log.e(TAG, "sSyncPrefs is: " + sSyncPrefs);
return sSyncPrefs;
}
private boolean isSyncFinished() {
boolean isSyncFinished = isHrEmployeeSyncFinished();
// isSyncFinished = true;
Log.e(TAG, "isSyncFinished is :" + isSyncFinished);
if (isSyncFinished) {
try {
setHrEmployeeSyncFinished(false);
mSyncFinishedListener.onSyncFinished();
} catch (Exception e) {
e.printStackTrace();
}
}
return isSyncFinished;
}
private boolean isHrEmployeeSyncFinished() {
return mPrefs.getBoolean(HrEmployeeSyncFinished, false);
}
public SyncPrefs setHrEmployeeSyncFinished(boolean hrEmployeeSyncFinished) {
mPrefs.edit().putBoolean(HrEmployeeSyncFinished, hrEmployeeSyncFinished).apply();
if (hrEmployeeSyncFinished) {
isSyncFinished();
}
return this;
}
}
上面的代码应该运行良好。但是,不知何故,我不能初始化static
成员sSyncPrefs
。我已经确认sSyncPrefs
正在初始化。但是,当我调用getInstance()
时,它总是返回null
。
下面是一些日志:
E/SyncPrefs: getInstance(Context, Employees) called
// Look here, it has memory address
E/SyncPrefs: sSyncPrefs initialized at: com.odoo.addons.employees.utils.SyncPrefs@7d0e0a5
E/HrEmployee: onSyncStarted
E/HrEmployee: onSyncFinished
E/SyncPrefs: getInstance() called
// now, where the memory address gone?
E/SyncPrefs: sSyncPrefs is: null
我不知道为什么会发生这种情况。任何想法,答案或建议将不胜感激。
3条答案
按热度按时间afdcj2ne1#
我强烈建议你在这里停止使用单例,正如你的问题所表明的,单例并不容易正确实现,特别是如果你在惰性地初始化它们。
但是,我相信您的代码在这里不起作用的原因是面临着一个基本的内存可见性问题:不能保证多个线程看到非易失性变量的最新值。
(我假设您不只是有另一段代码没有共享,在那里您将
sSyncPrefs
再次赋值给null
)。为了“正确地”实现这个功能(我使用这个术语并不严格,因为我认为单例不合适),您需要使用double-checked locking:
1.将
sSyncPrefs
变量设为volatile
:这确保了对
sSyncPrefs
的更新不会被线程缓存,并且总是从主存中读取该值。1.检查空值时使用同步:
第一个
null
检查允许您在变量初始化后跳过同步;如果发现它为空,则同步确保没有其它线程同时更新该值。1.您还需要在无参数
getInstance()
方法中使用双重检查锁定,以添加变量实际上已经初始化的检查。elcex8rz2#
您有2个
getInstance()
,其中一个总是返回null
,除非您之前调用了另一个。plupiseo3#
你需要在构造函数中设置Static,这样当singleton被初始化时,它会设置TAG,你列出的代码也没有你正在调用的方法。SyncPrefs是一个singleton,所以不应该有公共构造函数,并且构造函数应该在getInstance上或静态块中检查(Spring的做法)