service got stopped when the app goes background - android

Hi in project I'm using service for chat communication using SignalR. Chat communication is working fine but when the app goes to background the service got stopped I need to run the services fully till my app get deleted
Here is me service code
public class SignalRService extends Service {
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler; // to display Toast message
private final IBinder mBinder = new LocalBinder(); // Binder given to clients
public SignalRService() {
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public void onDestroy() {
Log.i("onDestroy","onDestroy");
mHubConnection.stop();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Return the communication channel to the service.
startSignalR();
return mBinder;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public SignalRService getService() {
// Return this instance of SignalRService so clients can call public methods
return SignalRService.this;
}
}
/**
* method for clients (activities)
*/
public void sendMessage(String message) {
String SERVER_METHOD_SEND = "Send";
mHubProxy.invoke(SERVER_METHOD_SEND, message);
}
/**
* method for clients (activities)
*/
public void sendMessage_To(String receiverName, String message) {
String SERVER_METHOD_SEND_TO = "SendChatMessage";
mHubProxy.invoke(SERVER_METHOD_SEND_TO, receiverName, message);
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
Credentials credentials = new Credentials() {
#Override
public void prepareRequest(Request request) {
request.addHeader("User-Name", "BNK");
}
};
String serverUrl = "http://10.10.10.180/signalr/hubs";
mHubConnection = new HubConnection(serverUrl);
mHubConnection.setCredentials(credentials);
String SERVER_HUB_CHAT = "ChatHub";
mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
Log.e("SimpleSignalR", e.toString());
return;
}
sendMessage("Hello from BNK!");
String CLIENT_METHOD_BROADAST_MESSAGE = "broadcastMessage";
mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
new SubscriptionHandler1<CustomMessage>() {
#Override
public void run(final CustomMessage msg) {
final String finalMsg = msg.UserName + " says " + msg.Message;
// display Toast message
mHandler.post(new Runnable() {
#Override
public void run() {
Log.i("message","message: "+finalMsg);
Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
}
});
}
}
, CustomMessage.class);
}}
And here is the activity code
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private SignalRService mService;
private boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setClass(mContext, SignalRService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
// Unbind from the service
Log.i("onStop","onStop");
if (mBound) {
unbindService(mConnection);
mBound = false;
}
super.onStop();
}
public void sendMessage(View view) {
if (mBound) {
// Call a method from the SignalRService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
EditText editText = (EditText) findViewById(R.id.edit_message);
EditText editText_Receiver = (EditText) findViewById(R.id.edit_receiver);
if (editText != null && editText.getText().length() > 0) {
String receiver = editText_Receiver.getText().toString();
String message = editText.getText().toString();
mService.sendMessage_To(receiver, message);
mService.sendMessage(message);
}
}
}
/**
* Defines callbacks for service binding, passed to bindService()
*/
private final ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to SignalRService, cast the IBinder and get SignalRService instance
SignalRService.LocalBinder binder = (SignalRService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.i("onServiceDisconnected","onServiceDisconnected");
mBound = false;
}
};}
My manifest code for service
<service
android:name=".SignalRService"
android:enabled="true"
android:exported="true" >
</service>
Plese help me on this

If you bind the service with any component the system will automatically destroy the service if no other client is bound with it.
If you want to run a service independently then you have to start a service rather than bind. But you can't communicate with a service if you start it with startService()
For more details you can see the documentation here

You can BOTH start AND bind the service.
In this way, even if multiple components bind to the service at once, then ALL of them unbind, the service will NOT be destroyed. Refer to A service can essentially take two forms: Bound
your service can work both ways: it can be started (to run indefinitely) and also allow binding. It's simply a matter of whether you implement a couple callback methods: onStartCommand() to allow components to start it and onBind() to allow binding.
// onBind method just return the IBinder, to allow clients to get service.
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// onStartCommand just return START_STICKY to let system to
// try to re-create the service if the servcie's process is killed.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
// and make startSignalR public to allow client to call this method.
public void startSignalR() {
}
In your clients, no need to keep a boolean mBound.
Just bind service when onCreate, unbind service when onDestroy. DO NOT unbind when onStop. Since onStop may called many times, for example dialog popup will invoke onStop, but your activity is still on foreground, this will cause your service destroyed.
Refer to my answer for question: Pass Service From one Activity to Another for sample code.

