Skip to content

Instantly share code, notes, and snippets.

@lujop
Created October 13, 2016 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lujop/9f29c0028d6800328d1ac0a11844c5bf to your computer and use it in GitHub Desktop.
Save lujop/9f29c0028d6800328d1ac0a11844c5bf to your computer and use it in GitHub Desktop.
Foreground service that can be shared with several activities
package cat.joanpujol.androidbase.services.foreground;
/**
* Created by lujop on 11/10/16.
*/
public interface ForegroundService {
void onCreate();
void onDestroy();
}
package cat.joanpujol.androidbase.services.foreground;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import cat.joanpujol.seniorlauncher.error.OperationError;
import timber.log.Timber;
/**
* Created by lujop on 11/10/16.
* Servei destinat fer tasques lligades a la vida d'una o varies activities. El seu cicle de vida dura mentre alguna
* de les activities que l'utilitzen estigui activa.<br/>
*/
public class ForegroundServiceManager {
private static ForegroundServiceManager INSTANCE;
private BindedServices bindedServices = new BindedServices();
private Handler mainThreadHandler;
private ForegroundServiceManager(Handler mainThreadHandler) {
this.mainThreadHandler = mainThreadHandler;
}
//Only for testing
synchronized static void clear() {
INSTANCE = null;
}
//Only for testing
synchronized static void init(Handler mainThreadHandler) {
INSTANCE = new ForegroundServiceManager(mainThreadHandler);
}
public static synchronized ForegroundServiceManager get() {
if(INSTANCE==null)
INSTANCE = new ForegroundServiceManager(new Handler(Looper.getMainLooper()));
return INSTANCE;
}
public <T extends ForegroundService> T bind(Class<T> foregroundServiceClass, Activity activity) {
ensureMainThread();
BindedInfo bi = bindedServices.getBinding(foregroundServiceClass);
if(bi!=null) {
bindedServices.addBind(foregroundServiceClass,activity);
} else {
ForegroundService foregroundService = createForegroundService(foregroundServiceClass);
bindedServices.registerNewbinding(foregroundServiceClass,foregroundService,activity);
}
Timber.v("Binding to service %s",foregroundServiceClass.getSimpleName());
return (T) bindedServices.getBinding(foregroundServiceClass).service;
}
private <T extends ForegroundService> ForegroundService createForegroundService(Class<T> foregroundServiceClass) {
try {
ForegroundService service = foregroundServiceClass.newInstance();
service.onCreate();
Timber.d("Create service %s",foregroundServiceClass.getSimpleName());
return service;
} catch (Exception e) {
throw new OperationError("Error creating foreground service",e);
}
}
public <T extends ForegroundService> void unbind(Class<T> foregroundService, Activity activity) {
ensureMainThread();
BindedInfo bi = bindedServices.getBinding(foregroundService);
if(bi!=null) {
bindedServices.removeBinding(foregroundService,activity);
if(!bindedServices.hasBindings(foregroundService)) {
postServiceRemove(foregroundService);
Timber.v("Posting service destroy for %s if not rebinded soon",foregroundService.getSimpleName());
}
Timber.v("Unbinding from service %s",foregroundService.getSimpleName());
}
}
private <T extends ForegroundService> void postServiceRemove(final Class<T> foregroundService) {
final long removeTime = System.currentTimeMillis();
BindedInfo bi = bindedServices.getBinding(foregroundService);
bi.postedRemoveTime = removeTime;
mainThreadHandler.post(() -> {
BindedInfo binow = bindedServices.getBinding(foregroundService);
Timber.v("Start destroy. removeTime=%d binow=%s",removeTime,binow);
if(binow!=null && !bindedServices.hasBindings(foregroundService) && binow.postedRemoveTime == removeTime) {
try {
binow.service.onDestroy();
Timber.d("Service %s destroyed",foregroundService.getSimpleName());
} catch (Exception e) {
throw new OperationError("Error destoying service",e);
}
bindedServices.removeService(foregroundService);
} else {
String reason = "unknow";
if(binow==null)
reason = "No binded service info";
else if(bindedServices.hasBindings(foregroundService))
reason = "Bindings present";
else if(binow.postedRemoveTime != removeTime)
reason = "Posted removeTime is "+binow.postedRemoveTime+" and removeTime is "+removeTime;
Timber.d("Not needed to destroy service %s by now. Reason: %s",foregroundService.getSimpleName(),reason);
}
});
Timber.v("Destroy posted with removeTime %d",removeTime);
}
private void ensureMainThread() {
boolean mainLooper = true;
try {
mainLooper = Looper.myLooper() == Looper.getMainLooper();
} catch(Exception e) {} //When running unit tests
if(!mainLooper)
throw new OperationError("Can only bind/unbind from Main Thread");
}
}
class BindedServices {
private Map<Class,BindedInfo> binded = new HashMap<>();
public BindedInfo getBinding(Class classe) {
return binded.get(classe);
}
public void registerNewbinding(Class classe, ForegroundService foregroundService, Activity activity) {
BindedInfo bi = new BindedInfo(foregroundService);
bi.bindedBy.add(activity);
bi.postedRemoveTime = 0;
binded.put(classe,bi);
}
public void addBind(Class classe, Activity activity) {
BindedInfo binding = getBinding(classe);
binding.postedRemoveTime = 0;
binding.bindedBy.add(activity);
}
public void removeBinding(Class classe,Activity activity) {
BindedInfo binding = getBinding(classe);
if(binding!=null)
binding.bindedBy.remove(activity);
}
public void removeService(Class classe) {
if(hasBindings(classe))
throw new OperationError("Can't remove a service "+classe.getSimpleName()+" with bindings");
binded.remove(classe);
}
public boolean hasBindings(Class classe) {
BindedInfo bi = getBinding(classe);
return bi!=null && !bi.bindedBy.isEmpty();
}
}
class BindedInfo {
public final ForegroundService service;
public final Set<Activity> bindedBy = new HashSet<>();
public long postedRemoveTime;
public BindedInfo(ForegroundService service) {
this.service = service;
}
@Override
public String toString() {
return "BindedInfo{" +
"bindedBy=" + bindedBy +
", service=" + service +
", postedRemoveTime=" + postedRemoveTime +
'}';
}
}
@tusharuit25
Copy link

And How to use it ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment