Activity recreating after firestore update method - java

I've been trying to make an app, where after a time of 60 seconds, all the data collected by user input gets updated in firestore's existing data. However, as soon as I call the .update method of firebase, the activity gets recreated and timer starts again and it doesn't move on to the next activity. Its stuck in a loop and after every 60 seconds the activity is just recreating itself again and again. I know its because of the update method because if I comment that particular update method line, the application runs fine and normally as it should i.e after 60 seconds the app starts next activity. Also, I'm using firestore listener to get data only once and not real time update listener. I still don't get why is it happening. Please help
Edit:
new CountDownTimer(5000, 1000) {
#Override
public void onTick(long l) {
roundTimerView.setText(String.valueOf(counter));
counter--;
}
#Override
public void onFinish() {
newPlayerData.cards = cards;
GamePage.this.finish();
Intent intent = new Intent(GamePage.this,NextRoundReady.class);
Bundle bundle = new Bundle();
bundle.putString("USERNAME",Username);
bundle.putString("ROOMID",RoomID);
intent.putExtras(bundle);
startActivity(intent);
cr.document(RoomID).update(Username,newPlayerData);
}
}.start();
cr.document.update is the one giving me trouble, if I comment it out, after timer runs out, next activity starts, but if I uncomment it, after timer ends, it will just recreate the activity and run in a loop

Honestly GamePage.this.finish() seems suspicious, what happens if you remove that?
What happens when you remove startActivity?
The update method shouldn't be the real problem, unless it causes an exception which might restart the app
Also try doing the update before starting the new activity

Related

How to finish an application when it goes to the background

I need to finish an application when it goes to the background, I'm using method finishAffinity() but it seems it does not work, someone can tell me another alternative
#Override
protected void onPause() {
finishAffinity()
super.onPause();
}
Here's answer
finishAffinity() is not used to "shutdown an application". It is used to remove a number of Activitys belonging to a specific application from the current task (which may contain Activitys belonging to multiple applications).
Even if you finish all of the Activitys in your application, the OS process hosting your app does not automatically go away (as it does when you call System.exit()). Android will eventually kill your process when it gets around to it. You have no control over this (and that is intentional).
you can use this
public void endTask() {
// Is the user running Lollipop or above?
if (Build.VERSION.SDK_INT >= 21) {
// If yes, run the fancy new function to end the app and
// remove it from the task list.
finishAndRemoveTask();
} else {
// If not, then just end the app without removing it from
// the task list.
finish();
}
}
Source and read more

RealmChangeListener does not execute when added shortly after asynchronous write

Long story short: I do not know why my RealmChangeListener does not trigger as intended under certain circumstances and am looking for advice on why a RealmChangeListener could not be working.
Update: I have verified that the RealmResults stay valid = true; loaded=false.
If I execute RealmResults.load() after adding the change listener, it will output a vague Throwing Exception 7 in the log and a BadVersionException when I step through the Realm source. I think this exception makes some sense, the asynchronous write updated the Realm and therefore the query seems to no longer work. However, both the executeTransactionAsync that writes in the MainActivity as well as the asynchronous queries are started from the main thread.
-
I have a MainActivity, in which upon pressing a button an asynchronous write will be performed.
I have another button that opens a second activity that displays data.
The second activity uses a ViewPager with a fragment for each tab. Each tab has a different query.
So what happens now is the following: I open the second activity, which instantiates four fragments, not attaching them to the activity.
The activity then executes queries, passing each RealmResults to the fragment, in which a RealmChangeListener will be installed to display the data once it is loaded. Could it be that the RealmChangeListener does not work when the fragment is not attached to an Activity?
Anyway, this is the method in the fragment that receives the RealmResults (created by findAllAsyncSorted()) and is supposed to update the data on the Adapter:
public void updateData(OrderedRealmCollection<Bean> realmCollection) {
Timber.v("Delegated data to fragment of adapter %s.", adapter);
this.data = (RealmResults<Bean>) realmCollection;
if (data.isLoaded()) {
Timber.d("Data is already loaded on adapter %s.", adapter);
adapter.updateDataManual(data);
}
if (!data.isValid()) {
Timber.e("Data is not valid.");
}
listener = new RealmChangeListener<RealmResults<Bean>>() {
#Override public void onChange(RealmResults<Bean> newResults) {
Timber.v("Change listener for manual data triggered: %d results in fragment for category %s and adapter %s.",
newResults.size(), category.toString(), adapter);
adapter.updateDataManual(newResults);
}
#Override protected void finalize() throws Throwable {
Timber.d("Finalizing change listener for adapter %s.", adapter);
super.finalize();
}
};
data.addChangeListener(listener);
MyTimer.setRepeatingCallback(() -> {
Timber.v("RealmResults in adapter %s are %s and %s, and the ChangeListener is %s.",
adapter,
data.isValid() ? "valid" : "invalid",
data.isLoaded() ? "loaded" : "not loaded",
listener);
return true;
}, 5000);
}
As you can see, I made efforts to ensure that the query is valid and has not loaded until the change listener is added and that neither the RealmResults nor the RealmChangeListener are garbage collected.
Still, out of four RealmChangeListeners, only two or less (sometimes zero) trigger.
Note that this only happens if the second activity is opened shortly after starting the asynchronous write on the MainActivity. If I wait for 2 seconds, everything works as intended. I did verify that the RealmChangeListener is not garbage collected, since the finalize() is called after exiting the app. I have no idea what could prevent the listener from working. Is there anything specific I should pay attention to?
You need to have a (strong) field reference to the RealmResults<T> so that you keep it from getting GC'd.
If a RealmResults gets GC'd, then Realm can no longer auto-update it.