Related

How to keep polling a bound service?

I looked up on the internet, but couldn't find an example covering my scenario. What I am trying to do is:
1) To start and bind to a service as soon as my activity starts (done)
2) The service then binds itself to another service looking for a user
input from a connected device, and saves a string a string to a variable (done)
3) I would like to send back this string to the activity, so I can check what it
is and based on it to make a network call.
Now number 3) is my challenge. I managed to do it with a Timer that runs for one second and then checks the value written in the service, but somehow this doesn't seem to be the right way and I think that there might be a more mature solution. However, I can't seem to figure it out.
I've taken the code from the documentation and only added the timer. It is just one service in this example that just generates a random number (this will normally be replaced by my second service).
This is the code for the service:
public class LocalService extends Service {
private final IBinder mBinder = new LocalBinder();
private final Random mGenerator = new Random();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
And this is the code in my activity:
public class MainActivity extends AppCompatActivity {
LocalService mService;
boolean mBound = false;
Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = new Timer();
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
timer.schedule(new MyTimerTask(new Handler(), this), 1000, 1000); // run on every second
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
timer.cancel();
timer.purge();
}
private class MyTimerTask extends TimerTask {
Handler handler;
MainActivity ref;
public MyTimerTask(Handler handler, MainActivity ref) {
super();
this.handler = handler;
this.ref = ref;
}
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
if (mBound) {
int num = ref.mService.getRandomNumber();
// just as an example, raise a toast to see if it works
// but otherwise the value will be handled
Toast.makeText(ref, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
});
}
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
My question is: is this a good approach (it works) or is it bad and what's the alternative?
You can use LocalBroadcastManager to send broadcasts from your Service to your Activity. For example, in your Service declare:
public static final String BROADCAST_INTENT = "broadcast_intent";
public static final String BROADCAST_VALUE = "broadcast_value";
private LocalBroadcastManager broadcastManager;
public void onCreate() {
super.onCreate();
broadcastManager = LocalBroadcastManager.getInstance(this);
}
Now whenever you want to send a String to your Activity you can do so like this:
private void sendBroadcast(String value) {
Intent intent = new Intent(BROADCAST_INTENT);
intent.putExtra(BROADCAST_VALUE, value);
broadcastManager.sendBroadcast(intent);
}
In your Activity declare a BroadcastReceiver:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
handleIntent(intent);
}
};
Register the receiver when you bind to your Service:
IntentFilter broadcastIntentFilter = new IntentFilter();
broadcastIntentFilter.addAction(StreamService.BROADCAST_INTENT);
LocalBroadcastManager.getInstance(context).registerReceiver((broadcastReceiver), broadcastIntentFilter);
And unregister where you unbind from your Service:
LocalBroadcastManager.getInstance(context).unregisterReceiver(broadcastReceiver);
Now when your service sends the broadcast you can handle it in your Activity:
private void handleIntent(Intent intent) {
if (intent.getAction().equals(StreamService.BROADCAST_INTENT)) {
String value = intent.getStringExtra(StreamService.BROADCAST_VALUE, "default");
}
}
I would like to send back this string to the activity, so I can check what it is and based on it to make a network call.
Use LocalBroadcastManager, greenrobot's EventBus, Square's Otto, or some other in-process event bus implementation. Raise an event when you have changed data. Have the activity register with the bus to find out about the event. Have the activity use the changed data when the change occurs.
is this a good approach
No.

My background music service stops when I load a new activity. Am I connecting or binding wrong?

