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.
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"));
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 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.
I'm trying to implement a looper so I can create a thread for sending GPS coordinates to a database. I'm new to Java and this is all really confusing, but maybe you can help. The following class method is called by the main activity when a button is pressed. The method finds the GPS location and then sends it to a different class's method that sends the latitude and longitude (along with the user id) to a database. I'm having trouble with my app slowing down and freezing at times, so it appears I need to implement some way to create a new thread for this process. Any ideas. I'm a noob so be gentle. Here is my code:
public class LocationActivity extends Thread{
private LocationManager locManager;
private LocationListener locListener;
public void startLocation(Context context, String usr_id2)
{
final String usr = usr_id2;
//get a reference to the LocationManager
locManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
//checked to receive updates from the position
locListener = new LocationListener() {
public void onLocationChanged(Location location) {
SendActivity.send(location, usr);
}
public void onProviderDisabled(String provider){
//labelState.setText("Provider OFF");
}
public void onProviderEnabled(String provider){
//labelState.setText("Provider ON ");
}
public void onStatusChanged(String provider, int status, Bundle extras){
//Log.i("", "Provider Status: " + status);
}
};
Looper looper = null;
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locListener, looper);
}
}
One other question, would I add the looper here or in the class that actually sends the information to the database? It uses HTTP Post to send to a PHP program that adds the info to a MySQL database.
I would be tempted to try a different tactic here. Android has a class called AsynTask which is written to ease the burden on developers of offloading work to be done on a thread.
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
In terms of writing to the DB and sending data back to a server, I would look at using a Service, but remember that the only Service that doesn't natively run on the UI thread is the IntentService
You should consider using AsyncTask class for such operations, it's pretty easy to use and it provides a simple way of synchronizing with the UI thread. Here's a nice post about threading in Android, you can learn more about AsyncTask in the Android docs. Hope this helps.
You can't access the 'context' or any UI object from a custom thread. This is because these are made in the main UI thread and you can't access these objects from your thread.
Just as Martyn said, you need to create a AsyncTask or use Handlers to access the context/ui.
In your case, go for the AsyncTask