package edu.vandy.model.services;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import edu.vandy.common.Utils;
import edu.vandy.model.aidl.AcronymExpansion;
import edu.vandy.model.aidl.AcronymRequest;
import edu.vandy.model.aidl.AcronymResults;
import edu.vandy.model.services.AcronymServiceBase;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
* This class uses asynchronous AIDL calls to expand acronyms via an
* Acronym Web service. The AcronymModel that binds to this Service
* will receive an IBinder that's an instance of AcronymRequest, which
* extends IBinder. The AcronymModel can then interact with this
* Service by making one-way method calls on the AcronymRequest object
* asking this Service to lookup the Acronym's meaning, passing in an
* AcronymResults object and the Acronym string. After the lookup is
* finished, this Service sends the Acronym results back to the
* AcronymModel by calling sendResults() on the AcronymResults object.
*
* AIDL is an example of the Broker Pattern, in which all interprocess
* communication details are hidden behind the AIDL interfaces.
*/
public class AcronymServiceAsync
extends AcronymServiceBase {
/**
* Reference to the ExecutorService that manages a pool of
* threads. We need this feature since Android's Binder framework
* executes oneway methods from a client in a single thread rather
* than a pool of thread.
*/
private ExecutorService mExecutorService;
/**
* Factory method that makes an Intent used to start the
* AcronymServiceAsync when passed to bindService().
*
* @param context
* The context of the calling component.
*/
public static Intent makeIntent(Context context) {
return new Intent(context,
AcronymServiceAsync.class);
}
/**
* Called when a client (e.g., AcronymModel) calls bindService()
* with the proper Intent. Returns the implementation of
* AcronymRequest, which is implicitly cast as an IBinder.
*/
@Override
public IBinder onBind(Intent intent) {
return mAcronymRequestImpl;
}
/**
* Hook method called when the Service is created.
*/
@Override
public void onCreate() {
// Call up to the super onCreate() method to perform its
// initialization operations.
super.onCreate();
// Create an ExecutorService that manages a pool of threads.
mExecutorService = Executors.newCachedThreadPool();
}
/**
* Hook method called when the last client unbinds from the
* Service.
*/
@Override
public void onDestroy() {
// Immediately shutdown the ExecutorService.
mExecutorService.shutdownNow();
// Call up to the super onCreate() method to perform its
// destruction operations.
super.onDestroy();
}
/**
* The concrete implementation of the AIDL AcronymRequest
* interface, which extends the Stub class that implements
* AcronymRequest, thereby allowing Android to handle calls across
* process boundaries. This method runs in a separate Thread as
* part of the Android Binder framework.
*
* This implementation plays the role of Invoker in the Broker
* Pattern.
*/
private final AcronymRequest.Stub mAcronymRequestImpl =
new AcronymRequest.Stub() {
/**
* Implement the AIDL AcronymRequest expandAcronym()
* method, which forwards to getAcronymResults() to obtain
* the results and then sends these results back to the
* AcronymModel via a callback.
*/
@Override
public void expandAcronym(final String acronym,
final AcronymResults callback)
throws RemoteException {
final Runnable getCurrentAcronymRunnable = () -> {
try {
// Call the Acronym Web service to get the
// list of possible expansions of the
// designated location.
final List<AcronymExpansion> acronymExpansions =
getAcronymExpansions(acronym);
if (acronymExpansions != null) {
Log.d(TAG, ""
+ acronymExpansions.size()
+ " result(s) for Acronym: "
+ acronym);
// Invoke a one-way callback to send
// list of Acronym expansions back to
// the client.
callback.sendResults(acronymExpansions);
} else {
Log.d(TAG,
"No expansion for \""
+ acronym
+ "\" found");
// Invoke a one-way callback to send
// an error message back to the
// client.
callback.sendError("No expansion for \""
+ acronym
+ "\" found");
}
} catch (Exception e) {
Log.d(TAG,
"getCurrentAcronym() "
+ e);
}
};
// Determine if we're on the UI thread or not.
if (Utils.runningOnUiThread())
// Execute getCurrentAcronymRunnable in a separate
// thread if this service has been configured to
// be collocated with an activity.
mExecutorService.execute(getCurrentAcronymRunnable);
else
// Run the getCurrentAcronymRunnable in the pool
// thread if this service has been configured to
// run in its own process.
getCurrentAcronymRunnable.run();
}
};
}