Android Service Thread only runs when connected to Android Studio - java

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.

Related

How to send data to Firebase continuously avoiding Doze and App Standby

I developed this app that needs to send data to Firestore when user press a button and ends when user stop it (by pressing another button). The data must be sent even if the user is doing other things or even if the user leavs the phone in standby for hours (so i need to avoid Doze and App Standby).
I used a service to achieve this and just one thing seems to work in the wrong way.
Service
public class MyService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
// vars declaration
private Date date = null;
// get every X seconds
public static final long DEFAULT_SYNC_INTERVAL = 60000;
private Runnable runnableService = new Runnable() {
#Override
public void run() {
class GetDataTask extends AsyncTask<String, Void, List<Data>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
if(getPositionData.isCancelled()){
return;
}
}
#SuppressLint({"MissingPermission", "HardwareIds"})
#Override
protected List<Data> doInBackground(String... v) {
// skipping get Timestamp code
// skipping get position code
myPos = new Data(position.getId(), latitude, longitude, timestamp);
// Insert data into Firebase
documentReference = firebaseFirestore.collection("data").document();
Map<String, Object> data = new HashMap<>();
data.put("lat", myPos.getLat());
data.put("date", myPos.getDate().toString());
documentReference.set(data).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.i("data", "data added.\n");
}
});
Toast.makeText(DataPollService.this, "" +
"ID: " + myPos.getImei()
+ " Latitude: " + myPos.getLat()
+ " Longitude " + myPos.getLng()
+ " Date: " + myPos.getDate()
, Toast.LENGTH_SHORT).show();
}
}
}, Looper.getMainLooper());
positionsList.add(myPos);
return positionsList;
}
protected void onPostExecute(List<Data> result) {
Position.getInstance().setPositions(result);
}
}
// RUN TASK
getPositionData = new GetDataTask();
getPositionData.execute(position.getId());
handler.postDelayed(runnableService, DEFAULT_SYNC_INTERVAL);
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ksurf)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
handler = new Handler();
handler.post(runnableService);
return START_NOT_STICKY;
}
// onBind
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
Log.d("show", "onDestroy");
handler.removeCallbacks(runnableService);
stopSelf();
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
In MainActivity starting the service like this:
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("inputExtra", "run");
ContextCompat.startForegroundService(this, serviceIntent);
If I don't use the phone and let the app sending data in background, the data are not sent to Firebase until I open the app back again. The service is not stopped, I just need to open the app in order to send data to firebase!
I read about Firebase Cloud Messaging but I didn't understand if I need them for my purpose. What am I doing wrong?
The data must be sent even if the user is doing other things or even if the user leavs the phone in standby for hours (so i need to avoid Doze and App Standby).
Generally speaking, it is not a good idea to have your app running processes when in Doze or App Standby mode. The Android documentation even points out that Network access is suspended; and therefore, your process might not be guaranteed to run reliably or terminate over other apps that may have a higher priority.
The problem is that if I don't use app for like 1 hour (so phone is in standby) data on firebase are only added when I open the app again. It's like they are saved in cache and sent to DB when app is opened again.
According to the documentation, "Cloud Firestore supports offline data persistence. This feature caches a copy of the Cloud Firestore data that your app is actively using, so your app can access the data when the device is offline. You can write, read, listen to, and query the cached data. When the device comes back online, Cloud Firestore synchronizes any local changes made by your app to the Cloud Firestore backend."
The service is not stopped, I just need to open the app in order to send data to firebase! I read about Firebase Cloud Messaging but I didn't understand if I need them for my purpose.
A recommended solution would to ping your app from your server using Firebase Cloud Messaging when your client app goes into idle mode. This feature is useful when you need to send real-time downstream messages to your backend server or simply notify your client app that new data is available to sync (which may be what you're looking for).
You can refer to the above documentation for further details.
in every platform or language, there is a deferent way to connect to the firebase, so, please more information about the platform can help ??,
but you can check this link maybe his can to help you -> link

How to setup a service continualy fetching data from Server, even when phone is sleeping?

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!

Java Android socket gets killed imidiatelly after screen goes blank

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;
}
}

Service code stops when application is stopped

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>

Android Bump Api Network on main thread exception

First of all, I'm quite new to the Android and JAVA world (coming from C/C++/Objective-C).
I'm trying to integrate the Android bump API (3.0, latest version) but I'm running into trouble.
I copied the exemple, it is working fine under Android 2.2, bump services are started correctly, but as for Android 3.0 and upper it does not works.
I've got an exception (the network on main thread one) when loading my activity, I know this exception and how to avoid it, but in this case, Bump state that they run their API in their own thread so I don't really know why I got it. They said that you don't need to run a thread or tasks.
Here is a sample of my Activity
public class BumpActivity extends Activity {
private IBumpAPI api;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bump);
bindService(new Intent(IBumpAPI.class.getName()), connection,
Context.BIND_AUTO_CREATE);
IntentFilter filter = new IntentFilter();
filter.addAction(BumpAPIIntents.CHANNEL_CONFIRMED);
filter.addAction(BumpAPIIntents.DATA_RECEIVED);
filter.addAction(BumpAPIIntents.NOT_MATCHED);
filter.addAction(BumpAPIIntents.MATCHED);
filter.addAction(BumpAPIIntents.CONNECTED);
registerReceiver(receiver, filter);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
private final ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder binder) {
Log.i("BumpTest", "onServiceConnected");
api = IBumpAPI.Stub.asInterface(binder);
try {
api.configure("API_KEY", "Bump User");
} catch (RemoteException e) {
Log.w("BumpTest", e);
}
Log.d("Bump Test", "Service connected");
}
#Override
public void onServiceDisconnected(ComponentName className) {
Log.d("Bump Test", "Service disconnected");
}
};
}
Sound like the problem occur during the connection service on the api.configure....
Should I run it in a separate thread or in it's own AsynchTask, but then how and why ?
I stuck on this problem for a day or so... and literarily 2 minutes after posting it here I resolved it...
I just put the api.configure on a separate thread (shorter than a AsynchTask).
private final ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder binder) {
Log.i("BumpTest", "onServiceConnected");
api = IBumpAPI.Stub.asInterface(binder);
new Thread() {
public void run() {
try {
api.configure("API_KEY",
"Bump User");
} catch (RemoteException e) {
Log.w("BumpTest", e);
}
}
}.start();
Log.d("Bump Test", "Service connected");
}
#Override
public void onServiceDisconnected(ComponentName className) {
Log.d("Bump Test", "Service disconnected");
}
};
Make request in background process.
The network on main thread one exception is happening in 2.2 and on 3.0 and above, the difference is that on 3.0 and above they force you to put everything that involves some heavy or slow ops in a different thread, as you said in an asyncTask.
You just have to create an inner asyncTask and on its onBackground method put your api.configure :)
class LoadBumpAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
api.configure("9b17d663752843a1bfa4cc72d309339e", "Bump User");
} catch (RemoteException e) {
Log.w("BumpTest", e);
}
return null;
}
}
Just call new LoadBumpAsyncTask().execute() on your service connected and will work.

Categories

Resources