My app is crashing when I create a new comment for a post with notifyDataSetChanged() that is linked to an adapter that shows time. All Data is taken from parse.com where I store all my database information. The freshly made comment is supposed to immediately shown in the listview in the same activity showing the comment and the time. The time is taken by createdAt() that is substracted by currentTime.
the error says:
java.lang.NullPointerException: Attempt to invoke virtual method 'long java.util.Date.getTime()' on a null object reference
and this is the button that will add the comment:
//Add a new comment
mAddComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("log", "add button clicked");
final ParseObject myComment = new ParseObject("Comments");
final String comment = mComment.getText().toString();
myComment.put("CommentOwner", currentUserUsername);
myComment.put("Comment", comment);
myComment.put("SocialId", ParseObject.createWithoutData("Social", socialId));
myComment.saveInBackground();
adapter.setNotifyOnChange(false);
adapter.add(myComment);
adapter.notifyDataSetChanged();
setListAdapter(adapter);
if (adapter.isEmpty()) {
} else {
mList.setSelection(mList.getAdapter().getCount() - 1);
}
mComment.setText("");
}
});
and this is the thing inside the adapter that shows the time:
public SocialCommentAdapter(Context context, List<ParseObject> Comment) {
super(context, R.layout.socialcommentcustomlayout, Comment);
mContext = context;
mComment = Comment;
}
.
.
.
.
.
final ParseObject commentObject = mComment.get(position);
//get Time
Date created = commentObject.getCreatedAt();
Date currentDate = new Date();
long timepast = currentDate.getTime() - created.getTime(); <<<< The Error points to this line of the code
long days = TimeUnit.MILLISECONDS.toDays(timepast);
timepast -= TimeUnit.DAYS.toMillis(days);
long hours = TimeUnit.MILLISECONDS.toHours(timepast);
timepast -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(timepast);
timepast -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(timepast);
StringBuilder sb = new StringBuilder(64);
if( days > 0){
sb.append(days);
sb.append("d");
}else if (hours>0){
sb.append(hours);
sb.append("h");
} else if (minutes >0){
sb.append(minutes);
sb.append("m");
}else if ( seconds >0){
sb.append(seconds);
sb.append("s");
}
holder.timeComment.setText(sb.toString());
Do you guys know what's wrong with this? Can you guys help me out?
Thank you
I know you just answered your own question, but I was in the middle of writing my answer and wanted to add an explanation as to why your variable is null, so I'll just post my answer anyway.
First of all, commentObject.getCreatedAt(); a few lines above your crash is returning null and so your created object is null, and that is the cause of your crash. Now for an explanation.
Reading the documentation on getCreatedAt(), we see that it returns the time which will be
...the creation time will be the time of the first ParseObject.save() call rather than the time the object was created locally.
Now, we can see that you have called myComment.saveInBackground(); in your onClick method on a new ParseObject, and added that object to a list. You then call adapter.notifyDataSetChanged(); which triggers the getView method where your code is crashing. Your saveInBackground method should save the object, but, it is going to do it in the background... a different thread from the thread you are creating the object on.
So the way I understand the documentation and what we are seeing here, getCreatedAt() will return null until the ParseObject is saved, at which time it will return the Date object with the correct data. Because you are calling saveInBackground instead of save, the saving happens on a background thread, which means that the saving probably doesn't happen right away. The execution order is as follows:
You Initialize the ParseObject
You call saveInBackground on the object, saving probably hasn't started yet
You add the object to your adapter and call notify
getView gets triggered and your app crashes because the Date is still null
Maybe around here (in some cases earlier) the save will be complete and Date won't be null anymore if your app doesn't crash.
The way you can fix it is by checking if the variable is null:
if (created != null) {
long timepast = currentDate.getTime() - created.getTime();
.
. // your date forming code here
.
holder.timeComment.setText(sb.toString());
} else {
holder.timeComment.setText("new");
}
Hope this helps explain why you were getting a null object.
I found something that can be a solution for me.
I believe the problem is that created time is null.
so I solved this problem by creating an if/else method as follow:
//get Time
Date created = commentObject.getCreatedAt();
Date currentDate = new Date();
if( created == null ){
holder.timeComment.setText("just now");
}else{
long timepast = currentDate.getTime() - created.getTime();
long days = TimeUnit.MILLISECONDS.toDays(timepast);
timepast -= TimeUnit.DAYS.toMillis(days);
long hours = TimeUnit.MILLISECONDS.toHours(timepast);
timepast -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(timepast);
timepast -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(timepast);
StringBuilder sb = new StringBuilder(64);
if( days > 0){
sb.append(days);
sb.append("d");
}else if (hours>0){
sb.append(hours);
sb.append("h");
} else if (minutes >0){
sb.append(minutes);
sb.append("m");
}else if ( seconds >0){
sb.append(seconds);
sb.append("s");
}
holder.timeComment.setText(sb.toString());
}
This way, if it cannot get the created time, it will just say "just now". this works well for me.
Related
let say i will like to automatically change my textview text at 02:00pm everyday how do I implement this functionality.
val df = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.JAPAN).parse("2:00pm")
val systemDat = Calendar.getInstance(Locale.JAPAN).after(df)
if (systemDat) {
binding.includeTokyoSession.text_one.text = "successful"
} else {
binding.includeTokyoSession.text_one.text = "failure"
}
I suppose you want to change the text of your TextView after a particular time, but it seems that you're not aware of the date when comparing and you have a couple of mistakes in your code.
First, this line of code:
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.JAPAN).parse("2:00pm")
will return a Date instance with this date and time in your local timezone 01-01-1970 02:00:00. However, you need to get a Date instance with today's date and the time 14:00:00.
Second, this line of code:
Calendar.getInstance(Locale.JAPAN).after(df)
this is a wrong usage of the Calendar::after() function, and that's because you can only pass a Calendar object to the function in order to get the right comparison result, otherwise it will always return false.
In your case you're passing a Date object.
Following is the implementation of the Calendar::after() function.
public boolean after(Object when) {
return when instanceof Calendar
&& compareTo((Calendar)when) > 0;
}
If you want to proper compare the current time today with 14:00 (comparing only the time today), here is a modification to your code:
val calendarToCompare = Calendar.getInstance(Locale.JAPAN).apply {
set(Calendar.HOUR_OF_DAY, 14)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val systemDat = Calendar.getInstance().after(calendarToCompare)
if (systemDat) {
textview.text = "successful"
} else {
textview.text = "failure"
}
If you want to perform a lifecycle-aware view update (ex. to set the text of your textview), you can check this gist.
My app contains listview that are time limited. In another activity, I have a PostActivity that let's user post something to mysql server using volley. User post are time-limited. After posting their content, they can post again after a certain hour or minutes. As I mentioned before that my listview is time-limited, they contain timeCreated & timeExpiry. Whenever the user logins and tries to post something, I get the value of timeExpiry and check if
timeExpiry - currentTime (timeExpiry, currentTime & timeCreated) are all in milliseconds
Long currentTime = System.currentTimeMillis()
Long remainingTime = userDate.getTimeExpiry - currentTime;
if (remainingTime > 0 || remainingTime == 0) {
//User is allowed to post
}
!Wait User can change their mobile time and post unlimitedly!!
Now for the sake of simplicity, is there anyway to get the time from my mysql server or from a NTP-Server. I tried alot of classes such as the SntpClient class**(link below)** `
but I get the date before 1970-1-1
https://gist.github.com/prasann/9003350
I used this function but the method DateUtils.getFormattedDateTime() was not implemened in any DateUtils class.
public String getUTCTime(){
long nowAsPerDeviceTimeZone = 0;
SntpClient sntpClient = new SntpClient();
if (sntpClient.requestTime("0.de.pool.ntp.org", 30000)) {
nowAsPerDeviceTimeZone = sntpClient.getNtpTime();
Calendar cal = Calendar.getInstance();
TimeZone timeZoneInDevice = cal.getTimeZone();
int differentialOfTimeZones = timeZoneInDevice.getOffset(System.currentTimeMillis());
nowAsPerDeviceTimeZone -= differentialOfTimeZones;
}
return DateUtils.getFormattedDateTime(new Date(nowAsPerDeviceTimeZone));
}
"0.de.pool.ntp.org" NTP-Server time for Germany
i runing this code on android after load a cursor with the query i pass to the adapter, but my date is in long in milliseconds format so i need to format properly before load the adapter!
problem is this code is taking 14 seconds to pass a 50 items load, the problem get worst if i call it inside the adapter getView cause get slow when i scrool, if i take this function out the program runs smoothly
this is call inside my listfragment
private String dateFormatPatternEdited(long timeMS) {
android.text.format.DateFormat df = new android.text.format.DateFormat();
final Calendar eDate = Calendar.getInstance();
Calendar sDate = Calendar.getInstance();
sDate.setTimeInMillis(timeMS);
long daysBetween = 0;
while (sDate.before(eDate)) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
String mDateFormatPattern = FuelTrackerApplication.dateFormat.format(timeMS);
if (daysBetween < 2){
mDateFormatPattern = FuelTrackerApplication.timeFormat.format(timeMS);
} else if(daysBetween < 365){
mDateFormatPattern = df.format(FuelTrackerApplication.dateFormatPattern,timeMS).toString();
}
return mDateFormatPattern;
}
and this is were i initialize the date formats i gonna use its called inside onCreate in FuelTrackerApplication i dont think theres nothing wrong with this
public void initializeDateFormat() {
android.text.format.DateFormat df = new android.text.format.DateFormat();
dateFormatPattern = "MMM dd";
if (android.os.Build.VERSION.SDK_INT >= 18){
dateFormatPattern = df.getBestDateTimePattern(Locale.getDefault(), dateFormatPattern);
}
dateFormat = df.getMediumDateFormat(getApplicationContext());
timeFormat = df.getTimeFormat(getApplicationContext());
dateFormat2 = df.getLongDateFormat(getApplicationContext());
}
Ok just a few things. Depending on how long ago your dates are going back. You are only interested if the days between go more then 365. So if your dates are going back for years, you're doing extra work.
while (sDate.before(eDate) && daysBetween <= 365) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
Will let it break, it means if you have 20 entries going back 5 years, you don't do so much work.
It might be worth while to possibly just check the milliseconds difference. I'm not sure if this is precise enough, but it should work. It means you don't need to loop everything E.g
long millisecondsToday = getMilliseconds;
long timeMs = // you already have this
long millisecondsDifference = millisecondsToday - timeMs;
if (millisecondsDifference < MILLISECONDS_TWO_DAYS) // set a final variable out of this method
// etc
If might also be worth while initialising some of your variables once outside of the method. Like your df, that is being created 50 times, and then just having something set on it. Same with your eDate.
i got this incredible faster and practicaly remove the hole function
instead goes like this
lDateBetween = NOW - timeMS;
if (lDateBetween < DAY)
return FuelTrackerApplication.timeFormat.format(timeMS);
else if (lDateBetween < YEAR)
return df.format(FuelTrackerApplication.dateFormatPattern,timeMS).toString();
else return FuelTrackerApplication.dateFormat.format(timeMS);
all calculation is made using milliseconds i also put 2 final NOW and YEAR, also df and lDateBetween
i think is the fastest i can get!
im working on auto profile changer application for android...this is my 2nd application im developing on android, so dont know all the bells and whistles of android development...
any way...i am having some trouble calculating the next time when the timer code is suppose to execute...mainly the issue is around the time that spans over midnight into the next day...
for example, say that the user created a sleeping profile that starts at 10:30PM and goes until 8AM the next day...using this as a example, the execution is getting inside the proper condition that i have placed in the code, but the nextUpdateInterval is not being set correctly...im still trying to figure out how to account/calculate time that spans over to the next day, and at this point im starting to think that im might be making this task overly complicated? any suggestions?
here is the snippet of the code:
timer.scheduleAtFixedRate( new TimerTask() {
public void run() {
//..... some code to convert user stored times into proper time formats
//PM to PM
if(isFromTimePM == true && isToTimePM == true){
}//PM to AM
else if(isFromTimePM == true && isToTimePM == false){
if(rightNowDate.getTime() >= fromDate.getTime() && rightNowDate.getTime() >= toDate.getTime()){
foundProfileIndex = i;
i = profileArrayListSize;
nextUpdateInterval = rightNowDate.getTime() - toDate.getTime();
}
}//AM to AM
else if(isFromTimePM == false && isToTimePM == false){
}//AM to PM
else if(isFromTimePM == false && isToTimePM == true){
}}, 0, nextUpdateInterval);
Thanks,
P
Forget about PM and AM. Since you appear to be using dates, when you use the getTime() method you are getting the number of milliseconds elapsed since the Epoch. Simply subtract these two and you will have the time interval in milliseconds:
long nextUpdateInterval = secondDate.getTime() - firstDate.getTime();
The nextUpdateInterval is a long and is 'time in milliseconds between successive task executions'.
none of this secondDate - firstDate was ever necessary, what i ended up doing was the following:
converted all times to miliseconds
converted the milliseconds into the date objects
used the compareTo to compare the 'to' 'from' dates to current date/time
if i found a profile match, then i stored the 'to' date as the next run date
created a reScheduleTimer method that took date as a paramter...and i just used the .schdule(timertask, date) to resechule the timer...
I have a button and when user clicks this button, it should update status of the data with the specified id
public void updateByClick(int id) {
myCursor.moveToPosition(id);
if (myCursor.getShort(myCursor.getColumnIndex(MyTable.STATUS)) == 0) {
ContentValues cv = new ContentValues();
cv.put(MyTable.STATUS, true);
getContext().getContentResolver().update(MyTable.CONTENT_URI, cv,
MyTable._ID + "= ?", new String[] {String.valueOf(id + 1)});
}
}
The problem is, that if user clicks button fast (so there are a lot of updateByClick events) then update operation calls many times (update isnt happened yet, so status still == 0)
How can I check if ContentResolver (or something else) is busy (currently updating) then I can prevent multiple update calls
I would solve this with timeStamps. Every time your update method is called you create a timeStamp with System.currentTimemillis() and check the difference with the last saved timeStamp. If it doesn't reach a threshold you don't call the update request.
A simple solution would be to simply ignore button presses that come in to quickly:
long prevTouchTime = 0;
long touchTimeout = 1000;
public void updateByClick(int id) {
long touchTime = System.currentTimeMillis();
if (touchTime - prevTouchTime < touchTimeout)
return;
prevTouchTime = touchTime;
//Do everything else
}