
com.octo.android.robospice.SpiceService Maven / Gradle / Ivy
package com.octo.android.robospice;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import roboguice.util.temp.Ln;
import android.app.Application;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import com.octo.android.robospice.networkstate.DefaultNetworkStateChecker;
import com.octo.android.robospice.networkstate.NetworkStateChecker;
import com.octo.android.robospice.persistence.CacheManager;
import com.octo.android.robospice.persistence.DurationInMillis;
import com.octo.android.robospice.persistence.exception.CacheCreationException;
import com.octo.android.robospice.persistence.exception.CacheLoadingException;
import com.octo.android.robospice.persistence.exception.CacheSavingException;
import com.octo.android.robospice.priority.PriorityThreadPoolExecutor;
import com.octo.android.robospice.request.CachedSpiceRequest;
import com.octo.android.robospice.request.RequestProcessor;
import com.octo.android.robospice.request.RequestProcessorListener;
import com.octo.android.robospice.request.listener.RequestListener;
import com.octo.android.robospice.request.listener.SpiceServiceListener;
import com.octo.android.robospice.request.notifier.DefaultRequestListenerNotifier;
import com.octo.android.robospice.request.notifier.RequestListenerNotifier;
import com.octo.android.robospice.request.notifier.SpiceServiceListenerNotifier;
/**
* This is an abstract class used to manage the cache and provide web service
* result to an activity.
* Extends this class to provide a service able to load content from web service
* or cache (if available and enabled). You will have to implement
* {@link #createCacheManager(Application)} to configure the
* {@link CacheManager} used by all requests to persist their results in the
* cache (and load them from cache if possible).
* @author jva
* @author mwa
* @author sni
*/
public abstract class SpiceService extends Service {
// http://stackoverflow.com/a/13359680/693752
/** JUNIT - this is for testing purposes only */
private static boolean isJUnit = false;
// ----------------------------------
// CONSTANTS
// ----------------------------------
protected static final int DEFAULT_NOTIFICATION_ID = 42;
protected static final int DEFAULT_THREAD_COUNT = 1;
protected static final int DEFAULT_THREAD_PRIORITY = Thread.MIN_PRIORITY;
private static final boolean DEFAULT_FAIL_ON_CACHE_ERROR = false;
// ----------------------------------
// ATTRIBUTES
// ----------------------------------
private SpiceServiceBinder mSpiceServiceBinder;
/** Responsible for processing requests. */
private RequestProcessor requestProcessor;
private int currentPendingRequestCount = 0;
private boolean isBound;
private Notification notification;
/** Responsible for persisting data. */
private CacheManager cacheManager;
private SpiceServiceListenerNotifier spiceServiceListenerNotifier;
private RequestListenerNotifier progressReporter;
private final SelfStopperRequestProcessorListener requestProcessorListener = new SelfStopperRequestProcessorListener();
// ----------------------------------
// CONSTRUCTOR
// ----------------------------------
/**
* Default constructor.
*/
public SpiceService() {
mSpiceServiceBinder = new SpiceServiceBinder(this);
}
@Override
public void onCreate() {
super.onCreate();
try {
cacheManager = createCacheManager(getApplication());
} catch (CacheCreationException e) {
Ln.e(e);
stopSelf();
return;
}
if (cacheManager == null) {
Ln.e(new CacheCreationException("createCacheManager() can't create a null cacheManager"));
stopSelf();
return;
}
progressReporter = createRequestRequestListenerNotifier();
spiceServiceListenerNotifier = createSpiceServiceListenerNotifier();
final ExecutorService executorService = getExecutorService();
final NetworkStateChecker networkStateChecker = getNetworkStateChecker();
requestProcessor = createRequestProcessor(executorService, networkStateChecker);
requestProcessor.setFailOnCacheError(DEFAULT_FAIL_ON_CACHE_ERROR);
notification = createDefaultNotification();
Ln.d("SpiceService instance created.");
}
@Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
super.onStartCommand(intent, flags, startId);
return START_NOT_STICKY;
}
/**
* Factory method to create an entity responsible for processing requests
* send to the SpiceService. The default implementation of this method will
* return a {@link RequestProcessor}. Override this method if you want to
* inject a custom request processor. This feature has been implemented
* following a request from Christopher Jenkins.
* @param executorService
* a service executor that can be used to multi-thread request
* processing.
* @param networkStateChecker
* an entity that will check network state.
* @return a {@link RequestProcessor} that will be used to process requests.
*/
protected RequestProcessor createRequestProcessor(ExecutorService executorService, NetworkStateChecker networkStateChecker) {
return new RequestProcessor(getApplicationContext(), cacheManager, executorService, requestProcessorListener, networkStateChecker, progressReporter, spiceServiceListenerNotifier);
}
/**
* For testing purposes only.
* @return the request processor of this spice service.
*/
protected RequestProcessor getRequestProcessor() {
return requestProcessor;
}
/**
* Method to create a Request Progress Reporter object which is responsible
* for informing the listeners of the current state of each request. You can
* use this method to modify the existing behavior
* @return {@link RequestListenerNotifier}
*/
protected RequestListenerNotifier createRequestRequestListenerNotifier() {
return new DefaultRequestListenerNotifier();
}
/**
* Factory method to create an entity responsible to check for network
* state. The default implementation of this method will return a
* {@link DefaultNetworkStateChecker}. Override this method if you want to
* inject a custom network state for testing or to adapt to connectivity
* changes on the Android. This method is also useful to create non-network
* related requests. In that case create a {@link NetworkStateChecker} that
* always return true. This feature has been implemented following a request
* from Pierre Durand.
* @return a {@link NetworkStateChecker} that will be used to determine if
* network state allows requests executions.
*/
protected NetworkStateChecker getNetworkStateChecker() {
return new DefaultNetworkStateChecker();
}
/**
* Factory method to create an {@link ExecutorService} that will be used to
* execute requests. The default implementation of this method will create a
* single threaded or multi-threaded {@link ExecutorService} depending on
* the number of threads returned by {@link #getThreadCount()} and will
* assign them the priority returned by {@link #getThreadPriority()}.
*
* If you override this method in your service, you can supply a custom
* {@link ExecutorService}.
*
* This feature has been implemented following a request from Riccardo
* Ciovati.
* @return the {@link ExecutorService} to be used to execute requests .
*/
protected ExecutorService getExecutorService() {
final int threadCount = getThreadCount();
final int threadPriority = getThreadPriority();
if (threadCount <= 0) {
throw new IllegalArgumentException("Thread count must be >= 1");
} else {
return PriorityThreadPoolExecutor.getPriorityExecutor(threadCount, threadPriority);
}
}
/**
* Creates the SpiceServiceListenerNotifier.
* @return ({@link SpiceServiceListenerNotifier)
*/
protected SpiceServiceListenerNotifier createSpiceServiceListenerNotifier() {
return new SpiceServiceListenerNotifier();
}
/**
* This method can be overriden in order to create a foreground
* SpiceService. By default, it will create a notification that can be used
* to set a spiceService to foreground (depending on the versions of
* Android, the behavior is different : before ICS, no notification is
* shown, on ICS+, a notification is shown with app icon). On Jelly Bean+,
* the notifiation only appears when notification bar is expanded / pulled
* down.
* @return a notification used to tell user that the SpiceService is still
* running and processing requests.
*/
@SuppressWarnings("deprecation")
public Notification createDefaultNotification() {
Notification notification = null;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
notification = new Notification.Builder(this).setSmallIcon(getApplicationInfo().icon).build();
} else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
notification = new Notification.Builder(this).setSmallIcon(getApplicationInfo().icon).getNotification();
} else {
notification = new Notification();
notification.icon = getApplicationInfo().icon;
// temporary fix https://github.com/octo-online/robospice/issues/200
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0);
notification.setLatestEventInfo(this, "", "", pendingIntent);
notification.tickerText = null;
notification.when = System.currentTimeMillis();
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
notification.priority = Notification.PRIORITY_MIN;
}
return notification;
}
protected int getNotificationId() {
return DEFAULT_NOTIFICATION_ID;
}
@Override
public void onDestroy() {
Ln.d("SpiceService instance destroyed.");
super.onDestroy();
}
// ----------------------------------
// DELEGATE METHODS (delegation is used to ease tests)
// ----------------------------------
public abstract CacheManager createCacheManager(Application application) throws CacheCreationException;
/**
* Override this method to increase the number of threads used to process
* requests. This method will have no effect if you override
* {@link #getExecutorService()}.
* @return the number of threads used to process requests. Defaults to
* {@link #DEFAULT_THREAD_COUNT}.
*/
public int getThreadCount() {
return DEFAULT_THREAD_COUNT;
}
/**
* Override this method to change the priority of threads requests. This
* method will have no effect if you override {@link #getExecutorService()}.
* @return the number of threads used to process requests.Defaults to
* {@link #DEFAULT_THREAD_PRIORITY}.
*/
public int getThreadPriority() {
return DEFAULT_THREAD_PRIORITY;
}
public void addRequest(final CachedSpiceRequest> request, final Set> listRequestListener) {
currentPendingRequestCount++;
requestProcessor.addRequest(request, listRequestListener);
showNotificationIfNotBoundAndHasPendingRequestsOtherwiseHideNotification();
}
public boolean removeDataFromCache(final Class> clazz, final Object cacheKey) {
return requestProcessor.removeDataFromCache(clazz, cacheKey);
}
public void removeAllDataFromCache(final Class> clazz) {
requestProcessor.removeAllDataFromCache(clazz);
}
public List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy