Dealing with a service after the app was swiped and relaunched - java

I am working on an audio player app. The user may play the files, and a service is launched to allow them to play them in the background. I allow my service to continue playing the tracks even after the user chooses to swipe the app from the recent list.
Now, if the user swipes the app, the service will continue playing without issues but if the user opens the app using the notification associated with the service, I do not know how to handle this. To my surprise, the app actually continues working with the code above with no issues (as far as I can see).
Is there something I need to do to handle the said case? Do I need a way to reassign the service to the newly launched instance of my app?
Thanks.

Looks like you are already doing it. onStart() you check if the intent is null and if it is, you create a new intent and bind it to a service (if it exists) otherwise create a new service.
Since onStart() is called every time your activity (not application) comes back from background to the foreground (say you launched the setting page and then come back to the main activity), it seems excessive to bind service during onStart(). I would move binding inside onCreate() since onCreate() is only called once for an activity.
Checking for null intent seems weird and maybe redundant, if you move binding inside onCreate(), you can be sure that you are only binding when the activity is launched, and if service already exists, activity will just bind to it.

Related

Perform an Action when the app comes to foreground

I am developing an app which has to perform some background work. For that reason, I am using a Service. Now what I want to achieve suppose the user start the background work and while the work is loading then the user has an option to either minizine the app or wait till the loading is over.
After the loading is over I want to open another Activity. Now my issue supposes the user starts the loading and minimizes the app then when the loading is over the user has not yet returned to the app then if I start the Screen without even the user having my app in his view then the user might get interrupted with his work.
So what I want is when the loading is over, I want to only open if my app is visible to the user and if the app is not visible to the user then I want to wait till the user return back and only when the user returns back I want to open the Screen if the loading is over.
Now what I have thought is I should have a boolean which will track whether the app is visible to the user. On onStop I will set the boolean value to false and onStart I will set the value to true. And again onStart I will check if the loading is finished and if yes then I will open the Screen.
But I want to know whether there is a better way to achieve this? If yes then how. The reason I am looking for a better way is that I want to write a clean code for my app which might avoid bugs and crashes.
That is exactly the scenario LiveData and RxJava are for. Your activity will get the data only when the activity is visible. Your Viwemodel will provide your live data to the activity only when your activity is available and it's lifecycle aware. You can also consider using WorkManager if your app needs to continue to work even after your user closed your app, even if user restarts your app. It also comes with Constraints to optimize your work based on Network, Battery life...and provides livedata for your Viewmodel to consume.

What is the difference between `finishAffinity();` and `finish()` methods in Android?

I've using some code for a sign in screen that forces the app to close if the user doesn't want to sign in. If the user chooses to not sign in/cancel, it calls the code to exit the app. I've successfully done this two ways (not at the same time) with:
finishAffinity();
System.exit(0);
and
finish();
System.exit(0);
Yet both lines of code seem to do the same thing... The app is closed to the user, yet remains in the background apps to be reopen if the user hits the 'overview' button they can select it to reopen. (Which just restarts the prompt to sign in.)
Since I notice no functional difference, I'm wondering what is the difference between finishAffinity() and finish() methods?
Bonus Question: Also, is there a way to completely shut down the app, so it also doesn't appear in the overview button app list?
finishAffinity():
Closes all the activities present in the current Stack
finish()
Closes only opened activity
Also, is there a way to completely shut down the app, so it also
doesn't appear in the overview button app list?
Yes you can add android:noHistory="true" to your activities tag in the Manifest.xml for this pupose
finishAffinity() : finishAffinity() is not used to "shutdown an application". It is used to remove a number of Activities belonging to a specific application from the current task (which may contain Activities belonging to multiple applications).
Even if you finish all of the Activities 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).
finish() : When calling finish() in an activity, the method onDestroy() is executed this method can do things like:
Dismiss any dialogs the activity was managing.
Close any cursors the activity was managing.
Close any open search dialog.
finishAndRemoveTask() method pop out all your activities from stack and removes the application from recent task list simple finish this current activity as well as all activities immediately below it in the current task that have the same affinity,
finish() method pop out your current activity from stack.
for detail document link
finishAffinity(): finish the current activity and all activities immediately below it in the current task that have the same affinity.
finishAndRemoveTask(): call this when your activity is done and should be closed and the task should be completely removed as a part of finishing the root activity of the task.

onTaskRemoved not getting called if the activity started by the service is removed

