Android Service only runs whilst debugging - java

Background
I am creating a service that syncs a local Realm database (stored on phone) with an online database. The database stores users and measurements.
I initialise the service by calling 'startService(new Intent(this, SyncService.class));' during the splash activity's onCreate() method, and specify in the manifest that the service should run for the length of the application.
The service has a broadcast receiver. When the receiver detects a network change from 'not connected' to 'connected', it fires a method called syncDatabases().
This method finds all measurements recorded locally after the timestamp of the last API callback, and sends them to the database. The API responds to a request by returning the object + a unique ID.
When a measurement is made whilst the device is offline, it is stored locally. When an internet connection is made, the syncDatabases() method should be called in order to update the online server with the local measurements.
My steps...
Steps when debugging the project:
With wifi I open the app and with an external device make a new measurement. This appears on both the app and in the database. I then turn wifi off and make another measurement - this appears on the device.
I attach the debugger.
I turn back on wifi and this triggers the services' receivers' onReceive() method. I step through this and it all works according to plan. I reach the syncDatabases() method, and from there I receive the callback from the API, and it then updates the Realm database with the new ID value.
The problem...
If I don't attach the debugger, nothing happens. The new measurements aren't pushed to the database, and none of my Log.e calls are printed.
Why is this happening? And is there an alternative solution / fix for this problem?
Code
Service class
public class SyncService extends Service {
private static final String TAG = "SYNCSERVICE";
private boolean mConnected = false;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null) {
final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
if (netInfo != null) {
switch (netInfo.getState()) {
case CONNECTED:
if (!mConnected) {
Log.e("NETWORK", "Network " + netInfo.getTypeName() + " now connected");
syncDatabases();
mConnected = true;
}
break;
default:
mConnected = false;
break;
}
} else mConnected = false;
}
}
};
#Override
public void onCreate() {
super.onCreate();
initReceiver();
ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager.getActiveNetworkInfo() != null) {
mConnected = true;
}
android.util.Log.e(TAG, "onCreate: SyncService created");
}
#Override
public void onDestroy() {
super.onDestroy();
unInitReceiver();
android.util.Log.e(TAG, "onDestroy: SyncService destroyed");
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
android.util.Log.e(TAG, "onBind: SyncService bound");
return null;
}
#Override
public boolean onUnbind(Intent intent) {
android.util.Log.e(TAG, "onUnbind: SyncService unbound");
return super.onUnbind(intent);
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
android.util.Log.e(TAG, "onRebind: SyncService rebound");
}
private void initReceiver() {
IntentFilter filters = new IntentFilter();
filters.addAction("android.net.wifi.WIFI_STATE_CHANGED");
filters.addAction("android.net.wifi.STATE_CHANGE");
registerReceiver(mReceiver, filters);
}
private void unInitReceiver() {
unregisterReceiver(mReceiver);
}
public void syncDatabases() {
RealmResults<UserDB> users = RealmDB.getInstance(getApplicationContext()).where(UserDB.class).findAll();
if (users.size() > 0) {
int userId = users.get(0).getmUserID();
Log.e("MESSAGE", PreferenceUtils.getInstance().getLastSyncDate());
Date lastSync = null;
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.getDefault());
try {
lastSync = sdf.parse(PreferenceUtils.getInstance().getLastSyncDate());
}
catch (ParseException e) {
e.printStackTrace();
try {
lastSync = BaseFragment.FORMAT.parse(PreferenceUtils.getInstance().getLastSyncDate());
}
catch (ParseException e1) {
e1.printStackTrace();
}
}
if (lastSync != null) {
Date lastSyncOffset = new Date(lastSync.getTime() + 1000);
/** Get all local results which have been made after the last sync date
 **/
RealmResults<MeasurementDB> newLocalMeasurements = RealmDB.getInstance(getApplicationContext())
.where(MeasurementDB.class).equalTo("user_ID", userId)
.greaterThan("dateCreated", lastSyncOffset)
.findAll();
/** For each measurement made after the last sync, add it to the server
 **/
for (MeasurementDB measurement : newLocalMeasurements) {
TemperatureListener mListener = new TemperatureListener(measurement);
ApiRequest.getInstance(getApplicationContext()).registerNewMeasurement(measurement.getAverage(),
measurement.getDateCreated().toString(), mListener, mListener);
}
}
}
}
/**
* Temperature listener receives the local copy of the temperature item. onResponse can then
* directly mutate the object instead of searching local db
*/
private class TemperatureListener implements Response.Listener<Measurement>, Response.ErrorListener {
private MeasurementDB measurement;
public TemperatureListener(MeasurementDB measurement) {
this.measurement = measurement;
}
#Override
public void onErrorResponse(VolleyError error) {
Log.e("OnResponse", "Failure");
}
#Override
public void onResponse(Measurement response) {
Log.e("OnResponse", "Success");
/** Update our local measurement's ID value (supplied by server)
 **/
RealmDB.getInstance(getApplicationContext()).beginTransaction();
measurement.setMeasurement_ID(response.getmMeasurementId());
RealmDB.getInstance(getApplicationContext()).commitTransaction();
/** Update the last sync date
 **/
PreferenceUtils.getInstance().setLastSyncDate(response.getmDateCreated());
}
}
}
Initialisation of Service in splash activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mTimedOut = true;
finishActivity();
}
}, DURATION);
/** Will sync application / cloud databases in background of app when network connected. **/
startService(new Intent(this, SyncService.class));
doApiWork();
}
Manifest entry
Stop with task to kill the service at the same time as the app.
Exported 'false' stops other apps from using the service.
<service
android:name=".network.SyncService"
android:stopWithTask="true"
android:enabled="true"
android:exported="false"/>
EDIT
I removed the service and left a receiver class, registered in the manifest, which triggers methods on another class when needed. However the receiver is only triggered in debug mode.

