无法在Java中的Singleton类中初始化静态成员 - Android

时间:2016-04-20 07:20:15

标签: java android singleton

我是单身SharedPreferences助手类名SyncPrefs

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 个答案:

答案 0 :(得分:3)

强烈建议您在这里停止使用单身人士。正如你的问题所示,单身人士不容易正确实施,特别是如果你懒洋洋地初始化它们。

但是,我认为您的代码在此处不起作用的原因是面临基本的内存可见性问题:无法保证多个线程会看到非易失性变量的最新值。

(我假设你不是只是另外一些你没有分享的代码,你在sSyncPrefsnull的另一个分配。)

为了“正确”实现这个(我使用松散的术语,因为我不认为单身是合适的),你需要使用double-checked locking

  1. 制作sSyncPrefs变量volatile

    private static volatile SyncPrefs sSyncPrefs;
    

    这可确保线程不缓存对sSyncPrefs的更新,并始终从主内存中读取该值。

  2. 检查无效时使用同步:

    public static SyncPrefs getInstance(Context context, final Employees employees) {
      if (sSyncPrefs == null) {
        synchronized (SyncPrefs.class) {
          if (sSyncPrefs == null) {
            sSyncPrefs = new SyncPrefs(context, employees);
            Log.e(TAG, "getInstance(Context, Employees) called");
            Log.e(TAG, "sSyncPrefs initialized at: " + sSyncPrefs);
          }
        }
      }
      return sSyncPrefs;
    }
    

    第一次null检查允许您在初始化变量后跳过同步;如果发现它为null,则同步确保没有其他线程同时更新该值。

  3. 您还需要在无参数getInstance()方法中使用双重检查锁定来添加对变量实际已初始化的检查。

    public static SyncPrefs getInstance() {
      if (sSyncPrefs == null) {
        synchronized (SyncPrefs.class) {
          if (sSyncPrefs == null) {
            throw new IllegalStateException("getInstance(Context, Employees) was not called first!");
          }
        }
      }
      return sSyncPrefs;
    }
    

答案 1 :(得分:0)

你有2 getInstance(),其中一个将永远返回null,除非您之前致电另一个。

答案 2 :(得分:-2)

你需要在构造函数中设置Static,这样当初始化单例时,它会设置TAG, 您列出的代码没有您要调用的方法。 SyncPrefs是一个单例,所以不应该有公共构造函数,构造函数应该在getInstance上或在静态块中检查(Spring的方式)

    public static class SyncPrefs {

     private static SyncPrefs instance;
     private static final String TAG;

     private SyncPrefs() {
         TAG = "Something";
    }

     public static SyncPrefs getInstance() {
        if(instance == null)
        { 
             instance = new SyncPrefs();
        }
        return instance;
    }
}