I have an activity that send some details to my db, but when I click the back button it stops.
Is it possible to make it run on the background?I heared that using a Service could solve that issue but since all of my classes are wrriten as Activities I would like to know if it's possible.
It's can't be done with some code on the onStop method?
Heey,
Create a new class that extends of Service.
Add the necessary overrides.
#Override
public IBinder onBind(Intent intent)
{
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
//this service will run until we stop it
// This will execute when the service starts
return START_STICKY; //makes it sticky to keep it on until its destroyd
}
#Override
public void onDestroy()
{
super.onDestroy();
// when the service is destroyd
}
You can start a service with:
stopService(new Intent(getBaseContext(), ServiceClassName.class));
You can stop a service with:
stopService(new Intent(getBaseContext(), ServiceClassName.class));
Hope this helps :)
You mentioned that all of your classes are written as Activities, which I am assuming means you have your application logic embedded with your user interface logic. This is generally not a good practice - you should try and migrate application specific code to a separate class. By having this functionality outside your user interface, your application will benefit in numerous ways.
To answer your question, it is possible to have your application perform functions in the background by subclassing Service. Take a look here for a great explanation.
You will have to override the onBackPressed method to do what you want. If you want to "hide" the activity (so the user sees that it 'closes') you can call the moveTaskToBack method.
You should write a Service that the Activity communicates with using intents. Thus it should send the data to the service for it to send to the database, then the service can inform the activity. When the Activity listens to events from the Service, you are implementing the Observer pattern (Listeners in Java and Android and many other event processing systems).
You can also poll the service, but that is not the preferred pattern for getting status updates.
Related
I am learning Java and Android development. Right now I am making an app that once launched will create a service. Then I want the service to do things based on a button that I press within my app.
This is my service class.
public class ServiceClass extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
// Issue is here. I cannot use findViewById
Button start_stop_button = (Button) findViewById(R.id.button2);
start_stop_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
// stop the wifi manager
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
My thought was I could just set a listener so when I press the button the onClick() would execute. But that wouldn't work since the service is not attached to any activity to know what the id "button1" is.
So then I thought about keeping a class variable and just updating it. These seems fairly simple but then I am not sure how I would be able to keep the service checking for the status of the variable to change. I could put it in a for loop to continue checking but I feel like a better way exists.
tldr; I have an app that kicks off a service. I want my app to be able to be closed and the service still run. But I want to have a start/stop button in my app that will trigger the service.
You don't need a button as such to explicitly stop a service because it will be stopped automatically when the job inside onStartCommand is done
If you want manual control for the service inside your Activity where your button actually is then you can use stopService and startService methods
You can find a detailed explanation here
Also Service is meant to be Ui-less, so you can't do something related to the UI in the service, that's not what it is meant for. You can pop a notification and on tap you can start your activity which can give you access to the button if you're wishing to do something like that
The Service class will load a library and it takes about 4-5 seconds for the library to become ready. What is the best way to make MainActivity to keep checking on the status of a static boolean in Service class and do something when it's ready? I looked around and knew that using busy wait loop is bad.
The outline of my planned MainActivity is
onCreate - start the service
onResume - show the splash screen until a specific boolean in Service become true then switch to another fragment
You could use a broadcast receiver from the Service to your MainActivity which triggers a method inside the MainActivity... instead of constantly checking a static bool in the service.
But you want to be very sure to handle cases where it never loads for whatever reason, otherwise users will be staring at a splash screen forever.
Simple solution is Broadcast Reciever Try this
BroadcastReceiver broadCastNewMessage = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do your stuff here
}
};
Now in onCreate() register this
registerReceiver(this.broadCastNewMessage, new IntentFilter("bcNewMessage"));
And in onDestroy()
unregisterReceiver(broadCastNewMessage);
Now Call this method from the service class where u want to update the activity
sendBroadcast(new Intent().setAction("bcNewMessage"));
When we press this button
We see the apps which we didn't close, like this
But when we want to close an app from this screen (below image), the method onDestroy() isn't called, however the app is closed. I need to call onDestroy() when the app is closed in this way. How can I do this?
As specified in the Android documentation, it is not guaranteed that onDestroy() will be called when exiting your application.
"There are situations where the system will simply kill the activity's hosting process without calling this method"
https://developer.android.com/reference/android/app/Activity.html#onDestroy%28%29
Instead, you can create a service which will be notified when the Task your activities are running inside is destroyed.
Create the service class:
public class ClosingService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
// Handle application closing
fireClosingNotification();
// Destroy the service
stopSelf();
}
}
Declare / register your service in the manifest (within the application tag, but outside any activity tags):
<service android:name=".services.ClosingService"
android:stopWithTask="false"/>
Specifying stopWithTask="false" will cause the onTaskRemoved() method to be triggered in your service when the task is removed from the Process.
Here you can run your closing application logic, before calling stopSelf() to destroy the Service.
You should read some info about Activity lifecycle. There is one thing about onDestroy method, it doesn't get called all time. You mustn't rely on it.
Specify please what are you trying to achive and I'll try to offer better solution.
Suggestion
So, if I understood you right, I can suggest one thing. Start a Service that will fire LocalBroadcast every N seconds (it's not really heavy to system). Register and BroadcastReceiver for this broadcast in Activities. This way you'll get true or false depending on if there is any BroadcastReceiver that can catch your LocalBroadcast. And if no receivers than check for some SharedPreferences value that indicates if Button was pressed.
More promising approach than using a bound service would be using activity lifecycle callbacks in the Application. Though the approach shown in the accepted answer would work but the service would be running in the background until the activity is terminated which is expensive. Instead, I would suggest the use of your implementation of Application.
1) Make a class extending Application, then use it by providing its name in the name attribute of Application tag in Manifest file
class MusicPlayerApplication: Application() {
private val TAG = MusicPlayerApplication::class.java.simpleName
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object: ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity?) {
}
override fun onActivityResumed(activity: Activity?) {
}
override fun onActivityStarted(activity: Activity?) {
}
override fun onActivityDestroyed(activity: Activity?) {
Log.d(TAG, "onActivityDestroyed: ")
val activityName = activity!!.localClassName
}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
}
override fun onActivityStopped(activity: Activity?) {
}
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
}
})
}
}
AndroidManifest.xml
<application
android:name=".MusicPlayerApplication"
....
I have tested this approach using logcat, my onDestory is not getting called but onActivityDestroyed in the callback is getting called every time I kill the activity from RAM but this doc says that onActivityDestroyed would be called when onDestory of an activity is called but that doesn't seem to happen. However, I find this approach better than using services.
I'm writting my own plug-in for an existing game engine (so to say it's 3rd-party lib in relation to the main application).
So, I have no access to the MainActivity sources.
Nevertheless I have to react somehow on main activity lifecycle events (onCreate, onDestroy, onPause, onResume, onNewIntent and some unimportant others).
Thanks to Application.ActivityLifecycleCallbacks, I have no problems with most of them.
The problem occurs with onNewIntent(). I can't find out a listener for this event and imagine a way to handle it.
Does anybody know how to catch onNewIntent event (surely, except overriding it)?
onNewIntent() works for singleTop or singleTask activities which already run somewhere else in the stack. if the MainActivity is not declared with singleTop or singleTask attributes, even if you use below code, it won't work:
#Override //won't be called if no singleTop/singleTask attributes are used
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// ...
}
To assure all setup logic hooked, it is best use onResume() by utilizing getIntent().
#Override
protected void onResume() { //will be called in any cases
super.onResume();
// getIntent() should always return the most recent
}
I have an app in which I want to perform lookup some data every, say, 15 mins. I have a service that I start with an alarm, but I also want to make sure that there is a network connection before I start looking.
To do this I think I should use a BroadcastReceiver to watch for changes to the network state. I have wrapped a broadcast receiver to help with this:
public abstract class NetworkMonitor extends BroadcastReceiver
{
boolean mDoingStuff;
public abstract void doStuff();
public NetworkMonitor()
{
mDoingStuff = false;
IntentFilter networkStateFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
MyApp.getContext().registerReceiver(this, networkStateFilter);
}
#Override
public void onReceive(Context context, Intent intent)
{
// network state changes, you can process it, information in intent
ConnectivityManager cn = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = ConnectivityManagerCompat.getNetworkInfoFromBroadcast(cn, intent);
// Only use Wifi Connections for updating this
if (info.isConnectedOrConnecting() && !mDoingStuff)
{
mDoingStuff = true;
doStuff();
}
}
}
I then use that in a service like:
public class WidgetUpdateService extends Service
{
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// Build the async task to get the data
final MyAsyncTask mTask = new MyAsyncTask();
// Register an interest in when the network changes
new NetworkMonitor(false)
{
public void doStuff()
{
mTask.execute();
}
};
// Make sure that if we get shut down then we get started again correctly.
return START_REDELIVER_INTENT;
}
protected class MyAsyncTask extends AsyncTask<Void, Void, Void>
{
public MyAsyncTask()
{
}
#Override
protected Integer doInBackground(Void... arg0)
{
// do work
}
#Override
protected void onPostExecute(Integer result)
{
WidgetUpdateService.this.stopSelf();
}
#Override
protected void onCancelled(Integer result)
{
WidgetUpdateService.this.stopSelf();
}
}
}
Where MyAsyncTask is an inner class that will cause the service to stopSelf() when it has completed.
This kinda works but:
I'm getting (according to logcat) far more calls to NetworkMonitor.doStuff() than I would expect. It seems that even when the service has been stopped (after the async task has finished correctly) the NetworkMonitor instance is still being receiving intents about changes to the network state. Why is this?
Do I need to have a variable to store the NetworkMonitor() instance in the service, or can I just have an anonymous instance like this? Looking at the docs the BroadcastReceiver should clear itself up after onReceive() has finished.
Why do I need NetworkMonitor.mDoingStuff? I'm guessing that if I can work out why the NetworkMonitor is not clearing itself up after onReceive() has finished then I may not need it any more?
Is this a sensible way of doing this or am I asking for trouble?
Please let me know if you need any more info, I'll be happy to supply it.
This kinda works
It's ghastly code, IMHO.
It seems that even when the service has been stopped (after the async task has finished correctly) the NetworkMonitor instance is still being receiving intents about changes to the network state. Why is this?
Because you never unregister the receiver. It will keep going -- and leaking memory like a sieve -- until your process is terminated.
Do I need to have a variable to store the NetworkMonitor() instance in the service, or can I just have an anonymous instance like this?
You need to have an instance so you can unregister it later. Registration and unregistration of the receiver should be done by the service; your register-the-receiver-in-its-constructor is part of what makes your code ghastly IMHO.
Looking at the docs the BroadcastReceiver should clear itself up after onReceive() has finished
A manifest-registered BroadcastReceiver lives for a single broadcast. A BroadcastReceiver registered via registerReceiver() lives until unregisterReceiver().
Why do I need NetworkMonitor.mDoingStuff?
You have bigger problems.
Is this a sensible way of doing this
Not really.
First, you will crash on your second broadcast, as you cannot execute() an AsyncTask instance multiple times.
Second, see the aforementioned failing-to-unregister issues.
Third, if you want to have a service that does one thing, then goes away, use an IntentService.
So, let's roll all the way back to the top:
I have an app in which I want to perform lookup some data every, say, 15 mins. I have a service that I start with an alarm, but I also want to make sure that there is a network connection before I start looking.
The right way to do this is:
Have your AlarmManager event route to a BroadcastReceiver. This is particularly important if you are using a _WAKEUP alarm type, as such events are only reliable if you use a BroadcastReceiver PendingIntent.
In that BroadcastReceiver, in onReceive(), if you have a network connection, send a command to an IntentService to do the work (and, if you are using a _WAKEUP alarm type, consider my WakefulIntentService, so the device stay awake while you're doing this).
If, instead, there does not appear to be a network connection, have your BroadcastReceiver enable another manifest-registered BroadcastReceiver set up to watch for CONNECTIVITY_ACTION events -- use PackageManager and setComponentEnabledSetting() for this.
In the CONNECTIVITY_ACTION BroadcastReceiver, in onReceive(), if you determine that you now have a network connection, kick off your IntentService (same as what you would do from the AlarmManager receiver if you already had a connection).
In the IntentService/WakefulIntentService, do your work in onHandleIntent(). This already has a background thread and will already call stopSelf() when there is no more work to be done.
In the IntentService/WakefulIntentService, in onDestroy(), disable the CONNECTIVITY_ACTION BroadcastReceiver via PackageManager and setComponentEnabledSetting(), returning you to your original state.
This way:
You don't leak memory, as you are doing here.
You don't have to mess with threading code, as you are doing here.
You don't have to worry about whether or not your process gets kicked out of memory in between the alarm and gaining connectivity.
If connectivity is blocked for some time (e.g., airplane mode), you don't register N receivers and set up N AsyncTasks, as you are doing here. Instead, you will get control again whenever connectivity changes occur in the future after your alarm has gone off.