Related

Keep Streaming service running over MediaBrowserService when Doze Mode

I am working on a Android Streaming App using MediaBrowserServiceCompat based on this great article https://medium.com/androiddevelopers/mediabrowserservicecompat-and-the-modern-media-playback-app-7959a5196d90. My app works pretty good except when phone goes to Doze Mode. I have been looking over internet and see some possible solutions to keep my service alive. One of those is running the UI and the Music Service in different Process. However since I am using the MediaBrowserService I don't know how to start the service from UI using AIDL.
This is how I connected the service with the Main Activity in the same process:
MainActivity.java
private class MediaBrowserConnection extends MediaBrowserHelper {
private MediaBrowserConnection(Context context) {
super(context, MusicService.class);
}
#Override
protected void onConnected(#NonNull MediaControllerCompat mediaController) {
mMediaController = mediaController;
}
#Override
protected void onChildrenLoaded(#NonNull String parentId,
#NonNull List<MediaBrowserCompat.MediaItem> children) {
super.onChildrenLoaded(parentId, children);
}
}
MediaBrowserHelper.java
public MediaBrowserHelper(Context context,
Class<? extends MediaBrowserServiceCompat> serviceClass) {
mContext = context;
mMediaBrowserServiceClass = serviceClass;
mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
mMediaControllerCallback = new MediaControllerCallback();
mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
}
public void onStart() {
if (mMediaBrowser == null) {
mMediaBrowser =
new MediaBrowserCompat(
mContext,
new ComponentName(mContext, mMediaBrowserServiceClass),
mMediaBrowserConnectionCallback,
null);
mMediaBrowser.connect();
}
Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
}
public void onStop() {
if (mMediaController != null) {
mMediaController.unregisterCallback(mMediaControllerCallback);
mMediaController = null;
}
if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
mMediaBrowser.disconnect();
mMediaBrowser = null;
}
resetState();
Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
}
Anyone knows how to do this or is there any other reliable solution to keep streaming alive?
Thanks

Disable VPN upon application exit

