I want to create a android app to track real time location with a android service. But when i kill the app from recent app list, Service also killed in android 6 (Huawei y3 2017). But same code works on android 8. Please help me to solve my problem. Please find below is my code.
I added START_STICKY and START_REDELIVER_INTENT flags in onStartCommand method.
I have tried to restart the service in onTaskRemoved method.
Tried to add android:process attribute to
Manifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Login" />
<activity android:name=".Home" />
<service
android:name=".TrackingService"
android:enabled="true"
android:exported="true" />
</application>
This is how start my service in Home activity
Intent intent = new Intent(Home.this, TrackingService.class);
startService(intent);
TrackingService.java service file
#Override
public void onCreate(){
buildNotification();
requestLocationUpdates();
Toast.makeText(getBaseContext(), "Service Started", Toast.LENGTH_SHORT).show();
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
return START_STICKY;
}
private void buildNotification() {
Random random = new Random();
int m = random.nextInt(9999 - 1000) + 1000;
String stop = "stop";
registerReceiver(stopReceiver, new IntentFilter(stop));
PendingIntent broadcastIntent = PendingIntent.getBroadcast(this, 0, new Intent(stop), PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.tracking_enabled_notif))
.setOngoing(true)
.setContentIntent(broadcastIntent);
startForeground(m, builder.build());
}
protected BroadcastReceiver stopReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
unregisterReceiver(stopReceiver);
stopSelf();
}
};
private void requestLocationUpdates() {
LocationRequest request = new LocationRequest();
request.setInterval(15000);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(this);
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("connected/"+user.getUid());
int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
if (permission == PackageManager.PERMISSION_GRANTED) {
client.requestLocationUpdates(request, new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
android.location.Location location =
locationResult.getLastLocation();
String lat = Double.toString(location.getLatitude());
String lng = Double.toString(location.getLongitude());
Toast.makeText(getBaseContext(), "Lat "+lat, Toast.LENGTH_SHORT).show();
if (location != null) {
ref.setValue(locationObj);
}
enter code here}
}, null);
}
}
The fact your service isn't getting killed on Android 8 sounds like a matter of luck. Probably your Android 8 phone has enough resources and keeps the service alive, but it will be killed sooner or later.
To achieve your goal you need to run your service in foreground. Check out official documentation:
You should only use a foreground service when your app needs to perform a task that is noticeable by the user even when they're not directly interacting with the app. For this reason, foreground services must show a status bar notification with a priority of PRIORITY_LOW or higher, which helps ensure that the user is aware of what your app is doing. If the action is of low enough importance that you want to use a minimum-priority notification, you probably shouldn't be using a service; instead, consider using a scheduled job.
Every app that runs a service places an additional load on the system, consuming system resources. If an app tries to hide its services by using a low-priority notification, this can impair the performance of the app the user is actively interacting with. For this reason, if an app tries to run a service with a minimum-priority notification, the system calls out the app's behavior in the notification drawer's bottom section.
Try to register your broadcast receiver in your main activity along with the intent actions that you want your receiver to do like this
private void RegisteringBroadcastReciever()
{
broadcastReceiver = new XYZIntentReceiver();
var filter = new IntentFilter();
filter.AddAction("hearing.arc.intent.action.action1");
filter.AddAction("hearing.arc.intent.action.action2");
filter.AddAction("hearing.arc.intent.action.action3");
filter.AddAction("hearing.arc.intent.action.action4");
AppInstance.ApplicationContext.RegisterReceiver(broadcastReceiver, filter);
}
Even i had a similar kind of issue. But i had that issue on Oreo devices as well. But this fix has resolved my issue. And Android asks to unregister this receiver in onDestroy() method. But don't do that. It might kill the service again.
Hope this helps.
Related
I'm building a simple reminder application that will remind you of your events.
I've just used "Alarm Manager" with "Broadcast Receiver " to trigger my service at a certain time in the future. The purpose of the service is to vibrate the device when the notification arrives. Notification is working fine but, the Service isn't working as expected. I know that I can simply put the code for vibration inside the "Broadcast Receiver" but NEED TO KNOW ABOUT THE SERVICE. Don't know what's wrong with my code help me out guys, THANKS in advance.
Broadcast Receiver as follows:
public class NotificationSetter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("AlarmTriggered", "Alarm is triggered to start background service");
Intent serviceVibes = new Intent(context,BackgroundService.class);
context.startService(serviceVibes);
Toast.makeText(context, String.valueOf(Build.VERSION.SDK_INT), Toast.LENGTH_SHORT).show();
//creating the Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "Notify");
builder.setSmallIcon(R.drawable.reminder);
builder.setContentTitle("Reminding of your event");
builder.setContentText("Time to play Cricket");
builder.setAutoCancel(true);
NotificationManagerCompat compat = NotificationManagerCompat.from(context);
compat.notify(3000, builder.build());
}
}
My service class:
public class BackgroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ServiceStarted", "Service is started using NotificationSetter");
Vibrator vibes = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
vibes.vibrate(VibrationEffect.createOneShot(2000,200));
}else {
vibes.vibrate(2000);
}
Toast.makeText(getApplicationContext(), String.valueOf(Build.VERSION.SDK_INT), Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
The Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.remindme">
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name="com.example.remindme.MyContext"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".StopAlarm"></activity>
<activity android:name=".GetEvent" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.example.remindme.BackgroundService"
android:enabled="true"
android:exported="true">
</service>
<receiver android:name=".NotificationSetter"
android:enabled="true"
android:exported="true"/>
</application>
</manifest>
According to documentation, since Android 8.0 afterwards, an app cannot start a foreground service unless in a few conditions:
https://developer.android.com/about/versions/oreo/background
Check if your service is triggered when you have your app in foreground, meaning that your app has a visible Activity to user. If it works in this condition, then it means your problem lies in background execution limits introduced in Android 8.0. So to make your service work, you can start it as a foreground service by calling this line of code:
ContextCompat.startForegroundService(context, new Intent(context, YourBackgroundService.class));
A foreground service needs a notification to be shown at status bar. You can either grab an instance of your alarm notification and use it as the foreground service notification or create a new notification.
My app uses a background service to listen to GPS position changes. It works on android 7.1.1 (even if my activity is closed or the screen is turn off). When I tried it on android 9 (with the AVD) it only works when the activity is in the foreground, otherwise not. It looks like the service is stopped when the app is closed. minSdkVersion is set to 23. Why app have different behavior on different API? Android does not guarantee the compatibility of apps on newer OS versions?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**code to request permission*/
startService(new Intent(this, EventControllerService.class));
}
}
public final class EventControllerService extends Service {
private final static int MIN_TIME_FOR_GPS_UPDATES = 1;
private final static float MIN_METER_FOR_GPS_UPDATES = 10.0f;
private static NotificationManager mNotificationManager;
private final BroadcastReceiver receiverDNDChange = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//work on receive
}
};
private final EventControllerService self = this;
private final LocationListener locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Toast.makeText(self, "DENTRO", Toast.LENGTH_LONG).show();
try {
//work to do on position change
} catch (SecurityException ignored) {
stopSelf();
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
};
#Override
public void onCreate() {
super.onCreate();
try {
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
IntentFilter filter = new IntentFilter();
filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
registerReceiver(receiverDNDChange, filter);
LocationManager mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_FOR_GPS_UPDATES, MIN_METER_FOR_GPS_UPDATES, locationListener);
} catch (SecurityException ignored) {
stopSelf();
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="marcomantovani.pcd.silentmap">
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<service android:name=".EventControllerService"/>
<receiver android:name="marcomantovani.pcd.silentmap.AutoStart">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
How can I change code to avoid this problem?
I understand you read this:
https://developer.android.com/about/versions/oreo/background
There are many changes related to background services and especially GPS in Android 9. Note this also (so targeting API23 is not a solution):
By default, these changes only affect apps that target Android 8.0 (API level 26) or higher. However, users can enable these restrictions for any app from the Settings screen, even if the app targets an API level lower than 26. You may need to update your app to comply with the new limitations.
What you can do right now and how we make it work in our apps. Migrate your service to compatible with Android 9. I suggest using:
startForegroundService()
Make your service to run on foreground. It's a small change. Just change one flag nad start your service this way.
Show notification that your app is working (this will keep your app working even when user is leaving the app otherwise - Android OS will limit your service)
Targeting API23 is a bad idea also because of this:
Starting August 1, 2019, Google Play requires that new apps target at least Android 9.0 (API level 28), and that app updates target Android 9.0 from November 1, 2019. Until these dates, new apps and app updates must target at least Android 8.0 (API level 26).
TL;DR
1. Migrate service to foreground
2. Create simple notification that will tell your user that app is working
3. Start service with startForeground()
Android has been steadily moving to restrict the access given to an app in the background. Location services were restricted from Android 8.
Reference:
https://developer.android.com/about/versions/oreo/android-8.0-changes#abll
I'm working on an application similar to Uber, and we need to calculate the wait time if the vehicle stops moving (or moving
I tried using GPS Location speed, but it doesn't seems to be helping. then we tried Activity recognition API that too is not giving immediate updates.
Is there any other way we could achieve this?. please note its not about detecting the speed. its about detecting if the vehicle is moving or not.
Thanks,
Aneef
As far as i can see it, you have 3 options :
You can implement your own technique for detecting driving - using Activity recognition and receiving location updates, though i recommend not to do so, don't reinvent the wheel, there are good apis developed already.
You could use a free sdk of Neura which can send you an event when your user starts driving and finishes driving.
Check out this git project : Basically, the project announces notifications with text-to-speech when you start driving, which isn't what you want, but you can do whatever you want when Neura sends you an event for start/finish driving.
Its very easy to just take this project and make it your own.
I highly recommend using this option.
You could use google's FenceApi in order to declare a driving fence.
Though this approach seems good, i've faced with the fact that this api didn't tell me sometimes when user starts/finishes driving, and sometimes it took a long time after i started driving that the api told me of that event.
a. include dependency to your app's build.gradle file :
compile 'com.google.android.gms:play-services-location:+'
compile 'com.google.android.gms:play-services-contextmanager:+'
b. Manifest definitions :
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme" >
<meta-data
android:name="com.google.android.awareness.API_KEY"
android:value="PUT_YOUR_AWARENESS_KEY_HERE" />
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
PUT_YOUR_AWARENESS_KEY_HERE : You need to generate a key here.
c. Your MainActivity class - explanations attached to the code :
public class MainActivity extends Activity {
private GoogleApiClient mGoogleApiClient;
private PendingIntent mPendingIntent;
private FenceReceiver mFenceReceiver;
// The intent action which will be fired when your fence is triggered.
private final String FENCE_RECEIVER_ACTION = BuildConfig.APPLICATION_ID + "FENCE_RECEIVER_ACTION";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this).addApi(Awareness.API).build();
mGoogleApiClient.connect();
// Set up the PendingIntent that will be fired when the fence is triggered.
mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(FENCE_RECEIVER_ACTION), 0);
// The broadcast receiver that will receive intents when a fence is triggered.
mFenceReceiver = new FenceReceiver();
registerReceiver(mFenceReceiver, new IntentFilter(FENCE_RECEIVER_ACTION));
createFence(DetectedActivityFence.IN_VEHICLE, "InVehicleFence");
}
#Override
public void onDestroy() {
try {
unregisterReceiver(mFenceReceiver); //Don't forget to unregister the receiver
} catch (Exception e) {
e.printStackTrace();
}
super.onDestroy();
}
private void createFence(int detectedActivityFence, final String fenceKey) {
AwarenessFence fence = DetectedActivityFence.during(detectedActivityFence);
// Register the fence to receive callbacks.
Awareness.FenceApi.updateFences(
mGoogleApiClient, new FenceUpdateRequest.Builder().addFence(fenceKey, fence, mPendingIntent)
.build()).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if (status.isSuccess()) {
Log.i(getClass().getSimpleName(), "Successfully registered.");
} else {
Log.e(getClass().getSimpleName(), "Could not be registered: " + status);
}
}
});
}
// Handle the callback on the Intent.
public class FenceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
FenceState fenceState = FenceState.extract(intent);
switch (fenceState.getCurrentState()) {
case FenceState.TRUE:
Log.i(fenceState.getFenceKey(), "Driving");
break;
case FenceState.FALSE:
Log.i(fenceState.getFenceKey(), "Not driving");
break;
}
}
}
}
I want to create an independent service of my main activity. This service is started at boot my phone and retrieves information via webservice every x minutes.
What are the best practices to do this?
Launch a service with BOOT_COMPLETED action?
There he other actions to launch a service without launching the main activity?
Another design or best practice?
I want a service with the same behavior as Facebook for example. This service is active all the time and displays a notification when you receive a message. If you click on the notification, it opens the Facebook application. But that kills the application, served remains active for receiving new messages.
My first test kills my service when I want to kill my main activity.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.mrbmx"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- The following two permissions are not required to use
Google Maps Android API v2, but are recommended. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="fr.mr.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="fr.mr.service.MyReceiver"
android:enabled="true"
android:exported="false"
android:label="OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name="fr.mr.service.LocalService"
android:enabled="true"
android:exported="false"
android:label="LocalService" />
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="#string/mapKey"/>
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
</application>
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = MyReceiver.class.getName();
#Override
public void onReceive( Context ctx, Intent i ) {
Log.d( TAG, "MyReceiver.onReceive : " + i.getAction() );
ctx.startService(new Intent().setComponent(new ComponentName(
ctx.getPackageName(), LocalService.class.getName())));
}
}
public class LocalService extends Service{
private static final String TAG = LocalService.class.getName();
private NotificationManager mNM;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = 1332;
private Timer timer ;
private int mId;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate");
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
timer = new Timer();
Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 0, note );
/*
Notification.Builder mBuilder =
new Notification.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("test title")
.setContentText("test content")
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
startForeground(1, mBuilder.getNotification());*/
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Received start id " + startId + ": " + intent);
mId = startId;
new Thread(new Runnable() {
public void run() {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
showNotification();
}
}, 0, 60000);
}
}).start();
return START_STICKY;
}
#Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
mNM.cancel(NOTIFICATION);
}
/**
* Show a notification while this service is running.
*/
private void showNotification() {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(getString(fr.mrbmx.R.string.notification_title))
.setContentText(getString(fr.mrbmx.R.string.notification_text))
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, MainActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(MainActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
}
}
Your service does not need to be active all the time to receive messages. Facebook, along with just about every other push based system, use Google Cloud Messaging (GCM) to wake up your device and send a message to your application from the remote server.
Of course, if you only need to periodically check (rather than be pushed information in near real time), then you can schedule an alarm to start your service every X minutes or build a Sync Adapter - an Android component specifically built to periodically load data from a remote server.
Note that many applications combine the two approaches and run a sync adapter in response to a GCM push.
I want to create an independent service of my main activity. This
service is started at boot my phone and retrieves information via
webservice every x minutes.
You can certainly do this by registering a BroadcastReceiver on your AndroidManifest.xml that responds to BOOT_COMPLETED and launch the Service from it. Your app will need to be ran at least once for BOOT_COMPLETED to be delivered (before API 11 it wasn't the case). Also, consider the implications of pulling data from a server too often (i.e. battery, etc).
I want a service with the same behavior as facebook for example. This
service is active all the time and displays a notification when you
receive a message. If you click on the nitification, it opens the
facebook application. But that kills the application, served remains
active for receiving new messages.
At least for their chat application, Facebook uses MQTT, which is a M2M publish/subscribe asynchronous mechanism. They do not pull data from the server every X minutes. They just listen to incoming data when it is broadcasted. If you want to use MQTT there is an open source project called Eclipse Paho that you might be interested in.
Status:--- I equally accept Karakuri's and Sharad Mhaske's answer, but since Sharad Mhaske answer after the start of bounty, the bounty should go to him.
Part-2 made: part-2 persistent foreGround android service that starts by UI, works at sleep mode too, also starts at phone restart
In stack overflow, only one answer may be accepted. I see both answers as acceptable but one has to be chosen (I chosed at random).
Viewers are invited to up/down vote answers/question to appreciate the effort!. I upvoted Karakuri's answer to compensate reputation.
Scenario:---
I want to make the user click a start/stop button and start/stop a service from UI activity. I have made the UI so dont care about that. But Just the logic of the Button click event.
Do not want the service to be bound to the UI activity. If activity closes, the service should keep running.
Want to make most effort that the service be persistent and does not stops in any case. Will give it most weight and run it as ForGroundSerice as it has a higher hierarchy of importance. (hope that's ok?)
Unless the stop button is clicked by my apps UI, do not want it to be stopped (or should restart itself) Even if android reclaim memory. I and the user of the phone, both are/will be aware of it. The service is most of importance. Even at sleep.
details= my app do some operations, sleep for user provided time (15 minuts usually), wakes and perform operations again. this never ends)
If I need AlarmManager, How to implement that? or any other way? Or just put the operations in a neverending while loop and sleep for 15 minuts at the end?
When the service is started (by clicked on start button). It should make an entry so that it auto starts if phone restarts.
QUESTION:---
Primary Question:
Just can't get an optimal strategy for the scenario... and also stuck on small bits of code, which one to use and how.
Gathered bits and pieces from stackoverflow.com questions, developer.android.com and some google results but cannot implement in integration.
Please read out the Requests Section.
Secondary Question:
The comments in my code are those small questions.
Research and Code:---
Strategy:
want this to happen every time the user opens the UI.
//Start Button:-----
//check if ForGroundService is running or not. if not running, make var/settings/etc "serviceStatus" as false
<-------(how and where to stare this and below stated boolean?)
//start ForGroundService
<-------(how?)
//make "SericeStatus" as true
//check if "ServiceStartOnBoot" is false
//Put ForGroundService to start on boot -------(to make it start when ever the phone reboots/restarts)
<-------(how?)
//make "ServiceStartOnBoot" as true
// the boolean can also be used to check the service status.
//Stop Button:------
//makes SericeStatus and ServiceStartOnBoot as false
//stops service and deletes the on boot entry/strategy
Activity UI class that starts/stops the service:
public class SettingsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
//some button here to start / stop and their onClick Listners
Intent mySericeIntent = new Intent(this, TheService.class);
}
private void startMyForGroundService(){
startService(mySericeIntent);
}
private void stopMyForGroundSerice(){
stopService(mySericeIntent);
/////// is this a better approach?. stopService(new Intent(this, TheService.class));
/////// or making Intent mySericeIntent = new Intent(this, TheService.class);
/////// and making start and stop methods use the same?
/////// how to call stopSelf() here? or any where else? whats the best way?
}
}
The Service class:
public class TheService extends Service{
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1, new Notification());
////// will do all my stuff here on in the method onStart() or onCreat()?
return START_STICKY; ///// which return is better to keep the service running untill explicitly killed. contrary to system kill.
///// http://developer.android.com/reference/android/app/Service.html#START_FLAG_REDELIVERY
//notes:-// if you implement onStartCommand() to schedule work to be done asynchronously or in another thread,
//then you may want to use START_FLAG_REDELIVERY to have the system re-deliver an Intent for you so that it does not get lost if your service is killed while processing it
}
#Override
public void onDestroy() {
stop();
}
public void stop(){
//if running
// stop
// make vars as false
// do some stopping stuff
stopForeground(true);
/////// how to call stopSelf() here? or any where else? whats the best way?
}
}
The Menifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:debuggable="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.myapp.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.myapp.SettingsActivity"
android:label="#string/title_activity_settings" >
</activity>
</application>
</manifest>
References:---
Android - implementing startForeground for a service? pointing answer 1, example code.
Trying to start a service on boot on Android
Android: Start Service on boot?
http://developer.android.com/guide/components/services.html
http://developer.android.com/reference/android/app/Service.html
http://developer.android.com/training/run-background-service/create-service.html not preffered by me.
http://developer.android.com/guide/components/processes-and-threads.html my starting point of research
Requests:---
I think this question is a normal practice for most people who are dealing with services.
In that vision, please only answer if you have experience in the scenario and can comprehensively explain the aspects and strategy with maximum sample code as a complete version so it would be a help to the community as well.
Vote up and down (with responsibility) to the answers as it matters to me who shared their views, time and experience and helped me and the community.
Que:Want to make most effort that the service be persistent and does not stops in any case. Will give it most weight and run it as ForGroundSerice as it has a higher hierarchy of importance. (hope that's ok?)
Answer:you need to start service with using START_STICKY Intent flag.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
Que:If I need AlarmManager, How to implement that? or any other way? Or just put the operations in a neverending while loop and sleep for 15 minuts at the end?
Answer:you need to register alarmmanager within service for the time after to some task.
//register alarm manager within service.
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("com.xxxxx.tq.TQServiceManager"), PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000 , 30 * 1000 , pendingIntent);
//now have a broadcastreceiver to receive this intent.
class Alarmreceiver extends Broadcastreceiver
{
//u can to task in onreceive method of receiver.
}
//register this class in manifest for alarm receiver action.
Que:When the service is started (by clicked on start button). It should make an entry so that it auto starts if phone restarts.
Answer:use broadcast reciver to listen for onboot completed intent.
public class StartAtBootServiceReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
try {
if( "android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
ComponentName comp = new ComponentName(context.getPackageName(), LicensingService.class.getName());
ComponentName service = context.startService(new Intent().setComponent(comp));
if (null == service){
// something really wrong here
//Log.Write("Could not start service " + comp.toString(),Log._LogLevel.NORAML);
}
}
else {
//Log.Write("Received unexpected intent " + intent.toString(),Log._LogLevel.NORAML);
}
} catch (Exception e) {
//Log.Write("Unexpected error occured in Licensing Server:" + e.toString(),Log._LogLevel.NORAML);
}
}
}
//need to register this receiver for Action_BOOTCOMPLETED intent in manifest.xml file
Hope this helps you clear out things :)
If you start a service with startService(), it will keep running even when the Activity closes. It will only be stopped when you call stopService(), or if it ever calls stopSelf() (or if the system kills your process to reclaim memory).
To start the service on boot, make a BroadcastReceiver that just starts the service:
public class MyReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, MyService.class);
context.startService(service);
}
}
Then add these to your manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application ... >
<receiver android:name="MyReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
Notice that the receiver is not enabled at first. When the user starts your service, use PackageManager to enable the receiver. When the user stops your service, use PackageManager to disable the receiver. In your Activity:
PackageManager pm = getPackageManager();
ComponentName receiver = new ComponentName(this, MyReceiver.class);
pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Use same method with PackageManager.COMPONENT_ENABLED_STATE_DISABLED to disable it.
I have made something like this myself but I learned a lot while developing it and discovered it is not completely necesary to have the service running all day draining your battery. what I made is the following:
Implement a Service that reacts to events. In my particular I wanted to automate my Wifi and mobile data connection. so i react to events like wifi connecting and disconnecting, screen turning on and off, etc. So this service executes what ever needs to be executed responding to this event and then stops, scheduling any further actions with the AlarmManager if so needed.
now, this events can by timers like you said yourself every 15 minutes it does something and sleeps, that sounds to me that you really dont want the service running 24/7 but just executing something every 15 minutes. that is perfectly achievable with the AlarmManager without keeping your service running forever.
I recommend implementing this service deriving from commonsware's WakefulIntentService.
This class already handles the wakeLock for you so that you can exceute code even if phone is asleep. it will simply wakeup execute and go back to sleep.
Now. About your question regarding the activity starting and stoping the service. you can implement in the button that it starts or cancels the AlarmManager alarm. Also you can use the sharedPreferences to store a simple boolean that tells you if it is enabled or not so the next time your service runs it can read the value and know if it should continue or stop.
If you implement it as a event-reactive service as i said, your button can even react to broadcast intents so that your activity doesn't even have to call the service directly just broadcast an intent and the service can pick it like other events. use a BroadcastReceiver for this.
I'll try to give examples but be wary that what you're asking is a lot of code to put it in one place...
BootReceiver:
public class BootReceiver extends BroadcastReceiver
{
private static final String TAG = BootReceiver.class.getSimpleName();
#Override
public void onReceive(final Context context, final Intent intent)
{
final Intent in = new Intent(context, ActionHandlerService.class);
in.setAction(Actions.BOOT_RECEIVER_ACTION); //start the service with a flag telling the event that triggered
Log.i(TAG, "Boot completed. Starting service.");
WakedIntentService.sendWakefulWork(context, in);
}
}
Service:
public class ActionHandlerService extends WakedIntentService
{
private enum Action
{
WIFI_PULSE_ON, WIFI_PULSE_OFF, DATA_PULSE_ON, DATA_PULSE_OFF, SCREEN_ON, SCREEN_OFF, WIFI_CONNECTS, WIFI_DISCONNECTS, WIFI_CONNECT_TIMEOUT, WIFI_RECONNECT_TIMEOUT, START_UP, BOOT_UP
}
public ActionHandlerService()
{
super(ActionHandlerService.class.getName());
}
#Override
public void run(final Intent intent)
{
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
mSettingsContainer.enabled = mSettings.getBoolean(getString(R.string.EnabledParameter), false);
if (intent != null)
{
final String action = intent.getAction();
if (action != null)
{
Log.i(TAG, "received action: " + action);
if (action.compareTo(Constants.Actions.SOME_EVENT) == 0)
{
//Do what ever you want
}
else
{
Log.w(TAG, "Unexpected action received: " + action);
}
}
else
{
Log.w(TAG, "Received null action!");
}
}
else
{
Log.w(TAG, "Received null intent!");
}
}
}
And your Manifest could go something like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.yourcompany.yourapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="false"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.yourcompany.yourapp.activities.HomeActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.yourcompany.yourapp.services.ActionHandlerService" />
<receiver android:name="com.yourcompany.yourapp.receivers.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
</application>
</manifest>