The Android TileService documentation states the following:
When a tile should be up to date and listing will be indicated by onStartListening() and onStopListening().
Based on the lifecycle for a TileService, it's intended that you START updating your tiles state when onStartListening() is called.
The issue is that it doesn't seem that this callback is called immediately when the tile enters the users view. Meaning it can show outdated information for several seconds some times before the view is properly updated.
An example of this would be that a service is Connected, the tile shows it as connected. I then switch to the application, turn off the service, then open the Quick Settings view back up. It still shows the service as connected for a long time.
In theory, the moment that the tile came into view it should have updated with the new data and displayed it as not connected.
I've confirmed this is the case by simply adding a log message as the first entry in the view. When I bring the Tile into the users view, that log message isn't written to log cat for upwards of 4-5 seconds.
So, what's going on? Am I missing something?
My Code
#Override
public void onStartListening() {
android.util.Log.d("Test", "onStartListening called");
updateTileDisplay();
registerDelegate();
}
The updateTileDisplay(); function is updating the title, state and icon for the Tile. Once it completes it is calling tile.updateTile(); as it should be. Additionally, the registerDelegate(); function is registering a delegate that will listen for internal state changes and update the tile accordingly. This delegate is later removed when onStopListening() is called.
I do not see that onStartListening called log message until upwards of 5 seconds after I have brought my tile into view. This seems not to line up with the TileService documentation.
Related
I am maintaining a simple Android kotlin app that only has one MainActivity.
The app has a lot of images. We want to reload the data and images when user navigate away and then return to our app, this works so far:
override fun onResume() {
super.onResume()
tryToLoadDataAgainAndReloadAlotOfImageViews()
}
However this produces one unpleasant side effect, for example:
User launch the app
onResume fired, we load data and refresh all images (corrrect)
App shows permission dialog to grant GPS locations
User click accept
permission dialog dismisses
onResume fired again, all data reloaded and images reloaded again.
This will looks like a glitch as all images are reloaded twice in a short period of time.
I understand that I may set booleans here and there before showing permission dialogs to resolve this, but it feels like a hack and possibly buggy code.
Is there any elegant way to resolve this? For example, detecting onResume is coming from another app, or coming from internal permissions dialogs?
We want to reload the data and images when user navigate away and then return to our app
What do you mean by this exactly? Are you talking about the app disappearing from the screen? (e.g. the user switches to another app, or hits the home button, or turns off the screen.) If so, do your refreshing in onStart instead of onResume, then it'll only fire when the app becomes visible again.
But if you want to handle things like multi-window mode, where your app is visible but might not have focus, then you'll need to look into detecting that when your app hits onPause, and maybe storing a flag onResume can check.
Basically Android's Activity and Fragment lifecycles have a couple of paired callbacks, onPause/onResume and onStop/onStart.
When your app is no longer in the foreground, but still visible, it moves to the PAUSED state. If it remains visible, but then moves to the foreground, it moves from PAUSED to RESUMED by calling onResume. This is what's happening with your dialog - because it's displayed over your Activity, the Activity is paused until the dialog is dismissed, at which point it calls onResume. So what's happening is by design!
But if your app is becoming invisible (i.e. it's not on the screen anymore) then it first moves to the PAUSED state (if it's not already), and then it moves to STOPPED and you get the onStop callback. When it returns to the foreground, onStart will be called, followed by onResume. (This is also what happens when the Activity/Fragment is (re)created of course!)
So you need to handle things in the appropriate lifecycle callback. It sounds like your needs are better met by having this stuff handled in onStart, since that only gets called when the Activity/Fragment appears. onResume can be called more often, since it's possible for the component to enter the PAUSED state (onResume is called when it's "unpaused") without moving to the STOPPED state - like with dialogs.
Like I said, if you want to handle multi-window mode that's more complicated, and the lifecycle behaviour depends on API level, so you'll have to look into that!
You can use the isFinishing method to check if the activity is in the process of finishing before calling tryToLoadDataAgainAndReloadAlotOfImageViews() in onResume()
try using Glide.
dependencies :
//Glide
implementation("com.github.bumptech.glide:glide:4.12.0")
kapt("com.github.bumptech.glide:compiler:4.12.0")
As per the official documentation
Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.
I have an android app that uses push. If the app receives a push message from the server it creates a push notification (with headline, message, icon) and presents it to the user.
normal case
The click on the notification opens up an Activity (not the launcher) and the activity opens a fragment that shows content based on the notification. No magic here and everything works as expected.
the special/bad case
If the device is inactive for at least 2h and you pick it up and click a notification that was pushed before, then it opens the Activity like in the normal case but no fragment comes up. Instead the launcher will be called.
While debugging this is time costly and frustrating I found out the following things. Maybe someone has an idea:
The Activity usually starts with an animation that I load from resources. I load and run it in onCreate(). If the Activity gets invoked by the push then the animation will be loaded but never runs. I bypassed this with a Handler that waits 2 seconds and checks if the animation listener was called. If not (bad case) then the handler calls the code to open the fragment.
opening the fragment caused a crash:
IllegalStateException: Cannot perform this after onSaveInstanceState. Similar like here Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState So I replaced commitAllowingStateLoss(). Now there is no crash anymore but the fragment does not appear. Only Fragment.newInstance() will be called but no onCreate() or any other lifecycle method.
Question:
What crazy state is it that seems to break functions in my code after longer inactivity?
Device:
I'm currently testing with a Nexus 5 with Android 6.0.1
I found out that if my device is in this crazy sleep state that my activity gets startet twice if I then click on a push notification. At first the push triggers the activity with its data in the intent. Then, after it, the launcher activity gets invoked (with an empty intent) and starts the main screen.
I fixed it with like here -> Activity opened twice
To sum it up:
1) I changed my code so that all my deeplinks, push notifications, etc. will invoke the lauchner activity. The activity then decides what to do. That means we have a single entry point into the app. That makes everything easy and the code clearer.
(with this fix the same activity will be invoked twice. we have to do a little more to fix it)
2) I marked the activity in the manifest with android:launchMode="singleTask". This means that if there is an instance of the activity already, it will be reused and not created a second time.
3) The flag from 2) triggers a callback method. Instead of invoking the activity a second time, the system reuses the already running activity and calls the method onNewIntent(intent). That means if the activity is freshly started, then onCreate() will be called. If the activity is reused (because it was triggered by a push notification, deeplink, etc.) then onNewIntent(intent) will be called. In both, onCreate() and onNewIntent() I call a method to decide what screen/fragment/activity is next and handover the intent from getIntent(). Done.
I need to calculate the time taken by my app to start. Do you have any ideas when to start and when to stop timing?
Use your timer at the beginning of oncreate() and stop it at the end of onResume().
According to lifecycle of an Activity.
To do this, you probably should be clear on what "start my app" means - if you are referring to an activity, then you should probably override the Activity constructor (not "onCreate" except in most cases there isn't any measurable time from the constructor before onCreate is called) and capture:
SystemClock.upTimeMillis()
then you need to create a listener for onGlobalLayout to determine when the activity is actually finished displaying on the screen and get the upTime again. For the listener, do it like in this post:
Activity lifecycle - receiving notification that layout is complete
And then take the difference between the times... however, that is really the "activity" startup time.
If you are certain your app is not running, and you want to see how long it takes to "start your app" when it is not loaded in memory at all, you should extend the Application class:
public class MyApp extends Application {
public MyApp() {
// get the time
}
}
and then override the constructor to capture the timestamp like above and store it. Your app's application class is constructed first, then activities are instantiated.
Then capture the listener time in the first activity to display, as mentioned above. That will probably tell you best how much time it took from when your app "starts" to when the user actually could see it.
There are a few things that happen between when a user action occurs that is intended to "start your app" that are not included, but you have no control over that, even though that should be included if you are trying to measure how long it takes from the user's perspective.
I have an android app widget that gets some information from the internet when it is first launched.
More precisely it launches a service that asynchronously does a network call. At the end of that network call, in the UI thread it updates the remoteview for the widget with new information.
Touching particular parts of the widget loads an activity which does check to see if the network call stored anything, but that is a conditional statement based on the size of the network call response, if that object doesn't contain the right things then it won't load that.
My problem is that touching the widget doesn't seem to load the activity UNTIL the asynchronous network call finishes. And this doesn't make since to me because that is a separate thread.
There never seems to be a circumstance where my activity gets to even check the condition for an empty object. Instead, I touch the widget, and that seems to be put into a queue, so when the network call finishes it then loads my activity and displays information about that network call.
Why is this happening? Is it something about how I update my remoteView? My views have listeners on them from when they are first placed on the launcher screen in the onUpdate method of the widget. Thanks
I wrote a simple alarm-clock style application that I run on my (jailbroken) Nook Simple Touch (aka NST), under Android 2.1.
When the scheduled alarm time arrives, my application needs to wake up the NST and display a page of HTML content. I use AlarmManager to get a callback at the right time, and it seems to work as expected -- almost.
The problem occurs when enough idle time has passed that the NST has activated its lock-screen mode (i.e. it is auto-displaying a caricature of a famous author). I can't find a programmatic way to dismiss the lock-screen so that my HTML content will be visible. I can see that my alarm callback routine ran at the expected time (via the LogCat view in Eclipse, after I reconnect to the NST with adb), and after I manually "drag to unlock" with my finger, I can see that my app's window updated as expected, but I need to have the text become visible when the alarm event occurs, not just after the user unlocks the device. I tried the code shown below (based on other StackOverflow answers) but it doesn't help.
Any ideas regarding a way to do this? (One solution that technically works is to keep FLAG_KEEP_SCREEN_ON set on my window at all times, so that the famous-author-lock-screen never appears in the first place, but that keeps the NST awake and therefore it uses up the battery rather quickly, so I want to avoid that if possible)
private void wakeUpTheScreen()
{
Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
Ha, I figured out (with some more help from previous StackOverflow answers) what I was doing wrong.
The problem is as described in the above link -- the AlarmManager was calling my BroadcastReceiver as expected, and then my BroadcastReceiver would sendMessage() a Message to my AlarmHandler (as shown in the Alarm example I was cribbing from). But the Nook would go back to sleep immediately after onReceive() returned, which meant that the secondary handler never got called, and therefore my wakeUpTheScreen() method wasn't getting executed.
I moved the wakeUpTheScreen() call so that it now gets called directly from the onReceived() method, and now the wakeup works as expected. :^)