So I'm working for a company that requires a VPN in order to connect to the database server. I'm facing an issue with disconnecting programmatically from the VPN service (or disabling it) upon onPause / onStop.
In order to ensure the user is indeed connected to a VPN, I'm using a network listener and if the user is not connected, a dialog is being shown and navigates the user to the VPN Settings Configuration. Once the user connects and resumes the application, the listener recognizes the VPN IP and everything runs great.
My issue is that I want to disable the VPN connection once the user has stopped using the application. Therefore, I've been trying to search for a solution that disables the VPN connection without requesting the user to go to the VPN Settings again. Is there an option to toggle the VPN off programmatically without navigating to the VPN Settings page?
Network Service:
public class NetworkSchedulerService extends JobService implements
ConnectivityReceiver.ConnectivityReceiverListener {
private ConnectivityReceiver mConnectivityReceiver;
#Override
public void onCreate() {
super.onCreate();
mConnectivityReceiver = new ConnectivityReceiver(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Override
public boolean onStartJob(JobParameters params) {
registerReceiver(mConnectivityReceiver, new IntentFilter(Constants.CONNECTIVITY_ACTION));
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
unregisterReceiver(mConnectivityReceiver);
return true;
}
#Override
public void onNetworkConnectionChanged(boolean isConnected) {
String message = isConnected ? "מחובר לרשת" : "אין חיבור פעיל לרשת";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
Listener:
private void scheduleJob() {
JobInfo myJob = new JobInfo.Builder(0, new ComponentName(this, NetworkSchedulerService.class))
.setRequiresCharging(true)
.setMinimumLatency(1000)
.setOverrideDeadline(2000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.build();
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(myJob);
}
#Override
protected void onStop() {
stopService(new Intent(this, NetworkSchedulerService.class));
super.onStop();
}
#Override
protected void onStart() {
super.onStart();
Intent startServiceIntent = new Intent(this, NetworkSchedulerService.class);
startService(startServiceIntent); // INTERNET LISTENER
}
Dialog:
public void dialogVPN() {
builder = new AlertDialog.Builder(LoadingSplash.this);
builder.setMessage("Please ensure VPN Connection");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent("android.net.vpn.SETTINGS");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, 10);
}
});
builder.show();
}
And the returnConnType:
public String returnConnType() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(this.CONNECTIVITY_SERVICE);
String result = "None";
if (connectivityManager != null) {
Network network = connectivityManager.getActiveNetwork();
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
if (capabilities == null) {
result = "None";
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
result = "WIFI";
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
result = "MOBILE";
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
result = "VPN";
}
}
return result;
}
Any suggestions as to how to solve this? A proper solution or an alternative one would be appreciated.
I don't think you can disable the VPN settings from inside the app using an API provided by Android, as I do not know about any such APIs. However, as a workaround, you can consider doing the following.
While exiting the application (on a back button press), you can use the same listener to pop up another dialog saying the user to turn off the VPN. Hence, turning off the VPN will follow the same tasks that the user had to do while turning on the VPN.
When a user exits the application using a home button press, you might consider using a JobScheduler in your onDestroy function of the exiting activity, so that you can check if the VPN connection is alive in a background service when the application is not running and create a notification which will tell the user that, the VPN is alive. Then on clicking the notification, redirect the user to the VPN configuration settings and guide the user to turn it off.
Hope that helps!
So thanks to #Reaz I've managed to find the proper solution for now.
Tested on Oreo
In case someone else is intrested..
app
implementation 'android.arch.lifecycle:extensions:1.1.1'
AppLifecycleObserver
public class AppLifecycleObserver extends MultiDexApplication implements LifecycleObserver {
public static final String TAG = AppLifecycleObserver.class.getName();
Notifications notif = new Notifications();
Context mContext;
public AppLifecycleObserver(Context context) {
mContext = context;
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onEnterForeground() {
Log.v(TAG,"FOREGROUND");
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onEnterBackground() {
Log.v(TAG,"BACKGROUND");
notif.createVPNNotification(mContext,"VPN","Please make sure to turn VPN off");
}
}
on Any activity you wish to implement # onCreate:
AppLifecycleObserver appLifecycleObserver = new AppLifecycleObserver(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(appLifecycleObserver);

How can i keep the job service running when the app is closed from recent task list by user

I am using Job Scheduler API in my app to schedule a job for me after specific time interval. It runs fine when the app is running. But whenever the user closes the app or clears it from the recent task list the app stops and the scheduled job never executes afterwards until you open the app and it is rescheduled again from the time it is opened.
Now i want someone to help me to keep the jobs on executing even if the app is closed or cleared from the recent task list.
If there is any alternative solution please tell me.
i am looking for the solution from the past 3 days. Tried everything said by developers on StackOverFlow and other sites and none of them worked for me.
This is where is schedule the job!
ComponentName componentName = new
ComponentName(getActivity().getBaseContext(),WallpaperJobService.class);
JobInfo jobInfo = new JobInfo.Builder(777,componentName)
.setRequiresCharging(sharedPreferences.getBoolean("Charging",false))
.setRequiredNetworkType(sharedPreferences.getBoolean("Wifi",false) ?
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY)
.setPeriodic(sharedPreferences.getInt("Duration",15) * 60 *
1000)
.setPersisted(true)
.build();
JobScheduler scheduler = (JobScheduler)
getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
My Job Service Class:
public class WallpaperJobService extends JobService {
private boolean jobCancelled;
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
#Override
public boolean onStartJob(JobParameters params) {
Log.i("WallpaperJobService", "Job started!");
changeWallpaper(params);
return true;
}
private void changeWallpaper(final JobParameters params) {
final ArrayList<Image> images = (ArrayList<Image>)
MainActivity.favoritesRoomDatabase.roomDao().getAllFavoriteWallpapers();
sharedPreferences = getSharedPreferences("GridSize", MODE_PRIVATE);
editor = sharedPreferences.edit();
if (images != null && images.size() != 0) {
if (sharedPreferences.getInt("Index", 0) == images.size()) {
editor.putInt("Index", 0);
editor.commit();
}
Picasso.get().load(Constants.domain +
images.get(sharedPreferences.getInt("Index", 0)).getImage_url()).into(new
Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap,
Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
if (jobCancelled) {
Log.i("WallpaperJobService","Returned");
return;
}
try {
//Doing some work here
} catch (IOException e) {
e.printStackTrace();
}
Log.i("WallpaperJobService", "Job finished!");
jobFinished(params, false);
}
}).start();
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable)
{
Log.i("WallpaperJobService", "Bitmap load failed " +
e.getMessage());
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
} else {
Log.i("WallpaperJobService", "Favorite database is null!");
}
}
#Override
public boolean onStopJob(JobParameters params) {
Log.i("WallpaperJobService", "Job cancelled before completion!");
jobCancelled = true;
return true;
}
}
When doing stuff periodically in the background — JobScheduler, WorkManager, AlarmManager, FCM push messages, etc. — you have to take into account that your process might not be around when it is time for you to do your work. Android will fork a process for you, but it is "starting from scratch". Anything that your UI might have set up in memory, such as a database, would have been for some prior process and might not be set up in the new process.

Android: Broadcast Receiver does not receive BluetoothDevice.ACTION_ACL_CONNECTED on restarting the application

I want my app to auto-connect to already connected bluetooth device on restarting the app. Below is procedure I am performing:-
[Initially] Bluetooth device is 'ON': Then on starting the app.
[Behavior]--> Bluetooth device gets paired and connected successfully ( Intent 'ACTION_ACL_CONNECTED' is received)
Bluetooth device is 'ON': Closed the app, then started the app again.
[Behavior]--> Even though it is connected as displayed on Bluetooth setting, and Broadcast Receiver does not receive Intent 'ACTION_ACL_CONNECTED'.
Note:- On closing the app, it does not disconnect the bluetooth connection.
So, on successful connection app straightaway goes to the HomeScreen. Otherwise, the app goes to a screen having button that takes it to Bluetooth setting(onClickListener present in the code below)
I am new to android development, so I really don't know where am I going wrong. I looked up the solutions for similar issues and applied them, but to no effect.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
registerReceiver(mReceiver, filter);
IntentFilter filter1 = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(mReceiver, filter1);
m_app = (BtApp) getApplication();
imagebt = (ImageView) this.findViewById(R.id.imagebt);
imagebt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
final Toast tag = Toast.makeText(getApplicationContext(), "Connect to device", Toast.LENGTH_LONG);
tag.show();
new CountDownTimer(1000, 1000)
{
public void onTick(long millisUntilFinished) {tag.show();}
public void onFinish() {
//tag.show();
}
}.start();
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()){
mBluetoothAdapter.startDiscovery();
}
Intent intentBluetooth = new Intent();
intentBluetooth.setAction(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(intentBluetooth);
}
});
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ( BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
m_app.m_main.setupCommPort();
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
m_app.m_device = device;
isconnected = true;
new Timer().schedule(new TimerTask() {
#Override
public void run() {
if ( m_app.m_main.m_BtService != null && m_app.m_main.m_BtService.getState() != BluetoothRFCommService.STATE_CONNECTED ) {
m_app.m_main.m_BtService.connect(device, false);
}
}
}, 3500);
} else if ( BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) ) {
isconnected = false;
m_app.m_main.tabHost.setCurrentTab(0);
}
}
};
#Override
protected void onStop()
{
unregisterReceiver(mReceiver);
super.onStop();
}
You won't get BluetoothDevice.ACTION_ACL_CONNECTED event since the device is still connected. The event is fired only on changing of device state from disconnected to connected.
You have 2 options.
You can put your BroadcastReceiver with BluetoothDevice.ACTION_ACL_CONNECTED and BluetoothDevice.ACTION_ACL_DISCONNECTED filters into the Service and track the device connection state in the background. On your app startup you can ask the service to give you the current state of the device.
You can check if some of the Bluetooth profiles contains your device name in the list of connected devices.
For API 18+ you can use BluetoothManager#getConnectedDevices() for API below 18 you can use the following snippet (for each Bluetooth profile)
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
for (BluetoothDevice device : proxy.getConnectedDevices()) {
if (device.getName().contains("DEVICE_NAME")) {
deviceConnected = true;
}
}
if (!deviceConnected) {
Toast.makeText(getActivity(), "DEVICE NOT CONNECTED", Toast.LENGTH_SHORT).show();
}
mBluetoothAdapter.closeProfileProxy(profile, proxy);
}
public void onServiceDisconnected(int profile) {
// TODO
}
};
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.A2DP);