I am using oreo 8.1.0.
I am facing a weird problem whose solution I can't find on stackoverflow, hence I am writing this question. I know one solution that through foreground service, I can implement it but I don't find notification user friendly in my application context.
I will describe my problem using two cases.
Case 1:
When user opens my app by clicking on the icon and removes it from recent apps, then service automatically restarts. This is fine.
Case 2:
Here my app is closed and is not in recent apps.
When user copies a text, then my service starts one of the activity of my app but when he removes it from the recent apps, then my service gets stopped permanently.
So my problem lies in second case,I don't want my service to get killed. Even if it gets killed I want it to restart.
I tried all the methods mentioned on the stackoverflow like using START_STICKY and onTaskRemoved but I am not able to make it work.
Even I tried killing my activity whenever user clicks on on recent app button and remove it from the recent apps programmatically but this also did not work.
Though this restarts the service even in second case when user kills my app using the back button.
This part of the code is from the activity which opens when user copies some text.
#Override
public void finish() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
super.finishAndRemoveTask();
} else {
super.finish();
}
}
This part of the code is from the service that starts the activity.
#Override
public void onTaskRemoved(Intent rootIntent){
Log.d("testing 12","onTaskRemoved");
Intent restartServiceTask = new Intent(getApplicationContext(),CBWatcherService.class);
restartServiceTask.setPackage(getPackageName());
PendingIntent restartPendingIntent =PendingIntent.getService(getApplicationContext(), 1,restartServiceTask, PendingIntent.FLAG_ONE_SHOT);
AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(this.ALARM_SERVICE);
myAlarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartPendingIntent);
super.onTaskRemoved(rootIntent);
}
#Override
public int onStartCommand(Intent intent,int flag,int startId){
super.onStartCommand(intent, flag, startId);
return START_STICKY;
}
EDIT 1
I just need the service to remain alive.Here are the things that will not hinder the user experience in my app case
1. Killing the activity by yourself programitically when onPause is called so that service does not get killed is acceptable if you make it happen
2. Showing the notification for few second is acceptable
3. Restarting the service is acceptable
From Android Oreo there is a paradigm shift to limit background service execution. It was a frequent criticism affecting battery life, performance and security. As posted in the comments above there are alternative approaches such as JobScheduler.
Refactoring services to the JobScheduler pattern can be quite involved. I advise you look into changing your IntentService to a JobintentService from the Android support v4 library. It uses JobScheduler on Oreo and later targets but reverts to the older IntentService design on older devices. You just need to replace your override of onHandleIntent with onHandleWork in your Service implementation.
Add
android:permission="android.permission.BIND_JOB_SERVICE"
to the service declaration in your AndroidManifest.xml. It can also be useful to add
<uses-permission android:name=”android.permission.WAKE_LOCK” />
But, saying all that, reading through your question, it seems to me your basic complaint is with the cosmetic issue of foreground services requiring a notification. In my view, your solution is either to simply use Context.startForegroundService, or make sure your service is bound. Either way there is visible content to the user that the service is running. You will find it an uphill struggle to try to subvert Android design decisions; better to embrace it - they are wanting you to show these notifications from now on.
Anyway updating your code for newer Android targets is still good practice even if you can't completely avoid those notification icons.
This was the problem
Whenever my service used to start the activity, my service used to get destroyed after the activity used to get started.
Why onTaskRemoved was not working?
The thing I was doing was not working because my service used to get destroyed and hence it was not active to listen to onTaskRemoved.
How I solved it?
The solution to this problem was putting a check in the activity(started by service) to check whether the service is alive or not. As in my case service was getting destroyed. Hence I need to start the service again if the service is not alive.
It even fits the OREO design pattern as we can start the background service when the app is in foreground and the service will stay alive even if the activity gets destroyed.
On Oreo though after sometime service gets destroyed but this is a different problem.
More Info

invoke a method from activity in another

