Java 单例实例返回null

Java 单例实例返回null,java,android,encryption,singleton,Java,Android,Encryption,Singleton,在我的应用程序中,我使用用户密码作为加密介质的加密密钥。我使用PBEWithMD5和DES加密媒体,使用存储在共享首选项中的密码可以很好地工作。现在为了达到一个安全级别,我将从共享首选项中删除密码,并使用一个仅在应用程序会话期间保持活动状态的单例,因为应用程序自动注销时需要输入密码。以下是我的单身汉: public class Credentials { private static Credentials dataObject = null; private Credentials() {

在我的应用程序中,我使用用户密码作为加密介质的加密密钥。我使用PBEWithMD5和DES加密媒体,使用存储在共享首选项中的密码可以很好地工作。现在为了达到一个安全级别,我将从共享首选项中删除密码,并使用一个仅在应用程序会话期间保持活动状态的单例,因为应用程序自动注销时需要输入密码。以下是我的单身汉:

public class Credentials {

private static Credentials dataObject = null;

private Credentials() {
// left blank intentionally
}

public static Credentials getInstance() {
if (dataObject == null)
    dataObject = new Credentials();
return dataObject;
}

private char[] user_password;

public char[] getUser_password() {

return user_password;
}

 public void setUser_password(char[] user_password) {

this.user_password = user_password;
}
}
如果应用程序注销、用户注销或被销毁,则密码将从内存中清零。但是,在尝试检索密码时,有时会收到一个空指针

   char[] pswd = Credentials.getInstance().getUser_password();

这可能是什么原因造成的?除了singleton之外,我还可以使用其他方法吗?

或者,您可以使用内置的Sqlite db存储密码,尽管我仍然建议您将其保存为加密以获得最大保护。您可以通过4个步骤完成此操作:

2创建实体对象以存储密码:

public class Password {
    int password_id; // will be auto-increamted
    String password;

    public Password(int password_id, String password) {
        this.password_id = password_id;
        this.password = password;
    }
// getter/setters ...
}
2创建Sqlite实用程序对象:

public class SQLiteDBAdapter {

    protected static final String DATABASE_NAME = "mydb";
    protected static final int DATABASE_VERSION = 1;

    protected Context context;
    protected static DatabaseHelper mDbHelper;

    public static final String TABLE_PASSWORD = "tbl_password";
    // columns
    public static final String PASSWORD_ID = "_id";
    public static final String PASSWORD = "password";
    // create table string
    private static final String CREATE_TABLE_PASSWORD =
            "CREATE TABLE if not exists " + TABLE_PASSWORD + " ( " +
                    PASSWORD_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    PASSWORD + " TEXT NOT NULL);";

    public SQLiteDBAdapter(Context context) {
      context = context.getApplicationContext();
    }

    public SQLiteDatabase openDb() {
      if (mDbHelper == null) {
          mDbHelper = new DatabaseHelper(mContext);
      }
      return mDbHelper.getWritableDatabase();
    }

    protected static class DatabaseHelper extends SQLiteOpenHelper {
      // -------------------------------------------------------------------------------------------
      public DatabaseHelper(Context context) {
          super(context, DATABASE_NAME, null, DATABASE_VERSION);
      }
      // -------------------------------------------------------------------------------------------
      @Override
      public void onCreate(SQLiteDatabase db) {
          db.execSQL(CREATE_TABLE_PASSWORD);
      }
      // -------------------------------------------------------------------------------------------
      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
          Log.w(TAG, "Upgrading database from version " + oldVersion + " to " +
                  newVersion + ", which will destroy all old data");
          db.execSQL("DROP TABLE IF EXISTS routes");
          onCreate(db);
      }
    }
}
3扩展Sqlite对象以操作表CRUD操作:

public class PasswordDbAdapter extends SQLiteDBAdapter {

    private SQLiteDatabase db;

    // these are column corresponding indices
    public static final int INDEX_PASSWORD_ID = 0;  // an auto-increment
    public static final int INDEX_PASSWORD = 1;

    public PasswordDbAdapter(Context context) {
        super(context);
    }

    public void addPassword(String password) {
        db = openDb();
        ContentValues values = new ContentValues();
        values.put(PASSWORD, password);
        db.insert(TABLE_PASSWORD, null, values);
    }

    public void updatePassword(String password) {
        db = openDb();
        ContentValues values = new ContentValues();
        values.put(PASSWORD, password);
        db.update(TABLE_PASSWORD, values, null);
    }

    public void deletePassword() {
        db = openDb();
        db.delete(TABLE_PASSWORD, null, null);
    }

    public boolean isEmpty() {
        db = openDb();
        boolean empty = true;
        Cursor cur = db.rawQuery("SELECT COUNT(*) FROM " + TABLE_PASSWORD, null);
        if (cur != null && cur.moveToFirst()) {
            empty = (cur.getInt (0) == 0);
        }
        cur.close();
        return empty;
    }

    public Password fetchPassword() {   // ok because there's only one password record
        db = openDb();
        Cursor cursor = db.query(TABLE_PASSWORD, new String[]{PASSWORD_ID, PASSWORD},
                null, null, null, null, null, null);
        if (cursor != null &&
            cursor.moveToFirst()) {
            return new Password(
                    cursor.getString(INDEX_PASSWORD_ID),
                    cursor.getInt(INDEX_PASSWORD));
        }
        return null;
    }
}
4最后,根据需要保存/更新/检索密码:

public class MainActivity extends AppCompatActivity {
    private PasswordDbAdapter passwordDB; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ... 
        // initialize the password db
        passwordDB = new PasswordDbAdapter(this);

        // check if password record exists
        if (passwordDB.isEmpty() {
            // save a new copy
            passwordDB.addPassword("the_password"); // more secure if it is saved encrypted
        } else {
            // update it
            passwordDB.updatePassword("the_password");
        }

    }
    ...
    public String fetchPassword() {
        return passwordDB.fetchPassword();  // or first decrypt it, then return it
    }
}

Credentials.getInstance不能返回null。这是因为您从未在该实例上调用过setUser\u password,所以Credentials.getInstance.getUser\u password会这样做。如果您认为使用非null参数调用了setUser\u password,那么问题在于您的singleton类不是线程安全的。@James Wahome:您的密码是null而不是singleton!!为singleton实例和其函数返回的值添加对null对象的正确检查。@AndyTurner我确信调用了setUser_password,并且确实存在一个值退出,问题是在一段时间后它变为null,因此我无法使用它,如果您认为调用setUser_password时使用了非null参数,问题是你的singleton类不是线程安全的..这是错误的..现在通读问题以达到安全级别我正在从共享首选项中删除密码并使用仅在应用程序会话期间保持活动的singleton我的目标是将其存储在运行时内存中,而不是将其保存在磁盘的共享pref中存储在任何情况下,您都不应该将密码存储在纯文本中。Android Singleton可能会有很大的问题。但是,你看。现在,关于使用Sqlite,我的建议是存储加密的密码。更简单的是,您可以对密码进行哈希运算,然后改用哈希运算。