package triaina.commons.workerservice; import java.lang.reflect.Constructor; import java.util.concurrent.atomic.AtomicReference; import triaina.commons.exception.NotFoundRuntimeException; import triaina.commons.utils.ClassUtils; import triaina.commons.utils.ConstructorUtils; import triaina.commons.workerservice.annotation.Assign; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.ResultReceiver; import android.util.Log; public class WorkerService extends AbstractIntentService { private static final String TAG = WorkerService.class.getSimpleName(); public static final String EXTRA_JOB = "job"; public static final String EXTRA_RECEIVER = "receiver"; public static final String EXTRA_CONFIRMED = "confirmed"; public static final String ACTION_CANCEL_TASK = "triaina.commons.workerservice.WorkerService.cancelTask"; private AtomicReference<Worker<?>> mCurrentWorker = new AtomicReference<Worker<?>>(); private volatile Handler mHandler; private BroadcastReceiver mCancelReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.v(TAG, "Cancel Broadcast Received"); try { Abortable abortable = (Abortable)getCurrentWorker(); if (abortable == null) return; boolean confirmed = intent.getBooleanExtra(EXTRA_CONFIRMED, false); if (!confirmed) abortable.confirm(WorkerService.this, mHandler); else abortable.onAbort(WorkerService.this, mHandler); } catch (ClassCastException exp) { //ignore } } }; public WorkerService() { super(TAG); } @Override public void onCreate() { super.onCreate(); mHandler = new Handler(); registerReceiver(mCancelReceiver, new IntentFilter(ACTION_CANCEL_TASK)); } @Override public void onDestroy() { unregisterReceiver(mCancelReceiver); super.onDestroy(); } protected Handler getHandler() { return mHandler; } @SuppressWarnings("unchecked") protected Worker<?> getWorker(Job job) { Assign assign = job.getClass().getAnnotation(Assign.class); if (assign == null) { Log.w(TAG, "Worker is not assigned to " + job.toString()); throw new NotFoundRuntimeException("Not found assigned worker"); } Worker<?> worker = ClassUtils.newInstance(assign.worker()); @SuppressWarnings("rawtypes") Class[] clazzs = assign.decorators(); if (clazzs.length == 0) return worker; int i = clazzs.length - 1; Worker<?> decorator; Worker<?> arg = worker; do { @SuppressWarnings("rawtypes") Constructor cons = getConstructor(clazzs[i], arg); decorator = (Worker<?>)ConstructorUtils.newInstance(cons, arg); arg = decorator; } while(--i >= 0); return decorator; } @SuppressWarnings({ "rawtypes", "unchecked" }) private Constructor getConstructor(Class clazz, Worker<?> worker) { try { return ClassUtils.getConstructor(clazz, new Class[]{worker.getClass()}); } catch (Exception ignore) { } try { return ClassUtils.getConstructor(clazz, new Class[]{NetworkWorker.class}); } catch (Exception ignore) { } return ClassUtils.getConstructor(clazz, new Class[]{Worker.class}); } protected void setCurrentWorker(Worker<?> worker) { mCurrentWorker.set(worker); } protected Worker<?> getCurrentWorker() { return mCurrentWorker.get(); } protected Job getJob(Intent intent) { return intent.getParcelableExtra(EXTRA_JOB); } @SuppressWarnings("unchecked") @Override public boolean onHandleIntent(Intent intent, int retry, int delayAmount) { Job job = getJob(intent); if (job == null) { Log.w(TAG, "Received job is empty"); return true; } try { @SuppressWarnings("rawtypes") Worker worker = getWorker(job); setCurrentWorker(worker); return worker.process(job, retry, delayAmount, (ResultReceiver)intent.getParcelableExtra(EXTRA_RECEIVER), this, getHandler()); } catch (Exception exp) { Log.e(TAG, exp.getMessage() + "", exp); return true; } finally { setCurrentWorker(null); } } public static void invoke(Context context, Job job) { invoke(WorkerService.class, context, job, null); } public static void invoke(Context context, Job job, ResultReceiver receiver) { invoke(WorkerService.class, context, job, receiver); } protected static void invoke(Class<? extends WorkerService> clazz, Context context, Job job, ResultReceiver receiver) { Intent intent = new Intent(); buildIntent(clazz, intent, context, job, receiver); context.startService(intent); } public static void buildIntent(Intent intent, Context context, Job job, ResultReceiver receiver) { buildIntent(WorkerService.class, intent, context, job, receiver); } protected static void buildIntent(Class<? extends WorkerService> clazz, Intent intent, Context context, Job job, ResultReceiver receiver) { intent.setClass(context, clazz); intent.putExtra(EXTRA_JOB, job); if (receiver != null) intent.putExtra(EXTRA_RECEIVER, receiver); } }