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

bakd9h0s  于 2023-01-15  发布在  Android
关注(0)|答案(3)|浏览(161)

我有一个名为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

我不知道为什么会发生这种情况。任何想法,答案或建议将不胜感激。

afdcj2ne

afdcj2ne1#

我强烈建议你在这里停止使用单例,正如你的问题所表明的,单例并不容易正确实现,特别是如果你在惰性地初始化它们。
但是,我相信您的代码在这里不起作用的原因是面临着一个基本的内存可见性问题:不能保证多个线程看到非易失性变量的最新值。
(我假设您不只是有另一段代码没有共享,在那里您将sSyncPrefs再次赋值给null)。
为了“正确地”实现这个功能(我使用这个术语并不严格,因为我认为单例不合适),您需要使用double-checked locking
1.将sSyncPrefs变量设为volatile

private static volatile SyncPrefs sSyncPrefs;

这确保了对sSyncPrefs的更新不会被线程缓存,并且总是从主存中读取该值。
1.检查空值时使用同步:

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检查允许您在变量初始化后跳过同步;如果发现它为空,则同步确保没有其它线程同时更新该值。
1.您还需要在无参数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;
}
elcex8rz

elcex8rz2#

您有2个getInstance(),其中一个总是返回null,除非您之前调用了另一个。

plupiseo

plupiseo3#

你需要在构造函数中设置Static,这样当singleton被初始化时,它会设置TAG,你列出的代码也没有你正在调用的方法。SyncPrefs是一个singleton,所以不应该有公共构造函数,并且构造函数应该在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;
    }
}

相关问题