package com.mcxiaoke.apptoolkit.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Project: apptoolkit
* Package: com.mcxiaoke.apptoolkit.service
* User: mcxiaoke
* Date: 13-6-15
* Time: 下午5:56
*/
abstract class BaseService extends Service implements Handler.Callback {
private static final String TAG = BaseService.class.getSimpleName();
private static final String CLASS_NAME = BaseService.class.getName();
protected static final void debug(String tag, String... messages) {
if (messages != null && messages.length > 0) {
for (String message : messages) {
Log.v(tag, message);
}
}
}
public static final String EXTRA_CMD = CLASS_NAME + ".EXTRA_CMD";
public static final String EXTRA_ID = CLASS_NAME + ".EXTRA_ID";
public static final String EXTRA_KEY = CLASS_NAME + ".EXTRA_KEY";
public static final String EXTRA_STATUS = CLASS_NAME + ".EXTRA_STATUS";
public static final String EXTRA_MESSENGER = CLASS_NAME + ".EXTRA_MESSENGER";
public static final String EXTRA_RESULT_RECEIVER = CLASS_NAME + ".EXTRA_RESULT_RECEIVER";
public static final String EXTRA_STRING_LIST = CLASS_NAME + ".EXTRA_STRING_LIST";
/**
* command for do nothing
*/
public static final int CMD_NONE = 0;
/**
* check task status ,running or not
* need extra: id , type long
*/
public static final int CMD_STATUS = -1001;
/**
* cancel task
* need extra: id, type long
*/
public static final int CMD_CANCEL = -1002;
private final Object mLock = new Object();
private boolean mDebug;
private BaseService mBaseService;
private NotificationManager mNotificationManager;
private LocalBroadcastManager mLocalBroadcastManager;
private ExecutorService mExecutor;
private HandlerThread mHandlerThread;
private Handler mHandler;
private Handler mUiHandler;
private Map<Long, Runnable> mTasks;
private Map<Long, Future<?>> mFutures;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
if (isDebug()) {
debug(TAG, "onCreate()");
}
mBaseService = this;
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mTasks = Collections.synchronizedMap(new WeakHashMap<Long, Runnable>());
mFutures = Collections.synchronizedMap(new WeakHashMap<Long, Future<?>>());
ensureHandler();
ensureExecutor();
}
@Override
public final int onStartCommand(Intent intent, int flags, int startId) {
if (isDebug()) {
debug(TAG, "onStartCommand()");
}
handleIntent(intent);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (isDebug()) {
debug(TAG, "onDestroy()");
}
destroyHandler();
destroyExecutor();
mTasks.clear();
mFutures.clear();
}
private void handleIntent(final Intent intent) {
if (intent == null) {
if (isDebug()) {
debug(TAG, "handleIntent() intent is null.");
}
return;
}
int cmd = intent.getIntExtra(EXTRA_CMD, 0);
if (isDebug()) {
debug(TAG, "handleIntent() cmd=" + cmd + " intent=" + intent.toString());
}
switch (cmd) {
case CMD_STATUS:
onCmdStatus(intent);
break;
case CMD_CANCEL:
onCmdCancel(intent);
break;
case CMD_NONE:
break;
default:
break;
}
final long taskId = System.currentTimeMillis();
final Runnable runnable = new Runnable() {
@Override
public void run() {
if (isDebug()) {
debug(TAG, "handleIntent() run() START");
}
onHandleIntent(taskId, intent);
if (isDebug()) {
debug(TAG, "handleIntent() run() END");
}
remove(taskId);
}
};
Future<?> future = submit(runnable);
mTasks.put(taskId, runnable);
mFutures.put(taskId, future);
}
private void onCmdStatus(Intent intent) {
long taskId = intent.getLongExtra(EXTRA_ID, 0);
if (isDebug()) {
debug(TAG, "onCmdStatus()taskId=" + taskId);
}
if (taskId > 0) {
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
if (messenger != null) {
boolean isRunning = isRunning(taskId);
Message message = Message.obtain();
message.what = CMD_STATUS;
message.arg1 = isRunning ? 1 : 0;
message.obj = isRunning;
}
}
}
private void onCmdCancel(Intent intent) {
long taskId = intent.getLongExtra(EXTRA_ID, 0);
if (isDebug()) {
debug(TAG, "onCmdCancel()taskId=" + taskId);
}
if (taskId > 0) {
cancel(taskId);
// Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
// if (messenger != null) {
// boolean isRunning = isRunning(taskId);
// Message message = Message.obtain();
// message.what = CMD_CANCEL;
// message.arg1 = isRunning ? 1 : 0;
// message.obj = isRunning;
// }
}
}
@Override
public boolean handleMessage(Message msg) {
if (isDebug()) {
debug(TAG, "handleMessage() msg=" + msg);
}
return true;
}
private void remove(long taskId) {
if (isDebug()) {
debug(TAG, "remove() taskId=" + taskId);
}
mTasks.remove(taskId);
mFutures.remove(taskId);
}
protected final void cancel(long taskId) {
if (isDebug()) {
debug(TAG, "cancel() taskId=" + taskId);
}
Runnable runnable = mTasks.remove(taskId);
if (runnable != null) {
if (runnable instanceof ExtendedRunnable) {
((ExtendedRunnable) runnable).cancel();
}
}
Future<?> future = mFutures.remove(taskId);
if (future != null) {
future.cancel(true);
}
}
protected final void cancelAllTasks() {
Collection<Long> taskIds = mTasks.keySet();
for (long id : taskIds) {
cancel(id);
}
mTasks.clear();
mFutures.clear();
}
protected final boolean isRunning(long taskId) {
return mFutures.get(taskId) != null && mTasks.get(taskId) != null;
}
protected final void execute(Runnable runnable) {
String name = "Runnable";
ensureExecutor();
if (runnable instanceof ExtendedRunnable) {
name = ((ExtendedRunnable) runnable).getName();
}
if (isDebug()) {
debug(TAG, "execute() name=" + name);
}
mExecutor.execute(runnable);
}
protected final Future<?> submit(Runnable runnable) {
ensureExecutor();
String name = "Runnable";
if (runnable instanceof ExtendedRunnable) {
name = ((ExtendedRunnable) runnable).getName();
}
if (isDebug()) {
debug(TAG, "submit() name=" + name);
}
return mExecutor.submit(runnable);
}
protected final <T> Future<T> submit(Runnable runnable, T result) {
ensureExecutor();
return mExecutor.submit(runnable, result);
}
protected final <T> Future<T> submit(Callable<T> callable) {
ensureExecutor();
return mExecutor.submit(callable);
}
protected final ExecutorService getExecutor() {
return ensureExecutor();
}
protected final BaseService getBaseService() {
return this;
}
private void ensureHandler() {
if (mHandlerThread == null) {
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
if (mUiHandler == null) {
mUiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
}
}
private void destroyHandler() {
synchronized (mLock) {
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
if (mHandlerThread != null) {
mHandlerThread.quit();
mHandlerThread = null;
}
if (mUiHandler != null) {
mUiHandler.removeCallbacksAndMessages(null);
mUiHandler = null;
}
}
}
private ExecutorService ensureExecutor() {
if (mExecutor == null) {
mExecutor = supplyExecutor();
}
return mExecutor;
}
private void destroyExecutor() {
if (mExecutor != null) {
mExecutor.shutdownNow();
mExecutor = null;
}
}
protected final void sendMessage(Message message) {
if (mHandler != null) {
mHandler.sendMessage(message);
}
}
protected final void sendUiMessage(Message message) {
if (mUiHandler != null) {
mUiHandler.sendMessage(message);
}
}
protected void runOnUiThread(Runnable runnable) {
if (mUiHandler != null) {
mUiHandler.post(runnable);
}
}
protected final void showNotification(int id, Notification notification) {
if (notification != null) {
mNotificationManager.notify(id, notification);
}
}
protected final void cancelNotification(int id) {
mNotificationManager.cancel(id);
}
protected final void cancelAllNotifications() {
mNotificationManager.cancelAll();
}
public final void sendLocalBroadcast(Intent intent) {
mLocalBroadcastManager.sendBroadcast(intent);
}
public final void sendLocalBroadcastSync(Intent intent) {
mLocalBroadcastManager.sendBroadcastSync(intent);
}
/**
* can be override to use custom executor
*
* @return
*/
protected ExecutorService supplyExecutor() {
return Executors.newCachedThreadPool();
}
protected abstract void onHandleIntent(long taskId, Intent intent);
protected abstract boolean isDebug();
protected static class LocalBinder<T extends BaseService> extends Binder {
T mService;
public LocalBinder(T service) {
this.mService = service;
}
public T getService() {
return mService;
}
}
}