CountDownTimer getting restarted after closing and reopening the app

I am trying to show a CountDownTimer to user which start decreasing immediately a user press the 'START' button, but if I'm closing the activity and reopening it, the CountDownTimer is getting reset and getting start again.
I used this link to learn how to set a CountDownTimer.
Here's how I coded it:
new CountDownTimer(ms, 1000) {
public void onTick(long millisUntilFinished) {
String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millisUntilFinished),
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) % TimeUnit.MINUTES.toSeconds(1));
holder.availableFor.setText(hms);
}
public void onFinish() {
holder.linearLayout.setVisibility(View.GONE);
holder.firebaseDatabase.child(itemID).setValue(null);
holder.geoFireReference.child(itemID).setValue(null);
}
}.start();
Please let me know how to keep the Timer going even if user closes the app.
As I understand you're creating this timer in onClick method.
Try to create some global variable
CountDownTimer timer
After that create it in onclick, as you do.
and in onpause:
if (timer != null) {
timer.cancel
}
You have to use service class here
is the link which may be help you.
You need to store the timer data in some global scope so the timer once set not get altered with activity life cycle.
best practice suggest, use service like answered by #shahid17june
also if you don't want to use that, or setup variable to store counttimerdata in application class (global data), and update it appropriately.

Is it possible to register a receiver in a test case?