Activity binds Service with Application context -> service leaks on pressing back after configuration change

I'm having an Activity that is launching more instances of itself on user interaction. This activity binds to a service that maintains a bluetooth connection. Currently this is realized like this:
#Override
public void onResume()
{
super.onResume();
//bind the activity to the service when it is not bound yet.
if (!this.isBound)
{
//Application context, because the Connection shall be kept over configuration change, and the activity will be replaced then.
Intent serviceIntent = new Intent(this.getApplicationContext(), ConnectionService.class);
bindService(serviceIntent, this.connection, Context.BIND_AUTO_CREATE);
this.isBound = true;
}
else
{
this.connection = (ServiceConnection)getLastNonConfigurationInstance();
}
this.visible = true;
}
/**
* Saves the bound state.
*/
#Override
public void onSaveInstanceState(Bundle outState)
{
outState.putBoolean("isBound", this.isBound);
}
/**
* Loads the bound state.
*/
#Override
public void onRestoreInstanceState(Bundle inState)
{
this.isBound = inState.getBoolean("isBound");
}
/**
* Called by system, gives the current ServiceConnection to the system to be retrieved again after restart.
*/
#Override
public Object onRetainNonConfigurationInstance() {
return this.connection;
}
#Override
public void onPause()
{
//unbind the activity to the service when it is finishing.
super.onPause();
this.visible = false;
if (this.isFinishing() && this.isBound)
{
unbindService(this.connection);
this.isBound = false;
}
}
Now my problem is: When I change the orientation while the app is running, everything is okay unitl i press the back button. Then
07-05 12:07:03.039: E/ActivityThread(17850): Activity mm.android.prototype.uilayer.DatapointActivity has leaked ServiceConnection mm.android.prototype.uilayer.DatapointActivity$1#408bcbb8 that was originally bound here
07-05 12:07:03.039: E/ActivityThread(17850): android.app.ServiceConnectionLeaked: Activity mm.android.prototype.uilayer.DatapointActivity has leaked ServiceConnection mm.android.prototype.uilayer.DatapointActivity$1#408bcbb8 that was originally bound here
is written on the Log, and the app crashes in the onPause() Method at the unbind command. I've read the post of Dianne Hackborn in the google forum about the thing, that i can't rely on the ServiceConnection, but i didn't find any solution how to cope with it. Can you help me to get rid of this error and/or explain me why my app behaves like this?
I solved it by makeing the ServiceConnection static and reusing it in every instance of myActivity. Also i added a static counter that counts the instances, and unbinds if the last instance closes. This also solves the config change issue:
private boolean isBound = false;
private static int bindCount = 0;
// handles the service connection.
protected static ServiceConnection connection = new ServiceConnection(){
// ...
}
#Override
public void onResume()
{
super.onResume();
//bind the activity to the service when it is not bound yet.
if (!this.isBound)
{
if (bindCount == 0) {
//Application context, because the Connection shall be kept over configuration change, and the activity will be replaced then.
Intent serviceIntent = new Intent(this.getApplicationContext(), ConnectionService.class);
bindService(serviceIntent, DatapointActivity.connection, Context.BIND_AUTO_CREATE);
this.isBound = true;
}
bindCount++;
this.isBound = true;
}
this.visible = true;
}
/**
* Saves the bound state.
*/
#Override
public void onSaveInstanceState(Bundle outState)
{
outState.putBoolean("isBound", this.isBound);
}
/**
* Loads the bound state.
*/
#Override
public void onRestoreInstanceState(Bundle inState)
{
this.isBound = inState.getBoolean("isBound");
}
#Override
public void onPause()
{
//unbind the activity of the service when it is finishing.
super.onPause();
this.visible = false;
if (this.isFinishing())
{
if(bindCount <= 1 && this.isBound )
{
unbindService(DatapointActivity.connection);
}
bindCount--;
}
}

Categories

Resources