I am migrating my app target SDK Version to android oreo.
One of the changes which might affect the app is that if the Display size of the device is changed then the app targeting Android Nougat and above will be notified as if there is a change in orientation of the device. Below is the source.
When the device density changes, the system notifies running apps in
the following ways:
If an app targets API level 23 or lower, the system automatically
kills all its background processes. This means that if a user switches
away from such an app to open the Settings screen and changes the
Display size setting, the system kills the app in the same manner that
it would in a low-memory situation. If the app has any foreground
processes, the system notifies those processes of the configuration
change as described in Handling Runtime Changes, just as if the
device's orientation had changed.
If an app targets Android 7.0, all
of its processes (foreground and background) are notified of the
configuration change as described in Handling Runtime Changes.
Now my app has a longrunning process which runs in an Async TSak in an Android Activity. Which means that the App has an AsyncTask which is inside the Activity Code.
I create a Dialog box on the start of the Async Task and hide it when the Async Task has done its work.
Now suppose the users start the task and then goes to the setting and changes the Display Size then return back to my app then the dialog box of the app is gone by the Async Task is still performed till the end which mean that the user might think that the app has finished the task but whereas the app would be actually performing the task. But my Activity is also responding as if restarted except that Async Task is running.
My app's orientation locked to portrait.
How should I handle such a scenario? Any help is appreciated. Thanks!
EDIT: This is the scenario
1) App Start
2) Main Screen
3) Users Press A Button
4) AsyncTask inside Main Screen Started
5) A Dialog Box is shown with the progress of task
6) User Minimizes the App and Goes to setting
7) Changes the Display Size/Density or changes the Font Size of the device
8) My App is called by the OS in such a way as if the device rotation is changed
9) Then the user returns back to the app
10) But the dialog box shown is no more being shown
11) But the async Task is running in the background and is still performing its task
12) The task is actually done but the user thinks that the task is still not done.
In your on_resume() method verify the screen size and the font size have not changed. If they have, adjust, remove and recreate, your dialog box appropriately. This is similar to handling orientation changes while suspended (adding font size as a condition).
EDIT:
Add android:configChanges="fontScale|density" to your manifest file.
static int fontScale = 1;
static int densityDpi = 0;
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig.fontScale != fontScale) {
// destroy and recreate dialog adjusting for font size.
fontScale = newConfig.fontScale;
}
if (newConfig.densityDpi != densityDpi) {
// destroy and recreate dialog adjusting for new screen size.
densityDpi = newConfig.densityDpi;
}
}
It would probably be better to utilize notifications for the async task, since they are unaffected by config changes and are not terminated in low memory conditions.
When you rotate the phone, your activity is actually destroyed and recreated. This includes your dialog. There are two ways to fix it:
1) Turn off this "helpful" functionality. If you don't have different layouts for landscape and portrait its what I'd suggest. Add android:configChange="orientation|resize" to your activity in the manifest.
2) Implement onSaveInstanceState/onRestoreInstanceState and have a variable that says whether or not you need to recreate and relaunch the dialog.
Suggestions:
Try using WorkManager to schedule the task and also ViewModel from the android architecture components which helps to retain the state even after the configuration changes
Related
I need a way to restore previous Android task when the current task (my application) is put in background using either back or overview button. For example, a video is played by youtube app when a SIP call is received. The softphone task is brought to foreground, then the call is answered. Once the call finishes, by pressing back or overview button youtube app is shown and the video continues playing. An example of such Android application is Linphone. I would like to know how this can be achieved programmatically.
As stated by others and here, Android handles it automatically for you. But if you need to add anything explicitly when going/coming to/from the background state then you can also override onSaveInstanceState() and onRestoreInstanceState() methods which will be called accordingly.
As your activity begins to stop, the system calls the onSaveInstanceState() method so your activity can save state
information to an instance state bundle. The default implementation of
this method saves transient information about the state of the
activity's view hierarchy, such as the text in an EditText widget or
the scroll position of a ListView widget.
To save additional instance state information for your activity, you must override onSaveInstanceState() and add key-value pairs to the
Bundle object that is saved in the event that your activity is
destroyed unexpectedly. If you override onSaveInstanceState(), you
must call the superclass implementation if you want the default
implementation to save the state of the view hierarchy.
#Override
protected void onRestoreInstanceState(Bundle outState) {
if (outState != null) {
Crashlytics.log(1, "FormActivity", "Method:onRestoreInstanceState, Msg: saved instance is not null");
if (outState.containsKey("record")
&& Session.getCurrentRecord() == null) {
Session.setCurrentRecord(
gson.fromJson(
outState.getString("record"),
Record.class
)
);
}
if (outState.containsKey("user")
&& Session.getCurrentUser() == null) {
Session.setCurrentUser(
gson.fromJson(
outState.getString("user"),
User.class
)
);
}
}
super.onRestoreInstanceState(outState);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
Session.setCurrentRecord(record);
outState.putString("record", gson.toJson(Session.getCurrentRecord()));
outState.putString("user", gson.toJson(Session.getCurrentUser()));
super.onSaveInstanceState(outState);
}
Source 1 Source 2
This is done automatically by the android system.
Now why you may not be able to notice this behavior for your app.
You may be launching your app from the app launcher. Which means that you already put all other apps (except the launcher) in background. Now depending on the launcher settings you may go on page from where you launched the app or home when you press back button.
When can you observe this behavior
If your activity is launched from background service, e.g. broadcast receiver
If your activity is launched by clicking on a notification button
basically when your activity is created without killing or putting other apps in background, you will get back to the same app when your app is closed.
Exception - If you use home button all apps go to background and home screen appears.
hope this helps.
here is the official documentation on how to preserve the UI state:
https://developer.android.com/topic/libraries/architecture/saving-states
if the user configured the phone to always kill activities in background or they have limited resources then you have to handle that, but in some cases (your activity wasn't killed and remained in memory) as Mayank answered the system will do it for you.
getting a call from phone app will interrupt your app (System-initiated UI state dismissal)
what you should do as suggested by the documentation above:
In the section: Managing UI state: divide and conquer
Local persistence: Stores all data you don’t want to lose if you open and close the activity. Example: A collection of song
objects, which could include audio files and metadata.
ViewModel: Stores in memory all the data needed to display the associated UI Controller. Example: The song objects of the most
recent search and the most recent search query.
onSaveInstanceState(): Stores a small amount of data needed to easily reload activity state if the system stops and then recreates
the UI Controller. Instead of storing complex objects here, persist
the complex objects in local storage and store a unique ID for
these objects in onSaveInstanceState(). Example: Storing the most
recent search query.
so In your case have a view model that stores the Url and the video time when the call got received
and I would also store the same info in the instanceState using the proper life cycle hooks
here is a good SO thread with example on how to use the savedInstanceState :
Saving Android Activity state using Save Instance State
it has old and new answers, you may want to read through it to get a sense of how things changed overtime
basically the three bullet points above are the recommended strategy by official documentation
I am trying to make an App which is going to display Some Images and Videos. So I am planning to add a splash screen of around 2seconds. After 2 seconds the user will be taken to the Main Screen of the App.
I want to start the loading of the Images, Video when then user is at the splash screen itself so that the user should wait for the least time when he is at the Main Screen.
So the loading will be started at the Splash Screen and then after two second the user will be taken to main screen irrespective of the completion of the loading.
Now since this involves two activities should I use a Async task or should I Use a service with an Async Task(For the callback of completion of code) within it?
Which one would be better. Also in Android 8.0 are there any restriction in using Services?
I think using a Async Task between two screens may cause Memory leak if not coded properly.
Any help would be really grateful.
EDIT: My app is having one more feature hence cannot make the user wait in the Splash Screen till the loading is over.
It is not very good to use AsyncTask for sharing results between 2 activities, because AsyncTack created in Splash activity will be destoyed (stopped) when switched to Main activity. Better to use service in this case and Main screen will subscribe for result.
So basically you want to start the download in the splash screen and continue the download in the activity that follows. In this way, you still have to implement a loading animation. In your case, I would recommend finishing your splash screen, as soon as everything is downloaded. In that way, you don't have to download anything anymore inside the app's lifecycle.
AsyncTasks continue to run even after switching to a new activity. You can try the following flow:
1. Splash screen
2. Trigger Async Task
3. Main Activity
4. Show Images/Videos
The only catch is, you will not be able to fix a time for #2 to complete to be able to start #4. This is the nature of AsyncTasks. You can workaround by using the OnPostExecute within AsyncTask.
Example: OnPostExecute call another method that will enable a button. Users can click on the button to view Images/Videos. But then, this might not be a good user experience to see some button suddenly getting enabled.
In that case I would rather create some Singleton with own Handler (that works in separate Thread), that will be started at Splash screen. After Main screen will start, it should ask that Singleton about respective data or should sign himself for receiving that data.
I want to execute a piece of code (say, for example, display a Toast) every time that the app is opened. So far I have managed to do this every time the app is launched by putting the code into my MyApp.java file that extends Application.
However, if I press the homescreen or back out of the app and then go into it, the message doesn't reappear. It only does when I relaunch the app. Any idea how to do this?
EDIT:
basically im asking how to execute code everytime the whole APP is brought to foreground (this can be first time open, after another app was used, after user backed out of app, etc). Where would I place onResume code? It wouldn't be in a particular activity, would it, since I want it to apply when entire app appears in foreground, not just particular activity.
You can try writing that code in your activity's #Override-d onResume() method.
The only way to do this is,
Determine which app is currently in the foreground
.Follow this discussion for getting an idea for the best way to do it.
[Determining the current foreground application from a background task or service
Suppose, if the function name is 'getCurrentForgroundApp()',
You need a service to execute getCurrentForgroundApp(); every one second
(1-second interval is depending on your purpose, can be lower or higher).
Now, you can identify which app is running foreground in every second.
So, check if your app is the one running foreground. If true, then execute the toast or code you need.
This is how app-locker apps showing lock screen over selected apps, whenever they come to the foreground.
You have to use the onResume callback:
Android API
Example of use from previous SO question
In activity class:
#Override
protected void onResume() {
super.onResume();
//your code here
}
Anyone have an idea how the Amazon mp3 app is displaying the player controls inside of the keyguard? I've been playing with some of the system alert and system overlay window parameters with now luck. The system alert will display over everything except the lock screen and the system overlay will display over the lock screen but in 4.0.3 and above you can not receive the touch events.
I have also seen options like WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG but it only seems to work for an activity; anything else I use it with gives me an exception that tells me that I can not use that flag with this type of window.
What I have noticed is that the player controls either replace or cover the keyguard clock and the player controls change size depending on the type of lock (like pattern lock or swipe lock). I've also noticed that the keyguard does not display my wallpaper anymore...
Any thoughts are welcome!
I need my J2ME app to run in the background and still allow the user to use his mobile without problem.
the app still needs to process some events in the background.
I would also like to allow the user to stop the app if he wants to.
How can I accomplish this?
Running a midlet in the background but still processing is not specified in the j2me standard i think. Normaly at the moment your midlet is moved to background the paused method should be called.
But not every vendor implements it that way. Symbian keeps your program running as if there was no change when minimized. At least on the N80 and N90.
A device's ability to run an application in the background depends on its ability to multitask. Therefore, more expensive, PDA-type devices are more likely to support background execution than lower-cost devices. For in background :-
private Display display = Display.getDisplay(this);
private Displayable previousDisplayable;
public void toBack() {
previousDisplayable = display.getCurrent();
display.setCurrent(null);
}
And foreground :-
public void toFront() {
display.setCurrent(previousDisplayable);
}
But Be aware that every device not supports that features.(Works on Nokia s60, SonyEricsson, but not on Nokia s40, Samsung and some others).
This is not always supported, but on the handsets that do, the command is:
Display.getDisplay(theMidlet).setCurrent(null);
If you app is in background then it doesn't receive any events. So I don't know how it can process any event in the background. If its a preload J2ME app then you can work with the handset manufacturer and obtain certain jad attributes to put your midlet in background. You can thus not allow the user to exit the app. You may want to think this use case through.
On the other hand you can do all these in Blackberry apps.
MIDlet.notifyPaused()