Reputation: 15592
I have a bound Service. An activity is binding it. It unbinds the Service on Activity's onStop()
method.
The problem is, if runtime changes (for example, orientation change) happen to the Activity, then the Activity is recreated. So, onStop()
method is called from the Activity, and the Activity unbinds the Service in that method, which results in destruction of the Service (and restarting of it).
I want to preserve the Service from being destroyed in the runtime changes while keeping Service stopping when the Activity is invisible. You could say that try startService()
but it makes the Service not to stop when Activity is invisible. If I add stopService Activity's onStop()
, then the result is the same as bindService()
and unbindService()
.
PostDelaying unbindService()
in Acitivity's onStop()
can solve this problem partly, but the delaying time will be arbitrary, and this prevents the Activity from getting GC for some time. I want a more clear solution.
I don't want solutions like android:configChanges="orientation"
since there are other runtime changes as well, and it is a discouraged way of processing runtime changes.
In short, I want the Service to act like a Fragment that called setRetainInstance(true)
. However, Fragments don't have something like bindService()
. What should I do?
Upvotes: 9
Views: 3231
Reputation: 312
Another way to keep alive a only-bound service during runtime changes could be to define in the service class a method like keepAlive
:
public void keepAlive(boolean value) {
if (value) startService(new Intent(getApplicationContext(), getClass()));
else stopSelf();
}
@Override
public void onRebind(Intent intent) {
keepAlive(false);
}
@Override
public boolean onUnbind(Intent intent) {
return true;
}
In onRebind()
the only-bound service status is resetted.
Upvotes: 0
Reputation: 18151
Call startService in onCreate and then onStop
@Override
protected void onStop()
{
super.onStop();
if (!isChangingConfigurations ())
{
// call stopService
}
}
Upvotes: 1
Reputation: 1007494
However, Fragments don't have something like bindService().
But they can use bindService()
from the Application
context:
public class BshFragment extends Fragment implements OnClickListener,
ServiceConnection {
private IScript service=null;
private Button btn=null;
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View result=inflater.inflate(R.layout.main, container, false);
btn=(Button)result.findViewById(R.id.eval);
btn.setOnClickListener(this);
btn.setEnabled(false);
setRetainInstance(true);
return(result);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().getApplicationContext()
.bindService(new Intent(getActivity(),
BshService.class), this,
Context.BIND_AUTO_CREATE);
}
@Override
public void onDestroy() {
getActivity().getApplicationContext().unbindService(this);
disconnect();
super.onDestroy();
}
@Override
public void onClick(View view) {
EditText script=(EditText)getView().findViewById(R.id.script);
String src=script.getText().toString();
service.executeScript(src);
}
@Override
public void onServiceConnected(ComponentName className, IBinder binder) {
service=(IScript)binder;
btn.setEnabled(true);
}
@Override
public void onServiceDisconnected(ComponentName className) {
disconnect();
}
private void disconnect() {
service=null;
btn.setEnabled(false);
}
}
(as seen in this sample project, covered in this book)
By using the Application
context, we are able to use the same Context
for binding and unbinding. By retaining the fragment, we can avoid unbinding and rebinding on a configuration change.
Personally, I just try to avoid the binding pattern. I am a fan of loosely-coupled interfaces, and so I prefer using services via the command pattern and startService()
.
Upvotes: 9