I use to connect with server a socket :
Socket requestSocket.connect(new InetSocketAddress(a, 6666), 3000);
It works pretty well all the time except when device stays in idle mode for longer time say 30 mins or so. After 30 mins if I bring device to wake state, and try to contact to server thro' my app it doesn't throw any exception. Which shows me that my socket connection is in still live state. But when I check at the server end same data is not received here.
Battery way is use background service with startForground
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
onHandleIntent(intent);
return START_STICKY;
}
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Notification.Builder builder = new Notification.Builder(getBaseContext())
.setContentTitle("")
.setContentText("Your content text");
startForeground(1, builder.build());
Socket requestSocket.connect(new InetSocketAddress(a, 6666), 3000);
}
}
This service will never pause/closed your socket connection even your app is closed or removed from the recent app.
You can use Bind service from UI if you want to update UI from background service
#Override
public IBinder onBind(Intent intent) {
if (TweetCollectorService.class.getName().equals(intent.getAction())) {
Log.d(TAG, "Bound by intent " + intent);
return apiEndpoint;
} else {
return null;
}
}
Related
My Android application (targeting API 33) invokes a foreground service when the app is launched for the first time. The service is used for sending a 'heartbeat' / status to an API which writes the timestamp of the last heartbeat / status update to a database.
Starting the foreground service in the 'MainActivity':
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity);
if(!clientServiceRunning()) {
Intent serviceIntent = new Intent(this, ClientService.class);
this.startForegroundService(serviceIntent);
}
authenticateUser();
checkNetworkState();
storeTerminalId(TERMINAL_ID);
}
Code of the foreground service class:
public class ClientService extends Service {
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public ClientService() {
super();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Thread that sends the heartbeat to the API
new Thread(
new Runnable() {
#Override
public void run() {
while (true) {
Log.d("Client Service", "Service is running");
// Sends request to API using Androids Volley library
sendHeartbeat();
try {
// Only send heartbeat every 2 minutes
Thread.sleep(120000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
).start();
new Thread(
new Runnable() {
#Override
public void run() {
// Some other task
}
}
).start();
// Necessary notification details for foreground service
final String CHANNEL_ID = "Client Service ID";
NotificationChannel notificationChannel = new NotificationChannel(
CHANNEL_ID,
CHANNEL_ID,
NotificationManager.IMPORTANCE_LOW
);
getSystemService(NotificationManager.class).createNotificationChannel(notificationChannel);
Notification.Builder serviceNotification = new Notification.Builder(this, CHANNEL_ID)
.setContentText("Client Service running the background")
.setSmallIcon(R.drawable.launcher_foreground_icon);
startForeground(1, serviceNotification.build());
return super.onStartCommand(intent, flags, startId);
}
}
Method that makes the API heartbeat request:
(Note that JSONRequestNoResponse is a custom volley response class)
private void sendHeartbeat() {
try {
RequestQueue requestQueue = Volley.newRequestQueue(this);
String URL = "https://api.myapplication.com/device/heartbeat";
JSONObject jsonBody = new JSONObject();
jsonBody.put("terminalId", String.valueOf(terminalId));
jsonBody.put("scannerConnectionState", String.valueOf(isScannerConnected));
JSONRequestNoResponse jsonRequest = new JSONRequestNoResponse(Request.Method.PUT, URL, jsonBody,
new Response.Listener<>() {
#Override
public void onResponse(JSONObject response) {
Log.d("Volley", "API: Heartbeat sent");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.w("Volley ERROR", "Service - Error Response");
}
}
);
requestQueue.add(jsonRequest);
} catch (JSONException e) {
e.printStackTrace();
}
}
When debugging and checking the Logcat output in Android Studio the Log.d() messages are displayed and I can see that in my database, the timestamp is renewed every two minutes since the service calls the API to initiate a this database update. The service also keeps running when the application is closed or when my device goes to sleep - as expected from a foreground service.
Now to my problem:
The service and the hence calling the API only works when my test device is connected to Android Studio. (The device and my PC are connected via a USB C cable). As soon as I disconnect the device from my PC, the service keeps running but the timestamp in the database is not updated every two minutes anymore.
When I reconnect my device to my PC and open Android Studio, the Logcat messages ("Client Service", "Service is running") resume and also the timestamp in the DB keeps updating regularly again.
So why does the service only really work when it is connected to a PC/Android Studio. I find this to be very peculiar.
My goal is to have a service polling a Server from the background and notifying the user he gets interesting data. Basically just like WhatsApp does (WhatsApp can be closed and the phone can be sleeping and you are still notified whenever you get a message).
I know about the Service class as well as the IntentService class and that they can do background operations and extend above the lifecycle of an activity.
To make myself familiar with them I wrote a test project with a server which basically just accepts socket connections and prints its input into the console.
Client-side, though, I just cannot get the service to stick around. After 5 seconds of the app being closed the service stops (onDestroy is not even called).
I've tried returning START_STICKY in the onStartCommand() method. I've also tried different approaches polling the data, such as using google's Volley library to fire requests every 5 seconds, as well as an ongoing, lasting Socket connection sending packets of data to the Server every 5 seconds. None of those attempts have been working, though, and the service always is killed. The sticky mode will . I've read some things about AlarmManager to restart the killed process, but some people said that this approach would be bad practice since the AlarmManager can be quite unreliable.
Is there something I am missing or doing wrong? Here is my client-side code:
This is the service's onStartCommand:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Service has started");
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("chId", "chName", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true);
channel.enableVibration(true);
notificationManager.createNotificationChannel(channel);
}
showNotification("Service has started");
long start = System.currentTimeMillis();
long now;
BufferedReader input = null;
PrintWriter output = null;
try {
Socket connection = new Socket("192.168.178.21", 6789);
input = new BufferedReader(new InputStreamReader(connection.getInputStream()));
output = new PrintWriter(connection.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
now = System.currentTimeMillis();
if ((now - start) >= 5000) {
output.write("Hi" + System.lineSeparator());
output.flush();
start = now;
}
}
}
}).start();
return START_STICKY;
}
and its onDestroy method
#Override
public void onDestroy() {
showNotification("on Destroy");
super.onDestroy();
}
The showNotification method just pushes a new notifcation forward:
private void showNotification(String text) {
NotificationCompat.Builder notification = new NotificationCompat.Builder(getApplicationContext(), "chId")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setTicker("Ticker")
.setWhen(System.currentTimeMillis())
.setContentTitle("Titel")
.setLights(Color.BLUE, 3000, 3000)
.setContentText(text);
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setContentIntent(pendingIntent);
notificationManager.notify((int) System.currentTimeMillis(), notification.build());
}
I have been reading about this topic a lot so I hope this is not a duplicate question. Thank you guys for your help!
I am developing android application, so I am starting a service with alarm:
public void scheduleLocationCheckerAlarm() {
Intent intent = new Intent(getApplicationContext(), LocationCheckerReceiver.class);
final PendingIntent pIntent = PendingIntent.getBroadcast(this, LocationCheckerReceiver.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long firstMillis = System.currentTimeMillis();
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis, 600000, pIntent);
}
LocationCheckerReceiver:
public class LocationCheckerReceiver extends BroadcastReceiver {
public static final int REQUEST_CODE = 12345;
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, LocationNotificator.class);
context.startService(i);
}
Service:
public class LocationNotificator extends Service {
public LocationNotificator() {
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Location checker", "Service running");
//My code is here
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("Location checker", "Service destroyed");
}
So I want this service to be checking for something every 1 minute and to be running all the time, even when the application is closed by the user.
You must call startForeground(FOREGROUND_ID, buildForegroundNotification(filename)); in order to ensure that your service running continuously. Also, this will post a notification from your app to show the user about the service state. Please follow the reference.
Here is the code :
public class LocationNotificator extends Service {
private static int FOREGROUND_ID=1338;
public LocationNotificator() {
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Location checker", "Service running");
//My code is here
startForeground(FOREGROUND_ID,
buildForegroundNotification(filename));
stopForeground(true);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("Location checker", "Service destroyed");
}
private Notification buildForegroundNotification(String filename) {
NotificationCompat.Builder b=new NotificationCompat.Builder(this);
b.setOngoing(true);
b.setContentTitle("Some Title")
.setContentText("some File name")
.setSmallIcon(android.R.drawable.stat_sys_download)
.setTicker("downloading");
return(b.build());
}
You need to read this first. https://developer.android.com/guide/components/services.html
In a nutshell, start your service as a foreground service so there's lesser chance of Android killing your service. As a foreground service, you need to display an on-going notification in the status bar.
There's no direct way of making sure your service is never killed by the Android system. A workaround is to send a broadcast in onDestroy() of your service, and have a Receiver in your Android application start the service upon receiving the broadcast.
By the way, it seems that your service is sending location updates periodically to your backend server. This might be better implemented using Firebase Job Dispatcher library or Evernote's Android-Job library.
Recent changes in Androids background task running behaviour makes it very difficult to keep Services alive and continue work in applications when the phone is locked. My Service is only working properly when the screen is on or the phone gets charged. When the phone is locked, the Service shuts down almost immediately or runs way too slow to be useful in any way.
I tried to use "START_STICKY" flag and a startForeground() Notification to keep the Service alive but this doens't help at all. I'm using a Handler that calls dowork() every 5 seconds, which then checks if theres something to do.
I want to perform a simple task on a certain time event: wake up every half/quarter or full hour, do some quick work without CPU limitation, then shut down until next time. The phone should wake up reliable and accurate on time and get "whitelisted" to use some CPU power for around half a minute. I don't do any intense work, that could affect user performance.
public class MyService extends Service {
public MyService() {
super();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("MyService")
.setContentText("Service is running")
.setPriority(IMPORTANCE_HIGH)
.setContentIntent(pendingIntent).build();
startForeground(1, notification);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
dowork();
handler.postDelayed(this, 5000);
}
}, 1500);
return START_STICKY;
}
For this question i want to refer to Alarm Manager Example
. This one is doing it's job pretty well, i finally got it working that way.
I have an app that gets users messages from database and if there is a new message it pushes a notification i use a service for that.. The service works fine when the app is opened or in the foreground.. But when I close it it doesn't work.. It is not destroyed or stopped it's just doesn't work :S I don't know why.. This is my service code :
public class BGService extends Service {
ArrayList<Message> messages = new ArrayList<Message>();
ArrayList<String> requests = new ArrayList<String>();
Timer timer = new Timer();
Timer timer2 = new Timer();
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onDestroy() {
Log.d("Chat", "BGService Destroyed");
timer.cancel();
timer.purge();
timer2.cancel();
timer2.purge();
}
#SuppressWarnings("unchecked")
#Override
public void onStart(Intent intent, int startId) {
Log.d("Chat", "BGService Started");
messages = (ArrayList<Message>) intent.getExtras().get("messages");
requests = (ArrayList<String>) intent.getExtras().get("requests");
Log.d("Button Clicked", "Messages: " + messages);
new Timer().scheduleAtFixedRate(new TimerTask() {
public void run() {
Log.d("Service", "Running");
}
}, 2000, 2000);
}
}
Your telling the service to stop in your code. Because your using onBind() it appears that your not starting the service and instead binging to it. If you bind to a service the service automatically ends when your activity ends.
If you want to keep your service running.
Start the service to keep service running
Bind to the service to have a service while app is running
Set a notification so the service is in the foreground
Change your manifest so the service runs in a separate process
Start the service so you can return startsticky in the onStartCommand() to tell the os you want this to stick on
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
Start Service. Bind to the service only if you have a connection to communicate with the service back and forth with. This is returned from onBind()
startService(new Intent(context, ServiceLocationRecorder.class));
// bind to the service
if (!mIsBound) {
// Bind to the service
bindService(new Intent(context,
ServiceLocationRecorder.class), mConnection,
Context.BIND_AUTO_CREATE);
mIsBound = true;
}
Binding to the service is used to setup a binder handler that you can communicate to and from the service with. Returning null in onBind() defeats the purpose of the onBind() event so you could skip this code.
/**
* When binding to the service, we return an interface to our messenger for
* sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
set the service to the foreground you do this and the os will be less likely to end your service to get memory for another app.
//this is done inside the service
startForeground(R.id.action_record, getMyCustomNotification());
Run the service in its own process then GC will be able to collect your activity and keep the service running.
<service
android:name="com.example.service"
android:process=":myseparateprocess" >s -->
</service>