I have the following code in my Conversation class:
private ConversationListener conversationListener = null;
private final Integer conversationListenerLock = 0;
public ConversationListener getConversationListener() {
synchronized (conversationListenerLock) {
return conversationListener;
}
}
I am now sometimes getting the following exception:
java.lang.NullPointerException: Null reference used for synchronization (monitor-enter)
at com.mobileoct.shared.data.Conversation.getConversationListener(Conversation.java:283)
at com.mobileoct.android.colposcope.main.ConversationManager.loadConversationsForUser(ConversationManager.java:260)
at com.mobileoct.android.colposcope.main.ConversationManager$1.run(ConversationManager.java:305)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
It appears that, somehow, conversationListenerLock is null. How could that possibly be? How could I get into this public, non-static method of the Conversation class without having initialized the conversationListenerLock value?
And how can I fix it so that that doesn't happen?
(As is clear from the stacktrace, this is running on Android, and thus appears to be some indeterminate version of Java that closely approximates Java 7.something.)
EDIT: To respond to some of the questions in the comments:
I am running this on a Samsung Galaxy J5 (SM-J500H), running Android 6.0.1. (I haven't yet run it on other devices, but this is the primary target device, so that doesn't matter much).
The only constructor for this class is:
public Conversation(String sessionId, String name, String ownerId, String userId) {
dataType = DATA_TYPE;
this.setId(UUID.randomUUID().toString());
this.setSessionId(sessionId);
this.setName(name);
this.setOwnerId(ownerId);
this.setUserId(userId);
}
However, this class is sometimes instantiated by Gson, as in:
Conversation conversation = new GsonBuilder().create().fromJson(jsonString, Conversation.class);
(There's more stuff in there between the new GsonBuilder() and the .create(), but I'm pretty certain it's not relevant.)
I don't know enough about unsafe publication to know whether that might fit the bill, but it seems like a likely culprit.
EDIT #2 - here's "all code directly and indirectly invoked from the ctor, and the initializers of all other fields" (some copied from the parent class):
public static final String DATA_TYPE = "conversation";
private static final Class<?> s_postServlet = AddConversation.class;
protected String id;
private String sessionId;
private String name;
private String ownerId; // the user that created the conversation
private Boolean deleted = false;
private Date lastUpdated = new Date();
private User user;
private User owner;
private Session session;
private SortedSet<Collaborator> collaborators = new TreeSet<>();
private SortedSet<Message> messages = new TreeSet<>();
private Set<Notification> notifications = new HashSet<>();
private ConversationListener conversationListener = null;
private final Object conversationListenerLock = new Object();
// From parent class
protected String dataType;
protected Date timestamp = new Date();
protected String userId;
public Conversation(String sessionId, String name, String ownerId, String userId) {
dataType = DATA_TYPE;
this.setId(UUID.randomUUID().toString());
this.setSessionId(sessionId);
this.setName(name);
this.setOwnerId(ownerId);
this.setUserId(userId);
}
public void setId(String id) {
this.id = id;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public void setName(String name) {
this.name = name;
}
public void setOwnerId(String ownerId) {
this.ownerId = ownerId;
}
// From parent class
public void setUserId(String userId) {
this.userId = userId;
}