I'm trying to add background music to my game and I thought I could persist it across activities by starting the service and then just connecting and binding to it in different activities in order to control it. But my music stops playing when I try to establish a connection from my second activity. I'm very new to working with services, so I apologize for any simple mistakes.
From my MusicService class (extends Service)
private final IBinder mBinder = new ServiceBinder();
MediaPlayer mPlayer;
private int length = 0;
public static boolean STOP_ON_DESTROY = true;
// Binder subclass. Allows access from clients to the server
public class ServiceBinder extends Binder {
MusicService getService(){
return MusicService.this;
}
}
public MusicService() {}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onCreate(){
super.onCreate();
}
#Override
public int onStartCommand (Intent intent, int flags, int startId)
{
return START_STICKY;
}
public boolean isPlaying(){
if(mPlayer != null){
return mPlayer.isPlaying();
}
return false;
}
public void stopMusic()
{
if(mPlayer != null){
if(mPlayer.isPlaying()) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
System.out.println("stopMusic service fn");
}
}
This is the code I call in both my Main and secondary activities in order to interact with the service. The music stops during the connectToMusicService function in the secondary activity. The Main activity works great.
onCreate(){....
startMusicService();
MusicService.STOP_ON_DESTROY = true;
}
private void startMusicService() {
Intent musicIntent = new Intent();
musicIntent.setClass(getApplicationContext(), MusicService.class);
startService(musicIntent);
}
#Override
public void onStart(){
super.onStart();
// establish connection for binding to the service
connectToMusicService();
// bind to the service
bindToMusicService();
}
private void bindToMusicService() {
Intent intent = new Intent(this, MusicService.class);
bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
MusicService.STOP_ON_DESTROY = true;
}
private void connectToMusicService() {
myServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.ServiceBinder binder = (MusicService.ServiceBinder) service;
mService = binder.getService();
if(!mService.isPlaying())
mService.startMusic();
isServiceBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("Service disconnected from main");
isServiceBound = false;
}
};
}
The only thing I do during my Main activity's onStop is
#Override
public void onStop(){
super.onStop();
if(mService != null) {
if (MusicService.STOP_ON_DESTROY) {
mService.stopMusic();
}
}
}
UPDATE: I got it working. My issue wasn't with service binding at all. It was with static STOP_ON_DESTROY variable I was using to manage whether the music should stop when leaving an activity. I cleaned that up and all is good now. Thanks!
First of all, do you need to bind at all? Or could starting the service be enough? Started services run until you stop them (except if killed by the system when resources are scarce). I am not sure there's any point binding from each of your activities.
Btw if your service should run and play music also when your activities are closed, consider making it a foreground service.

Using Timer within an Android Service

I have a service that is going to act as an internal clock as part of another app, and it contains a Timer which runs every one second to update the time.
Problem is, when I close and re-open the app, the Service binds itself and all the code runs again in the service and then there are two Timers per second, and so on.
I've tried following the Local Service example, but the same thing happens http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
Is it something to do with the bind/unbind of the service?
I'd like for just one process to run, so I'm looking for a way really to see if the process is running already, and if not then start it.
LocalService
`public class LocalService extends Service {
// This is the object that receives interactions from clients.
private final IBinder mBinder = new LocalBinder();
private long dateOfLastSync;
private long timeNow;
private final Handler handler = new Handler();
private static Timer timer = new Timer();
private Context ctx;
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
// The LocalBinder provides the getService() method for clients to retrieve the current instance of LocalService.
LocalService getService() {
Log.d("INTERNALCLOCK", "getService");
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public void onCreate() {
Log.d("INTERNALCLOCK", "onCreate");
ctx = this;
startService();
}
#Override
public void onDestroy() {
Log.d("INTERNALCLOCK", "onDestroy");
Toast.makeText(this, "Service Stopped ...", Toast.LENGTH_SHORT).show();
}
private void startService()
{
timer.scheduleAtFixedRate(new mainTask(), 0, 1000);
}
private class mainTask extends TimerTask
{
public void run()
{
toastHandler.sendEmptyMessage(0);
}
}
public void getTimeFromServer() {
FetchServerTime fetchTime = new FetchServerTime();
fetchTime.execute();
}
#Override
public IBinder onBind(Intent intent) {
Log.d("INTERNALCLOCK", "onBind");
getTimeFromServer();
return mBinder;
}
/** method for clients */
public long getTimeNow() {
Log.d("INTERNALCLOCK", "getTimeNow");
return timeNow;
}
public void startTimeCounter() {
Log.d("INTERNALCLOCK", "startTimeCounter");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("INTERNALCLOCK", "onStartCommand");
return START_STICKY;
}
private final Handler toastHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
timeNow += 1000;
Log.d("INTERNALCLOCK", "time is " + timeNow);
Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_SHORT).show();
}
};
class FetchServerTime extends AsyncTask<Integer, String, Long> {
private String mUrl;
private Date date;
private AlarmManager mAlarmManager;
public FetchServerTime() {
mUrl = "XXX";
}
#Override
protected Long doInBackground(Integer... params) {
(get server time code)
}
#Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
Log.d("INTERNALCLOCK", "Server time on post execute: " + result);
timeNow = result;
dateOfLastSync = result;
}
}
`
InternalClockActivity
public class InternalClockActivity extends Activity implements CanUpdateTime {
private TextView timeLabel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d("INTERNALCLOCK", "onCreate - main");
timeLabel = (TextView) findViewById(R.id.time);
doBindService();
}
private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
}
};
private boolean mIsBound;
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(InternalClockActivity.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
public void updateTime(String time){
timeLabel.setText(time);
}
}
I just created some static variables to see if the timer was running, i.e.
private static boolean isDownloadTaskRunning = false;
private void startService()
{
if (!isDownloadTaskRunning) {
// Timers are not running already
// start timer to download date at intervals
timerToDownloadNewData.scheduleAtFixedRate(new DownloadDateTask(), 0, TIMES.FIFTEEN_MINS.getSeconds());
// set running to true
isDownloadTaskRunning = true;
}
}