i know that this question was asked before but i had tried all the solution and get Error
i have two activity on android studio ...
the first called 'MainActivity' and contain a method ' deleteFromArrayList() '
the secound on called 'DeletButtonActivity' and contain a method ' delete(View v) '
i want to invoke 'deleteFromArrayList ()' wihtout creating another class or make the method static .... becouse i have an ArrayList inside deleteFromArrayList()
note : i send value of index i want to delete from array list using Intent ..the code in DeleteButtonActivity is
public void delete(View v) {
try {
Intent i = new Intent(DeleteButton.this, MainActivity.class);
i.putExtra("index", (int) spinner2.getSelectedItemId());
(new MainActivity()).DeletButtonActivity();
Toast.makeText(getApplicationContext(), "it was deleted", Toast.LENGTH_SHORT).show();
}
catch(Exception e){
Toast.makeText(getApplicationContext(), e+"", Toast.LENGTH_SHORT).show();
}
}
and the code in MainActivity
public void deleteFromArrayList (){
this.arrayList.remove(getIntent().getIntExtra("index",-1));
}
when i run the app i got an Error NullPointerException,,,
can anyone help me ..please
hope that i describe the problem very well
Activities in Android are no just a simple class but they also have a Lifecycle:
An activity has essentially four states:
If an activity is in the foreground of the screen (at the top of the
stack), it is active or running. If an activity has lost focus but is
still visible (that is, a new non-full-sized or transparent activity
has focus on top of your activity), it is paused.
A paused activity is
completely alive (it maintains all state and member information and
remains attached to the window manager), but can be killed by the
system in extreme low memory situations.
If an activity is completely
obscured by another activity, it is stopped. It still retains all
state and member information, however, it is no longer visible to the
user so its window is hidden and it will often be killed by the system
when memory is needed elsewhere.
If an activity is paused or stopped,
the system can drop the activity from memory by either asking it to
finish, or simply killing its process. When it is displayed again to
the user, it must be completely restarted and restored to its previous
state.
So the problem with your code is that when you want to access the array in a stopped activity, the instance you have been using before might not be alive anymore.
How to solve your problem
A very simple approach is to use parameter passing before you do the transition from one activity to the other, for this you'd pass your Array as an intent extra and then you "get the result back" when you finish the second activity by using onActivityResult() callback.
A second approach could be to use a Service that is something similar to an Activity but it has no UI and it has its own lifecycle. Being able to be alive even when you app it is not. Using a Service, you'll keep the Array inside the service and you'll communicate with the array to do the usual operations.
A third approach could be to use an EventBus. A very simple communication mechanism between Activities, Fragments, Threads, Services. There's a great talk titled Android Application Architecture on Android Dev Summit 2015 that uses EventBus as a communication mechanism and to implement a MVC architecture pattern on a REST Android App.
Back to your question. If you just need to 'share' an array between two activities, use the first approach. The second and third are just examples of different alternatives for the case you need a lot more than that.
You must not do this. There are mechanism to communicate between activities or fragments.
On can be, using startActivityForResult, this is Activity A calls Activity B, then in B you do something, and communicate the result back to Activity A.
You can have another workaround to what you want. If you can access the data in both of your activities, you can modified in ether one of them, when the activity starts, it will show the updated data.
Please first read well about an Activity here, and also provides more context of your question.

How do you get an android app to launch multiple apps and then return to the initial app?

I am writing my first android app so I will try my best to be clear and precise. I can see some similar questions to this but none that seems to answer it exactly so perhaps it can't be done but here goes:
I have got my main activity to the point where it has data stored which includes a list of apps on the device that the user has selected to launch. On clicking a button on the main activity screen I would like the device to launch each of these selected apps in turn and then (ideally) return the user to the main activity that I have written. I would also like to define some restrictions on running each app - for example, each app runs for 30 seconds or until the app stops using the internet whichever comes earliest.
I don't believe I have any issue with linking all of this to the button click, nor is there any issue cycling through all of the selected apps. What I really need is the code to launch each app and then recall from it/move to the next app (ideally after the 30 seconds or when the app stops using the internet). Hopefully the below code makes clear where I am looking for help with the TODO. Does anyone know whether this is possible and if it is how can I get it done?
public class MainActivity extends Activity {
... //some code up here
//when the Run Apps button is clicked the onClick will run all of the selected apps using this Method:
public void RunApps(View view) {
//run through the list of Apps and run the ones that are selected
for (App application : list) {
if (application.isSelected()) {
/* TODO code that is meant to run the selected app and return to the main
* activity after say 30 seconds or when the app is done using the internet.
* As a starter I have the below but this is crashing and even if it did run
* I believe that it would not return me to the original main activity:
*/
Intent i = new Intent(Intent.ACTION_MAIN);
PackageManager manager = getPackageManager();
i = manager.getLaunchIntentForPackage(application.getPackageName());
i.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(i);
}
}
};
...//some more code here
}
just a couple of notes - the App class is defined elsewhere and includes the package name and whether the app has been selected by the user. list is a List of Apps.
I believe that the ACTION_MAIN and CATEGORY_LAUNCHER values may not be the best to use and perhaps startActivity(i) is not the right method for what I want but am not sure how that needs to be changed or whether there a more fundamental changes needed.
Many thanks for any help.
You should run each app from your top-level MainActivity sequentially by invoking them one-at-a-time.
Here's how:
Keep a counter in your MainActivity to indicate which app you are
currently invoking.
Use startActivityForResult() instead of startActivity() to start
your applications. This will cause execution to return to
MainActivity.onActivityResult() when each of the apps is finished.
The requestCode of startActivityForResult() will be returned to
onActivityResult(), so you will know which application completed.
Therefore, MainActivity can increment the counter and start the
next application in onActivityResult().
Restriction:
One of your requirements is to return to MainActivity after each
app completes. These steps satisfy that requirement.
Another requirement is to return to `MainActivity after all of the
apps are finished. These steps also satisfy that requirement.
You will know when you have finished
the final app because of the value of your counter.
The final requirement is to limit the duration of each app to 30
seconds. This is a more difficult problem. You will use a Timer in
your MainActivity as a watchdog to monitor the spawned apps. Use methods
described here to stop the spawned app when time runs out:
Finish an activity from another activity.
Warning: get everything else working first, before you try to externally stop an app.
That's all. Good luck!

Categories

Resources