I am creating a separate thread in a android bounded service's onStartCommand() and running it using a condition check in Runnable's Run() e.g. while (condition), there is a activity which is using this bounded service and then unbound it. I read that there is a rule that bounded services automatically destroyed if there is no activity bound to it. Do I need to flip my condition in unbind method otherwise how service will know that it must terminate now ? If yes, then its controlled by my code not by Android itself
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
public class ServiceDemo extends Service {
private static final String TAG = "ServiceDemo";
private boolean isRunning = false;
private boolean isBound = false;
private int counter = 0;
private static final int MIN = 0;
private static final int MAX = 100;
IBinder myServiceBinder = new MyServiceBinder();
public class MyServiceBinder extends Binder {
public ServiceDemo getServiceInstance(){
return ServiceDemo.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
isBound = true;
return myServiceBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand executed in Thread : "+Thread.currentThread().getId());
isRunning = true;
generateSequentialInteger(MIN, MAX);
return START_NOT_STICKY;
}
private void generateSequentialInteger(int start, int stop){
new Thread(new Runnable() {
#Override
public void run() {
counter = start;
while(isRunning && counter<stop){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "Thread ID : "+Thread.currentThread().getId()+" : Service has generated number "+counter);
counter++;
}
}
}).start();
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind executed");
isBound = false;
return super.onUnbind(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
isRunning = false;
Log.i(TAG, "onDestroy executed");
}
}
'''
Related
I have created a background service in android in java which plays music at the start of activity.
package com.sudarshan.tictactoenew;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.app.Service;
public class BackgroundSoundService extends Service {
private static final String TAG = null;
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.c);
player.setLooping(true); // Set looping
player.setVolume(100,100);
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return startId;
}
public void onStart(Intent intent, int startId) {
// TO DO
}
public IBinder onUnBind(Intent arg0) {
// TO DO Auto-generated method
return null;
}
public void onStop() {
}
public void onPause() {
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
#Override
public void onLowMemory() {
}
}
//calling background service
Intent intent = new Intent(MenuPage.this, BackgroundSoundService.class);
startService(intent);
But this runs forever, I mean till the app is not closed.How do I stop this when I move to the next activity?I am calling background service in menupage activity
In my app, I am using bound services. However, while running the code, I start the service and bind to it(OnBind() method works), but afterwards OnStartService() is never called, and my service is equal to null.
Here is my service code:
public class MyService extends Service{
boolean toExit = false;
boolean isThreatDetectionOn;
boolean isDanger = false;
int counter;
class MyServiceBinder extends Binder {
public MyService getService()
{
return MyService.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.i("Bind", "thats good");
return null;
}
#Override
public void onDestroy() {
stop();
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
isThreatDetectionOn = true;
new Thread(new Runnable() {
#Override
public void run() {
start();
}
}).start();
return START_STICKY;
}
}
start() and stop() are method that run in the service that start generating random numbers and stop generating random numbers, respectively.
Here is the bind/unbind code for the service:
private MyService ListeningService;
private boolean isServiceBound = false;
private ServiceConnection serviceConnection;
bindService = (Button) findViewById(R.id.button7);
bindService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
Log.i("Great","Button Bind Clicked");
if(serviceConnection == null)
{
Log.i("Great","Service Connection is Null");
serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MyService.MyServiceBinder myBinder = (MyService.MyServiceBinder)iBinder;
listeningService = myBinder.getService();
isServiceBound = true;
Log.i("Yes", "Service Connected");
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
isServiceBound = false;
Log.i("No", "Service DisConnected");
}
};
}
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
});
unbindService = (Button) findViewById(R.id.button8);
unbindService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isServiceBound)
{
unbindService(serviceConnection);
isServiceBound = false;
Log.i("No", "Service DisConnected");
}
}
});
Even after onBind() is called(in the logs it says "Bind:thats good") isServiceBound still remains false when it should now become true because the service is now connected.
I am new in android development. My service killed before starting loop..
I started service from another activity like MainActivity
package com.example.ch_m_usman.sharedtasklist.Services;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class ReminderService extends IntentService {
public ReminderService() {
super("Reminder Service");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.e("check","")
//Loop does not start.
for (int i=0;i<5;i++){
Log.e("Inside for","");
}
}
}
//How to use loop in intent service
Use below code:
public class TaskConfirm extends IntentService {
private final IBinder mBinder = new LocalBinder();
public TaskConfirm() {
super("TaskConfirm");
setIntentRedelivery(true);
}
#Override
public void onCreate() {
super.onCreate();
//Initilise your objects here like below...
taskIdList=new ArrayList<String>();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return super.onStartCommand(intent, flags, startId);
}
#Override
protected void onHandleIntent(Intent intent) {
// get intent data here like below if required......
if (intent != null) {
String taskId=intent.getStringExtra("taskId");
}
// apply your loop here .......
for (int i=0;i<5;i++){
Log.e("Inside for","");}
}
public class LocalBinder extends Binder {
public TaskConfirm getService() {
return TaskConfirm.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Start service like below from your activity :
Intent serviceIntent = new Intent(context, TaskConfirm.class);
// put some extra data if required
serviceIntent.putExtra("taskId","1");
startService(serviceIntent);
If you want to access this service in other activity then bind that activity to service:
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, TaskConfirm.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
TaskConfirm.LocalBinder binder = (TaskConfirm.LocalBinder) service;
mService = binder.getService();
mBound = true;
// do your task on service-connected...
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
I had created countdown timer to display timeout it works well when user minimize application but it stops when user close application. i had added code below kindly help me it's an emergency.
this is MainActivity.java
public static final String TAG = "Demo";
TextView t1 ;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
t1 = (TextView)findViewById(R.id.t1);
startService(new Intent(this, count_servie.class));
}
private BroadcastReceiver br = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
updateGUI(intent);
}
};
public void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter(count_servie.COUNTDOWN_BR));
}
private void updateGUI(Intent intent) {
if (intent.getExtras() != null) {
long millisUntilFinished = intent.getLongExtra("countdown", 0);
t1.setText("Countdown seconds remaining: " + millisUntilFinished / 1000);
}
}
this is my count_servie.java
public class count_servie extends Service {
public static final String COUNTDOWN_BR = "com.demo.DSemo.countdown_br";
Intent bi = new Intent(COUNTDOWN_BR);
CountDownTimer cdt = null;
public void onCreate() {
super.onCreate();
cdt = new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
bi.putExtra("countdown", millisUntilFinished);
sendBroadcast(bi);
}
public void onFinish() {
}
};
cdt.start();
}
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
public IBinder onBind(Intent arg0) {
return null;
}
Thanks in advance.
You need to return START_STCKY in your onStartCommand() method for the service to run even when app is closed.
....
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STCKY;
}
....
You can refer thislink for correctly implementing a service.
Alternatively, you can refer this SO question.
Update
Use a Foreground Service to avoid your Service being killed. In order to make your service Foreground, replace your onStartCommand code with this
#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("Timer")
.setContentText("Doing some work...")
.setContentIntent(pendingIntent).build();
startForeground(1337, notification);
cdt = new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
bi.putExtra("countdown", millisUntilFinished);
sendBroadcast(bi);
}
public void onFinish() {
stopForeground(true);
}
};
cdt.start();
return START_STICKY;
}
Udpate 2: Counter using Service and SharedPreferences
Replace your Actvity's code with this:
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.support.v4.os.ResultReceiver;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.util.Calendar;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String SHARED_PREF = "MyPref";
private final static int MAX_COUNTER = 30;
public static final String KEY_COUNTER_SECONDS = "seconds";
public static final String KEY_SAVED_COUNTER = "saved_counter";
public static final String KEY_SAVED_TIME_MILLI = "saved_time_milli";
MyResultReceiver mReceiver;
TextView mTvCounter;
SharedPreferences mSharedPref;
long mMaxCounterValueInSeconds = MAX_COUNTER;
long mCurCounterValue = 0;
boolean mShouldSaveValues;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvCounter = (TextView) findViewById(R.id.tv_counter);
mReceiver = new MyResultReceiver(null);
mSharedPref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
}
#Override
protected void onResume() {
super.onResume();
//register listener
MyService.registerReceiver(mReceiver);
//get values from shared pref
long savedCounter = mSharedPref.getLong(KEY_SAVED_COUNTER, -1);
long savedTime = mSharedPref.getLong(KEY_SAVED_TIME_MILLI, -1);
//if -1 counter was running when app was closed, get saved values from shared pref
if (savedTime != -1) {
//elapsedTime is the time spent in seconds while the app was in background
long elapsedTime = (getCurrentTimeInMilli() - savedTime)/1000; //convert to sec
mCurCounterValue = savedCounter + elapsedTime;
if(mCurCounterValue < MAX_COUNTER){
//calculate current counter value from values retrieved from shared pref
mMaxCounterValueInSeconds = MAX_COUNTER - mCurCounterValue;
//start the value with updated max count value
startService(mMaxCounterValueInSeconds);
}else{
mCurCounterValue = MAX_COUNTER;
}
}else{
//if counter was not running, start the service with max count value = MAX_COUNTER
startService(mMaxCounterValueInSeconds);
}
//update text view
mTvCounter.setText("" + mCurCounterValue);
}
private void startService(long maxCounter){
mShouldSaveValues = true;
Intent intent = new Intent(this, MyService.class);
Bundle bundle = new Bundle();
bundle.putLong(KEY_COUNTER_SECONDS, maxCounter);
intent.putExtras(bundle);
startService(intent);
}
#Override
protected void onPause() {
super.onPause();
//stop the service
stopService(new Intent(this, MyService.class));
//unregister listener
MyService.unregisterReceiver();
if(mShouldSaveValues) {//save the values only when counter has started
//save values in the shared preference
SharedPreferences.Editor editor = mSharedPref.edit();
Log.d(TAG, "saving counter: " + Long.parseLong(mTvCounter.getText().toString()));
editor.putLong(KEY_SAVED_COUNTER, Long.parseLong(mTvCounter.getText().toString()));
editor.putLong(KEY_SAVED_TIME_MILLI, getCurrentTimeInMilli());
editor.apply();
}
}
/**
* This method returns current time in milli seconds
*
* #return time in milliseconds
*/
private long getCurrentTimeInMilli() {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
long timeInMilli = date.getTime();
return timeInMilli;
}
/**
* ResultReceiver is used to get values from MyService.class
* It is registered in onResume() &
* unregistered in onPause()
*/
class MyResultReceiver extends ResultReceiver {
public MyResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
String strMilliFinished = resultData.getString(MyService.KEY_MSG);
updateUI(Long.parseLong(strMilliFinished));
}
private void updateUI(final long milliFinished) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mCurCounterValue++;
mTvCounter.setText("" + mCurCounterValue);
if(milliFinished == 0) {
//resetting counter values
mShouldSaveValues = false;
mMaxCounterValueInSeconds = MAX_COUNTER;
mCurCounterValue = 0;
SharedPreferences.Editor editor = mSharedPref.edit();
editor.putLong(KEY_SAVED_COUNTER, -1);
editor.putLong(KEY_SAVED_TIME_MILLI, -1);
editor.apply();
}
}
});
}
}
}
Replace your Service code with this:
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.os.ResultReceiver;
import android.util.Log;
public class MyService extends Service {
public static final String KEY_MSG = "msg";
CountDownTimer cdt = null;
private static ResultReceiver mReceiver;
public MyService() {
}
public static void registerReceiver(ResultReceiver receiver) {
mReceiver = receiver;
}
public static void unregisterReceiver() {
mReceiver = null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
long maxCounterValueInSeconds = bundle.getLong(MainActivity.KEY_COUNTER_SECONDS);
long maxCounter = maxCounterValueInSeconds * 1000;
cdt = new CountDownTimer(maxCounter, 1000) {
public void onTick(long millisUntilFinished) {
sendMessage(1, "" + millisUntilFinished);
}
public void onFinish() {
sendMessage(1, "" + 0);
stopSelf();
}
};
cdt.start();
return START_STICKY;
}
private void sendMessage(int resultCode, String message) {
if (mReceiver != null) {
Bundle bundle = new Bundle();
bundle.putString(KEY_MSG, message);
mReceiver.send(resultCode, bundle);
}
}
#Override
public void onDestroy() {
super.onDestroy();
cdt.cancel();
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
Note: I am using ResultReceiver instead of BroadcastReciver
I have the following abstract class:
public abstract class AbstractService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This class is extended by two other classes:
public abstract class AbstractMessengerService extends AbstractService {
static final int MSG_REGISTER_CLIENT = 9991;
static final int MSG_UNREGISTER_CLIENT = 9992;
ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.
private class IncomingHandler extends Handler { // Handler of incoming messages from clients.
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
Log.i("MyService", "Client registered: " + msg.replyTo);
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
Log.i("MyService", "Client un-registered: "+msg.replyTo);
mClients.remove(msg.replyTo);
break;
default:
//super.handleMessage(msg);
onReceiveMessage(msg);
}
}
}
#Override
public void onCreate() {
super.onCreate();
onStartService();
Log.i("MyService", "Service Started.");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
#Override
public void onDestroy() {
super.onDestroy();
onStopService();
Log.i("MyService", "Service Stopped.");
}
protected void send(Message msg) {
for (int i=mClients.size()-1; i>=0; i--) {
try {
Log.i("MyService", "Sending message to clients: "+msg);
mClients.get(i).send(msg);
}
catch (RemoteException e) {
// The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
Log.e("MyService", "Client is dead. Removing from list: "+i);
mClients.remove(i);
}
}
}
public abstract void onStartService();
public abstract void onStopService();
public abstract void onReceiveMessage(Message msg);
}
And:
public abstract class AbstractAIDLService extends AbstractService {
private Handler handler;
ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return android.os.Process.myPid();
}
#Override
public void setActivityColor(String color, long startTime) throws RemoteException {
long elpsedTime = (System.nanoTime() - startTime) / (1000 * 1000);
showElpesedTime(String.valueOf(elpsedTime));
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
private void showElpesedTime(final String time)
{
handler.post(new Runnable() {
public void run() {
Toast toast = Toast.makeText(AbstractAIDLService.this, "Time passed reaching the server: "+time+"ms", Toast.LENGTH_SHORT);
toast.show();
}
});
}
#Override
public void onCreate() {
super.onCreate();
onStartService();
Log.i("MyService", "Service Started.");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onDestroy() {
super.onDestroy();
onStopService();
Log.i("MyService", "Service Stopped.");
}
public abstract void onStartService();
public abstract void onStopService();
public abstract void onReceiveMessage(Message msg);
}
Now I created a ServiceManager class which is a factory class that I want to create concrete version of those class and passed them to this manager class. I have a constructor in this ServiceManager:
public class ServiceManager {
private static final String TAG = ServiceManager.class.getSimpleName();
private Class<? extends AbstractService> mServiceClass;
public ServiceManager(Context context, ServiceType type, Class<? extends AbstractService> serviceClass, Handler incomingHandler) {
this.mActivity = context;
this.mIncomingHandler = incomingHandler;
if (type.equals(ServiceType.AIDL)) {
this.mServiceClass = (AbstractAIDLService) serviceClass;
} else if (type.equals(ServiceType.MESSENGER)) {
this.mServiceClass = (AbstractMessengerService) serviceClass;
}
if (isRunning()) {
doBindService();
}
}
But for some reason, those lines: this.mServiceClass = (AbstractAIDLService) serviceClass;, this.mServiceClass = (AbstractMessengerService) serviceClass; give me a casting problem. How can this be done?
I think in your ServiceManager you cannot assign a AbstractAIDLService or AbstractMessengerService to Class<? extends AbstractService> mServiceClass because they are different classes and Class is not the father class of the other two.
I don't know what are you trying to do but this should work:
public class ServiceManager {
private static final String TAG = ServiceManager.class.getSimpleName();
private AbstractService mServiceClass;
public ServiceManager(Context context, ServiceType type, AbstractService serviceClass, Handler incomingHandler) {
this.mActivity = context;
this.mIncomingHandler = incomingHandler;
this.mServiceClass = serviceClass;
if (isRunning()) {
doBindService();
}
}
And save the ServiceType in other variable.