I want to test inside a unit test whether or not an alarm programmed using the AlarmManager is fired, and if so, if it is fired within the correct period.
Here is the receiver class to be tested.
I've created it inside my test project.
(NOTE: it's not registered in the manifest)
public class MockBroadcastReceiver extends BroadcastReceiver {
private static int numTimesCalled = 0;
MockBroadcastReceiver(){
numTimesCalled = 0;
}
#Override
public void onReceive(Context context, Intent intent) {
numTimesCalled++;
}
public static int getNumTimesCalled() {
return numTimesCalled;
}
public static void setNumTimesCalled(int numTimesCalled) {
MockBroadcastReceiver.numTimesCalled = numTimesCalled;
}
}
And here's the unit test. The programReceiver method actually belongs to a class in the main project, but I've included it inside the test so that you don't need to read so much code.
public class ATest extends AndroidTestCase {
MockBroadcastReceiver mockReceiver;
#Override
protected void setUp() throws Exception {
mockReceiver = new MockBroadcastReceiver();
getContext().registerReceiver(mockReceiver, new IntentFilter());
}
#Override
protected void tearDown() {
getContext().unregisterReceiver(mockReceiver);
mockReceiver = null;
}
public void test(){
//We're going to program twice and check that only the last
//programmed alarm should remain active.
final Object flag = new Object();
MockBroadcastReceiver.setNumTimesCalled(0);
new Thread (){
#Override
public void run(){
programReceiver(getContext(), MockBroadcastReceiver.class, 60000, 60000);
SystemClock.sleep(20000);
programReceiver(getContext(), MockBroadcastReceiver.class, 60000, 60000);
SystemClock.sleep(90000);
synchronized(flag){
flag.notifyAll();
}
}
}.start();
synchronized(flag){
try {
flag.wait();
} catch (InterruptedException e) {
}
}
assertEquals(1, MockBroadcastReceiver.getNumTimesCalled()); //should have been called at least once, but its 0.
}
private static void programReceiver(Context context, Class<? extends BroadcastReceiver> receiverClass, long initialDelay, long period){
Intent intent = new Intent(context, receiverClass);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent); //Cancel any previous alarm
alarmManager.setInexactRepeating (
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + initialDelay,
period,
pendingIntent
);
}
}
When I execute the test method, the receiver should have been registered dynamically in the setUp. Then I program the same alarm twice. My intent was to test that only the last alarm remained active, but I'm having trouble getting the receiver called at all. The test fails as it is expected to be called once (or at least a number of times >= 1), but the counter in the mock receiver is 0. I've set a break point in the onReceive method and it is never hit. I've also added logging and nothing shows in the logcat. So I'm 100% sure the receiver is not being called. I've also tried increasing the sleep time in the thread, because setInexactRepeating fires very inexactly, but I can wait for ages and it is still not called.
I've also tried registering it in the test project's manifest instead of programmatically and the results are the same.
Why is the receiver not being called?
UPDATE
I can confirm the AlarmManager is not the issue. Alarms are correctly registered according to adb dumpsys alarm.
I'm now trying to get the receiver to run by calling sendBroadcast, but I'm in a dead end. The receiver just won't get called. I tried the main app context, the test case context, even ActivityInstrumentationTestCase2. Tried also adding WakeLocks and nothing. There's just no way of getting it called. I think this might be caused by some flags in the intent or intent filter (android seems to be really picky with flags).
In the android source-code there is a alarmManagerTest that executes alarmManager.setInexactRepeating with a broadcastreceiver.
The main difference is that the working android test has a delay of 15 minutes while your test uses 1 minute delay.
The Android documentation for AlarmManager says:
public void setInexactRepeating (int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
...
intervalMillis interval in milliseconds between subsequent repeats of the alarm. Prior to API 19, if this is one of INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY then the alarm will be phase-aligned with other alarms to reduce the number of wakeups. Otherwise, the alarm will be set as though the application had called setRepeating(int, long, long, PendingIntent). As of API 19, all repeating alarms will be inexact and subject to batching with other alarms regardless of their stated repeat interval.
I am not shure if this means that only multibles of 15 minutes are allowed.
On my Android 2.2 handset the inexact timer only works if it is a multible of 15 minutes.
I'm not sure why your test is not working as expected. A couple of suggestions spring to mind:
Most of the doco I've seen, eg. [1], suggests that Object.wait() should always be called within a loop based on a condition that does not hold. Your code does not do this. Perhaps you could try re-working it so that it does.
For completeness, perhaps you should output something for the InterruptedException that can be thrown by flag.wait() just in case.
[1] https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--
So the answer is yes, it is possible, BUT receivers will only work with implicit (action-based) Intents and IntentFilters.
Explicit intents (class based, with no filters) don't work with dynamically registered receivers. For explicit intents to work, you need to register the receiver in the manifest, and not dynamically as I was trying to do.
This is one of the darkest and least documented features of Android. In my experience with intents, you can never be sure. And it's also very difficult to diagnose, as there's no warning printed in the logcat that could help understand the problem. Kudos to #d2vid's answer here:
https://stackoverflow.com/a/19482865
So to fix my code I'd have to add the receiver element inside the application tag in the manifest. As the receiver is a mock receiver class created inside the test project, I'd have to edit the test project manifest, not the main project one. But on the other hand, in Eclipse test projects, receiver tags added to the manifest don't seem to work. Android Studio looks better suited for manifest merging, but this is a legacy project started in Eclipse and we are not porting it.
In conclusion: explicit intent testing is broken in Eclipse for those receivers that don't exist in the main project. The class I needed to test uses only explicit intents so my test can't be written in Eclipse.

Iconsistent results when returning to a previous activity

I have an android app that has various activities.
for example there is a home screen and an update screen.
The home screen is a list activity.
A button on the home screen brings you to the update screen that updates the database from a server. In theory when Returning to teh homescreen after an update, the list should be changed to reflect the update just done.
the code for going to the update screen is as follows:
Button synch = (Button) findViewById(R.id.synchButton);
synch.setOnClickListener(new OnClickListener() {
public void onClick(View viewParam) {
Intent intent = new Intent(HomeScreen.this, SynchScreen.class);
startActivity(intent);
}
});
and the code for returning back to the homescreen after the updates is:
main_menu.setOnClickListener(new OnClickListener() {
public void onClick(View viewParam) {
finish();
}
});
the list is compiled from an async task that runs in onStart, so my understanding is that onStart should run when I return to the homescreen, thus always displaying the most up to date version of the list.
On my Emulator I always get teh updated list, but when I run it on my phone the list is never updated, it just returns to the state of teh screen before I did the update.
any ideas?
thanks
Kevin
Check the Activity lifecycle section of the Android documentation. The code updating the view should probably be moved to onResume, since the Activity might not get killed when launching a new one.
Put the code for starting the Asynctask in onResume. Read the documentation related to activity life cycle.
#Override
public void onResume() {
super.onResume();
Apaptor.refreshListView(Vector);
}

Categories