ServiceConnection.onServiceConnected() never called after binding to started service

In a game application I have the following scenario:
From the main game Activity, the player starts several game tasks that run in the background with varying duration.
The player should be able to view the progress of the running game tasks in a separate View.
To do this, I created two Activitys and a Service, defined as follows:
Service ProgressService handles several ProgressBars running simultaneously on parallel threads.
Activity WorkScreen2 creates a game task, starts the Service with startService() with task parameters passed in a Bundle.
Activity ProgressScreen binds to the Service to get and display the ProgressBars of the running tasks.
Both activities run under separate TabHosts of one TabActivity.
The problem I'm having is that the ServiceConnection.onServiceConnected() method is never called. I get a Java.lang.NullPointerException because I try to call a method of the Service object that should be assigned in this method. See code below.
I use getApplicationContext().bindService() to bind the Activity to the Service because TabSpec cannot bind to Services. This method returns true. Therefore, binding is successful.
Here is the Service:
public class ProgressService extends Service implements GameConstants {
public static final String BROADCAST_PROGRESS = "com.mycompany.android.mygame.progressbroadcast";
private static final long UPDATE_INTERVAL = 500;
private IBinder mBinder;
private List<ProgressBar> mProgressBarList;
private List<String> mStaffNameList;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
ProgressBar progressBar = new ProgressBar(ProgressService.this);
mProgressBarList.add(progressBar);
Bundle bundle = msg.getData();
String staffName = bundle.getString(WorkScreen2.STAFF_NAME);
mStaffNameList.add(staffName);
int taskDurationMillis = bundle.getInt(WorkScreen2.TASK_DURATION) * 1000;
progressBar.setMax(taskDurationMillis / 1000);
long startTimeMillis = SystemClock.uptimeMillis();
long elapsedTimeMillis = SystemClock.uptimeMillis()
- startTimeMillis;
Intent intent = new Intent();
intent.setAction(BROADCAST_PROGRESS);
while (elapsedTimeMillis < taskDurationMillis) {
try {
Thread.sleep(UPDATE_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
elapsedTimeMillis = SystemClock.uptimeMillis()
- startTimeMillis;
int elapsedTimeSeconds = (int) elapsedTimeMillis / 1000;
progressBar.setProgress(elapsedTimeSeconds);
sendBroadcast(intent);
}
progressBar.setVisibility(View.GONE);
mProgressBarList.remove(progressBar);
mStaffNameList.remove(staffName);
sendBroadcast(intent);
if (mProgressBarList.isEmpty()) {
stopSelf(msg.arg1);
}
}
}
#Override
public void onCreate() {
super.onCreate();
mBinder = new ProgressServiceBinder();
mProgressBarList = Collections
.synchronizedList(new ArrayList<ProgressBar>());
mStaffNameList = Collections.synchronizedList(new ArrayList<String>());
}
/*
* Creates a thread for each game task with parameters passed in
* <code>intent</code>
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "starting service", Toast.LENGTH_LONG).show();
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
Handler serviceHandler = new ServiceHandler(thread.getLooper());
Message msg = serviceHandler.obtainMessage();
msg.arg1 = startId;
msg.setData(intent.getExtras());
serviceHandler.sendMessage(msg);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class ProgressServiceBinder extends Binder {
ProgressService getService() {
return ProgressService.this;
}
}
public List<ProgressBar> getProgressBarList() {
return mProgressBarList;
}
public List<String> getStaffNameList() {
return mStaffNameList;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service done", Toast.LENGTH_SHORT).show();
}
}
And this is the Activity that binds to it:
public class ProgressScreen extends ListActivity {
private final String TAG = "ProgressScreen";
private ProgressScreenAdapter mAdapter;
private ProgressService mProgressService;
private List<ProgressBar> mProgressBarList;
private List<String> mStaffNameList;
#Override
public void onCreate(Bundle bundle) {
Log.i(TAG, "ProgressScreen oncreate");
super.onCreate(bundle);
setContentView(R.layout.progress_screen_layout);
IntentFilter filter = new IntentFilter();
filter.addAction(ProgressService.BROADCAST_PROGRESS);
registerReceiver(receiver, filter);
doBindService();
mAdapter = new ProgressScreenAdapter(this, mStaffNameList, mProgressBarList);
setListAdapter(mAdapter); // Returns true
/*
* This is where I get the NullPointerException
* mProgressService is null here
*/
mProgressBarList = mProgressService.getProgressBarList();
mStaffNameList = mProgressService.getStaffNameList();
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(ProgressService.BROADCAST_PROGRESS);
registerReceiver(receiver, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
boolean doBindService() {
return getApplicationContext().bindService(new Intent(this, ProgressService.class), mConnection, Context.BIND_AUTO_CREATE);
}
void doUnbindService() {
getApplicationContext().unbindService(mConnection);
}
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
mProgressService = ((ProgressService.ProgressServiceBinder) binder).getService();
Toast.makeText(ProgressScreen.this, "Connected to ProgressService", Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName name) {
mProgressService = null;
}
};
private BroadcastReceiver receiver = new BroadcastReceiver () {
#Override
public void onReceive(Context context, Intent intent) {
mAdapter.notifyDataSetChanged();
}
};
}
And the Service is started from the main Activity as follows:
Intent intent = new Intent(WorkScreen2.this, ProgressService.class);
intent.putExtra(TASK_DURATION, task.getDuration());
intent.putExtra(STAFF_NAME, staff.getName());
startService(intent);
The AndroidManifest.xml contains
<service
android:name=".ProgressService"
android:label="#string/progress_service">
</service>
ServiceConnection's onServiceConnected() is called, but nobody guarantees that it will be called before onCreate continues execution. So, what happens here - you successfuly bind to the service (that's why onBind returns true), but you're not fully connected - onServiceConnected() has not yet been called, so your local mProgressService object is not yet initalized, and therefore you get the NullPointerException.
Solution:
Move these two lines:
mProgressBarList = mProgressService.getProgressBarList();
mStaffNameList = mProgressService.getStaffNameList();
from onCreate() to onServiceConnected() function (use the service object after it is initialized in onServiceConnected()).
Check AndroidManifest.xml of yours and add service that you tried to bind.
You have to return your Binder inner class from
private final IBinder mBinder = new ServiceBinder();
public class ServiceBinder extends Binder {
public PlayerActivity getService() {
return PlayerActivity.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}

Sticky Service Management

I've got a Sticky Service (returns START_STICKY from onStartCommand) which executes some code in an AsyncTask, but I'm having some problems with how and when to start, bind, stop, unbind. I only want the service around whilst the parent activity is alive, I don't want it hanging around in the background when the app has been closed, but I need the service to survive an orientation change. I currently don't need the service to be active for the entire duration of the activity being active, so I call stopSelf() after the main work is done in my AsyncTask in the Service and then start the Service again when needed. Sometimes I'll need to interrupt the work the service is doing, cancel the AsyncTask and start again with different data. The problem is that no matter what I do - I can't seem to get it solid throughout all the different possible scenarios. Can anyone have a look through and tell me what I'm doing wrong?
My Service is :
public class ChordCalculatorService extends Service {
private final IBinder mBinder = new LocalBinder();
private AsyncTask<SearchData, SearchStatusData, List<Item>> currentTask;
#Override
public void onCreate() {}
#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;
}
/**
* Class for clients to access. Because we know this service always runs in
* the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public ChordCalculatorService getService() {
return ChordCalculatorService.this;
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public SearchData getSearchData() {
return searchData;
}
public void startWork() {
if (currentTask != null && currentTask.getStatus() == Status.RUNNING) {
currentTask.cancel(true);
}
if(searchData != null) {
Worker task = new Worker();
currentTask = task.execute(new SearchData[] { searchData });
} else {
Message msg = handler.obtainMessage(ERROR, "No search data set");
handler.sendMessage(msg);
}
}
class Worker extends AsyncTask<SearchData, SearchStatusData, List<Item>> {
// ... code ...
#Override
protected void onPostExecute(List<Item> result) {
Message msg = handler.obtainMessage(COMPLETE, new StatusData(Status.STATUS_FINISHED, result));
handler.sendMessage(msg);
stopSelf();
}
}
}
Currently I have the Service being started when my custom View is created:
public class MyCustomView extends BasicFretBoardView {
private ServiceConnection conn;
private MyService myService;
private boolean isServiceStarted;
private boolean isServiceBound;
public MyCustomView(Context context, AttributeSet attr) {
super(context, attr);
startService();
}
public void startService() {
Intent serviceIntent = new Intent(getContext(), MyService.class);
conn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = ((LocalBinder) service).getService();
myService.registerHandler(serviceHandler);
}
#Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
// Explicitly start the service. Don't use BIND_AUTO_CREATE, since it
// causes an implicit service stop when the last binder is removed.
getContext().startService(serviceIntent);
getContext().bindService(serviceIntent, conn, 0);
isServiceStarted = true;
isServiceBound = true;
}
public void stopService() {
if (isServiceStarted) {
Intent serviceIntent = new Intent(getContext(), MyService.class);
getContext().stopService(serviceIntent);
isServiceStarted = false;
}
unBindService();
}
public void unBindService() {
if(isServiceBound) {
getContext().unbindService(conn);
isServiceBound = false;
}
}
// gets called based on some user interaction
private void startServiceWork() {
if(!isServiceStarted) {
startService();
} else {
myService.cancelCalcalation();
}
myService.setData(data);
myService.startWork();
}
}
and stopping the service is handled in the Activity:
public class CustomChordActivity extends Activity {
// ... code ...
#Override
public void onBackPressed() {
super.onBackPressed();
}
#Override
protected void onPause() {
if(isFinishing()) {
chordsView.stopService();
}
super.onPause();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
protected void onDestroy() {
chordsView.unBindService();
super.onDestroy();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
}
}
It seems that you want your task to run on demand, maybe an IntentService would be a more suitable option. When you need work to be done, (startServiceWork()), you just start the service and that kicks off your AsyncTask. The service will then finish after the task has finished.
Now, regarding orientation changes, you would have to implement a Broadcast Receiver whose intent filter is "android.intent.action.CONFIGURATION_CHANGED". (I assume that you want the service to do work when the orientation changes) Place the Broadcast Receiver, within your activity/main ui thread. This will in effect make the hosting process of your Broadcast Receiver to be the main application process making it safer to start the service from within the Broadcast Receiver.

Categories

Resources