How to maintain single instance of MediaPlayer [Android] - android

I am using android media player class for playing notification sound in my android Application.
MediaPlayer player = MediaPlayer.create(getApplicationContext(), R.raw.notify);
player.setLooping(false);
player.start();
I need to play different notification sounds in different Activities, so every time i need to play the sound i need to create media player instance and then i need to say start.
But instead of doing this, How can i maintain single instance of the media player throughout the application and use it in all the activities to play the sounds.
Can someone please suggest me the better way of implementing it.
From my point of view i will create one singleton class and i will add all the MediaPlayer related function in this class.
Thanks.

You should consider the Singleton pattern. Make a class MyPlayer that has a static method getMediaPlayer() that returns the same instance of MediaPlayer each time called.

I always do similar thing with a modified version of Singleton Pattern. Since context is needed everywhere in Android, I pass the context to the instance:
public class Asset{
public static Asset(Context context);
}
You can also have different singleton across different context scope, in this implementation, for example:
private static Hashtable<Context, Asset> instances;
public static Asset(Context context){
if (!instances.containKey(context)){
instances.put(context, new Asset(context));
return instances.get(context);
}
The advantage of this compare to classic singleton, is you can define the scope of your singletons. Sometimes you just need the instance stay in same Activity, but second Activity may have different instance. If you need it across different Activity, you just need to pass context.getApplicationContext().

Elaborating a bit more on singleton considerations:
Note: here, the problem of Audioservice failure when creating a series of players consecutively (say 20 mediaPlayers) is addressed too.
Creating the player: The singleton class should create another thread to handle the mediaplayer operations (not the main UI thread)
Create Player Runnable: This thread (created by the singleton instance should be given background priority, a delay of "Thread.sleep(500);" before creation logic to allow the AudioService- used by the MediaPlayer.create()- to finish its work since the later method returns instantly.
Create Player Runnable code:
/**
* Created by George hannuneh on 10/12/2015.
* Holds the background work for creating a media player
*/
public class CreatePlayerRunnable implements Runnable {
static final int CREATE_STATE_FAILED = -1;
static final int CREATE_STATE_STARTED= 0;
static final int CREATE_STATE_COMPLETED= 1;
private static final String TAG ="CreatePlayerRunnable";
private static int sRunnablesCount = 1;
final TaskRunnableCreatePlayerMethods mPlayerTask;
/**
*
* An interface that defines methods that PlayerCreationTask implements. An instance of
* CreatePlayerTask passes itself to an CreatePlayerRunnable instance through the
* CreatePlayerRunnable constructor, after which the two instances can access each other's
* variables.
*/
interface TaskRunnableCreatePlayerMethods {
/**
* Sets the Thread that this instance is running on
* #param currentThread the current Thread
*/
void setCreatePlayerThread(Thread currentThread);
Context getActivity();
Uri getMediaUri();
void handleCreationState(int createStateFailed);
void setPlayer(MediaPlayer returnMediaPlayer);
String getPlayerId();
MediaPlayer getPlayer();
}
/**
* This constructor creates an instance of CreatePlayerRunnable and stores in it a reference
* to the CreatePlayerTask instance that instantiated it.
*
* #param createPlayerTask The CreatePlayerTask
*/
CreatePlayerRunnable(TaskRunnableCreatePlayerMethods createPlayerTask) {
mPlayerTask = createPlayerTask;
}
#Override
public void run() {
/*
* Stores the current Thread in the CreatePlayerTask instance,
* so that the instance
* can interrupt the Thread.
*/
mPlayerTask.setCreatePlayerThread(Thread.currentThread());
// Moves the current Thread into the background
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
MediaPlayer returnMediaPlayer = null;
try {
Thread.sleep(500);
// Before continuing, checks to see that the Thread hasn't
// been interrupted
if (Thread.interrupted()) {
throw new InterruptedException();
}
returnMediaPlayer = MediaPlayer.create(mPlayerTask.getActivity(), mPlayerTask.getMediaUri());
if (returnMediaPlayer == null) {
Log.e("CreatePlayerRunnable", mPlayerTask.getMediaUri()+ " - failed to create player");
return;
}
PlayerEventsHandler playerEvents = new PlayerEventsHandler(mPlayerTask.getPlayerId());
returnMediaPlayer.setLooping(true);
returnMediaPlayer.setOnCompletionListener(playerEvents);
returnMediaPlayer.setOnErrorListener(playerEvents);
returnMediaPlayer.setVolume(0f, 0f);
returnMediaPlayer.start();
} catch (InterruptedException e1) {
// Does nothing
} catch(Exception e)
{
returnMediaPlayer = null;
e.printStackTrace();
}
finally {
if(MainActivity.DEBUG_MODE_ENABLED){
Log.d(TAG, "end of runnable: "+ sRunnablesCount++);
}
if (null == returnMediaPlayer){
mPlayerTask.handleCreationState(CREATE_STATE_FAILED);
} else {
mPlayerTask.setPlayer(returnMediaPlayer);
// Reports a status of "completed"
mPlayerTask.handleCreationState(CREATE_STATE_COMPLETED);
}
// Sets the current Thread to null, releasing its storage
mPlayerTask.setCreatePlayerThread(null);
// Clears the Thread's interrupt flag
Thread.interrupted();
}
}
}

Related

Connecting two objects from different scenes in Unity [duplicate]

How can I pass score value from one scene to another?
I've tried the following:
Scene one:
void Start () {
score = 0;
updateScoreView ();
StartCoroutine (DelayLoadlevel(20));
}
public void updateScoreView(){
score_text.text = "The Score: "+ score;
}
public void AddNewScore(int NewscoreValue){
score = score + NewscoreValue;
updateScoreView ();
}
IEnumerator DelayLoadlevel(float seconds){
yield return new WaitForSeconds(10);
secondsLeft = seconds;
loadingStart = true;
do {
yield return new WaitForSeconds(1);
} while(--secondsLeft >0);
// here I should store my last score before move to level two
PlayerPrefs.SetInt ("player_score", score);
Application.LoadLevel (2);
}
Scene two:
public Text score_text;
private int old_score;
// Use this for initialization
void Start () {
old_score = PlayerPrefs.GetInt ("player_score");
score_text.text = "new score" + old_score.ToString ();
}
but nothing displayed on screen, and there's no error.
Is this the correct way to pass data ?
I am using Unity 5 free edition, develop game for Gear VR (meaning the game will run in android devices).
Any suggestion?
There are many ways to do this but the solution to this depends on the type of data you want to pass between scenes. Components/Scripts and GameObjects are destroyed when new scene is loaded and even when marked as static.
In this answer you can find
Use the static keyword
Use DontDestroyOnLoad
Store the data local
3a PlayerPrefs
3b serialize to XML/JSON/Binary and use FileIO
1. Use the static keyword.
Use this method if the variable to pass to the next scene is not a component, does not inherit from MonoBehaviour and is not a GameObject then make the variable to be static.
Built-in primitive data types such as int, bool, string, float, double. All those variables can be made a static variable.
Example of built-in primitive data types that can be marked as static:
static int counter = 0;
static bool enableAudio = 0;
static float timer = 100;
These should work without problems.
Example of Objects that can be marked as static:
public class MyTestScriptNoMonoBehaviour
{
}
then
static MyTestScriptNoMonoBehaviour testScriptNoMono;
void Start()
{
testScriptNoMono = new MyTestScriptNoMonoBehaviour();
}
Notice that the class does not inherit from MonoBehaviour. This should work.
Example of Objects that cannot be marked as static:
Anything that inherits from Object, Component or GameObject will not work.
1A.Anything that inherits from MonoBehaviour
public class MyTestScript : MonoBehaviour
{
}
then
static MyTestScript testScript;
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
This will not work because it inherits from MonoBehaviour.
1B.All GameObject:
static GameObject obj;
void Start()
{
obj = new GameObject("My Object");
}
This will not work either because it is a GameObject and GameObject inherit from an Object.
Unity will always destroy its Object even if they are declared with the static keyword.
See #2 for a workaround.
2.Use the DontDestroyOnLoad function.
You only need to use this if the data to keep or pass to the next scene inherits from Object, Component or is a GameObject. This solves the problem described in 1A and 1B.
You can use it to make this GameObject not to destroy when scene unloads:
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
You can even use it with the static keyword solve problem from 1A and 1B:
public class MyTestScript : MonoBehaviour
{
}
then
static MyTestScript testScript;
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
The testScript variable will now be preserved when new scene loads.
3.Save to local storage then load during next scene.
This method should be used when this is a game data that must be preserved when the game is closed and reopened. Example of this is the player high-score, the game settings such as music volume, objects locations, joystick profile data and so on.
Thare are two ways to save this:
3A.Use the PlayerPrefs API.
Use if you have just few variables to save. Let's say player score:
int playerScore = 80;
And we want to save playerScore:
Save the score in the OnDisable function
void OnDisable()
{
PlayerPrefs.SetInt("score", playerScore);
}
Load it in the OnEnable function
void OnEnable()
{
playerScore = PlayerPrefs.GetInt("score");
}
3B.Serialize the data to json, xml or binaray form then save using one of the C# file API such as File.WriteAllBytes and File.ReadAllBytes to save and load files.
Use this method if there are many variables to save.
General, you need to create a class that does not inherit from MonoBehaviour. This class you should use to hold your game data so that in can be easily serialized or de-serialized.
Example of data to save:
[Serializable]
public class PlayerInfo
{
public List<int> ID = new List<int>();
public List<int> Amounts = new List<int>();
public int life = 0;
public float highScore = 0;
}
Grab the DataSaver class which is a wrapper over File.WriteAllBytes and File.ReadAllBytes that makes saving data easier from this post.
Create new instance:
PlayerInfo saveData = new PlayerInfo();
saveData.life = 99;
saveData.highScore = 40;
Save data from PlayerInfo to a file named "players":
DataSaver.saveData(saveData, "players");
Load data from a file named "players":
PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>("players");
There is another way:
ScriptableObject
ScriptableObjects are basically data containers but may also implement own logic. They "live" only in the Assets like prefabs. They can not be used to store data permanently, but they store the data during one session so they can be used to share data and references between Scenes ... and - something I also often needed - between Scenes and an AnimatorController!
Script
First you need a script similar to MonoBehaviours. A simple example of a ScriptableObject might look like
// fileName is the default name when creating a new Instance
// menuName is where to find it in the context menu of Create
[CreateAssetMenu(fileName = "Data", menuName = "Examples/ExamoleScriptableObject")]
public class ExampleScriptableObject : ScriptableObject
{
public string someStringValue = "";
public CustomDataClass someCustomData = null;
public Transform someTransformReference = null;
// Could also implement some methods to set/read data,
// do stuff with the data like parsing between types, fileIO etc
// Especially ScriptableObjects also implement OnEnable and Awake
// so you could still fill them with permanent data via FileIO at the beginning of your app and store the data via FileIO in OnDestroy !!
}
// If you want the data to be stored permanently in the editor
// and e.g. set it via the Inspector
// your types need to be Serializable!
//
// I intentionally used a non-serializable class here to show that also
// non Serializable types can be passed between scenes
public class CustomDataClass
{
public int example;
public Vector3 custom;
public Dictionary<int, byte[]> data;
}
Create Instances
You can create instances of ScriptableObject either via script
var scriptableObject = ScriptableObject.CreateInstance<ExampleScriptableObject>();
or to make things easier use the [CreateAssetMenu] as shown in the example above.
As this created ScriptabeObject instance lives in the Assets it is not bound to a scene and can therefore be referenced everywhere!
This when you want to share the data between two Scenes or also e.g. the Scene and an AnimatorController all you need to do is reference this ScriptableObject instance in both.
Fill Data
I often use e.g. one component to fill the data like
public class ExampleWriter : MonoBehaviour
{
// Here you drag in the ScriptableObject instance via the Inspector in Unity
[SerializeField] private ExampleScriptableObject example;
public void StoreData(string someString, int someInt, Vector3 someVector, List<byte[]> someDatas)
{
example.someStringValue = someString;
example.someCustomData = new CustomDataClass
{
example = someInt;
custom = someVector;
data = new Dictionary<int, byte[]>();
};
for(var i = 0; i < someDatas.Count; i++)
{
example.someCustomData.data.Add(i, someDatas[i]);
}
example.someTransformReference = transform;
}
}
Consume Data
So after you have written and stored your required data into this ExampleScriptableObject instance every other class in any Scene or AnimatorController or also other ScriptableObjects can read this data on just the same way:
public class ExmpleConsumer : MonoBehaviour
{
// Here you drag in the same ScriptableObject instance via the Inspector in Unity
[SerializeField] private ExampleScriptableObject example;
public void ExampleLog()
{
Debug.Log($"string: {example.someString}", this);
Debug.Log($"int: {example.someCustomData.example}", this);
Debug.Log($"vector: {example.someCustomData.custom}", this);
Debug.Log($"data: There are {example.someCustomData.data.Count} entries in data.", this);
Debug.Log($"The data writer {example.someTransformReference.name} is at position {example.someTransformReference.position}", this);
}
}
Persistence
As said the changes in a ScriptableObject itself are only in the Unity Editor really persistent.
In a build they are only persistent during the same session.
Therefore if needed I often combine the session persistence with some FileIO (as described in this answer's section 3b) for loading and deserializing the values once at session begin (or whenever needed) from the hard drive and serialize and store them to a file once on session end (OnApplicationQuit) or whenever needed.
(This won't work with references of course.)
Besides playerPrefs another dirty way is to preserve an object during level loading by calling DontDestroyOnLoad on it.
DontDestroyOnLoad (transform.gameObject);
Any script attached to the game object will survive and so will the variables in the script.
The DontDestroyOnLoad function is generally used to preserve an entire GameObject, including the components attached to it, and any child objects it has in the hierarchy.
You could create an empty GameObject, and place only the script containing the variables you want preserved on it.
I use a functional approach I call Stateless Scenes.
using UnityEngine;
public class MySceneBehaviour: MonoBehaviour {
private static MySceneParams loadSceneRegister = null;
public MySceneParams sceneParams;
public static void loadMyScene(MySceneParams sceneParams, System.Action<MySceneOutcome> callback) {
MySceneBehaviour.loadSceneRegister = sceneParams;
sceneParams.callback = callback;
UnityEngine.SceneManagement.SceneManager.LoadScene("MyScene");
}
public void Awake() {
if (loadSceneRegister != null) sceneParams = loadSceneRegister;
loadSceneRegister = null; // the register has served its purpose, clear the state
}
public void endScene (MySceneOutcome outcome) {
if (sceneParams.callback != null) sceneParams.callback(outcome);
sceneParams.callback = null; // Protect against double calling;
}
}
[System.Serializable]
public class MySceneParams {
public System.Action<MySceneOutcome> callback;
// + inputs of the scene
}
public class MySceneOutcome {
// + outputs of the scene
}
You can keep global state in the caller's scope, so scene inputs and outputs states can be minimized (makes testing easy). To use it you can use anonymous functions:-
MyBigGameServices services ...
MyBigGameState bigState ...
Splash.loadScene(bigState.player.name, () => {
FirstLevel.loadScene(bigState.player, (firstLevelResult) => {
// do something else
services.savePlayer(firstLevelResult);
})
)}
More info at https://corepox.net/devlog/unity-pattern:-stateless-scenes
There are various way, but assuming that you have to pass just some basic data, you can create a singelton instance of a GameController and use that class to store the data.
and, of course DontDestroyOnLoad is mandatory!
public class GameControl : MonoBehaviour
{
//Static reference
public static GameControl control;
//Data to persist
public float health;
public float experience;
void Awake()
{
//Let the gameobject persist over the scenes
DontDestroyOnLoad(gameObject);
//Check if the control instance is null
if (control == null)
{
//This instance becomes the single instance available
control = this;
}
//Otherwise check if the control instance is not this one
else if (control != this)
{
//In case there is a different instance destroy this one.
Destroy(gameObject);
}
}
Here is the full tutorial with some other example.
you have several options.
The first one I see is to use static variables, which you will not lose their information or value passing from scenes to scenes (since they are not bound to the object). [you lose the information when closing the game, but not when passing between scenes]
the second option is that the player or the object of which you do not want to lose the information, you pass it through the DontDestroyOnLoad function
Here I give you the documentation and the sample code. [You lose the information when you close the game, but not when you go between scenes]
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
Third is to use the playerPrefab [https://docs.unity3d.com/ScriptReference/PlayerPrefs.html]
that allow you to save information and retrieve it at any time without hanging it even after closing the game [you must be very careful with the latter if you plan to use it to save data even after closing the game since you can lose the data if you close the game suddenly , since player prefab creates a file and retrieves the information from there, but it saves the file at the end or closes the app correctly]

Trouble warming up Chrome CustomTabs with latest androidx browser dependency

I'm looking for an example of how to use the CustomTabs "warm up" functionality to preload Chrome when the host application is initialized. Documentation around using Chrome CustomTabs for loading URLs is woefully incomplete, and all the examples out there are from 5 years ago and use ugly callback mechanisms to accomplish this.
In the latest version (2.2.0), I was able to find this method that looked promising:
* Connects to the Custom Tabs warmup service, and initializes the browser.
*
* This convenience method connects to the service, and immediately warms up the Custom Tabs
* implementation. Since service connection is asynchronous, the return code is not the return
* code of warmup.
* This call is optional, and clients are encouraged to connect to the service, call
* <code>warmup()</code> and create a session. In this case, calling this method is not
* necessary.
*
* #param context {#link Context} to use to connect to the remote service.
* #param packageName Package name of the target implementation.
* #return Whether the binding was successful.
*/
public static boolean connectAndInitialize(#NonNull Context context,
#NonNull String packageName) {
if (packageName == null) return false;
final Context applicationContext = context.getApplicationContext();
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
#Override
public final void onCustomTabsServiceConnected(
#NonNull ComponentName name, #NonNull CustomTabsClient client) {
client.warmup(0);
// Unbinding immediately makes the target process "Empty", provided that it is
// not used by anyone else, and doesn't contain any Activity. This makes it
// likely to get killed, but is preferable to keeping the connection around.
applicationContext.unbindService(this);
}
#Override
public void onServiceDisconnected(ComponentName componentName) { }
};
try {
return bindCustomTabsService(applicationContext, packageName, connection);
} catch (SecurityException e) {
return false;
}
}
But the warmup method doesn't ever get called and performance doesn't seem to be improved. I tried calling this in the Application class as well as the Activity classes that I'd be loading URLs from.
I'm loading URLs using CustomTabsClient.Builder()..build().launchUrl(url)

CountDownTimer : In Activity, ViewModel or separate class?

I would like to create a CountdownTimer which will trigger events that will update the UI (trigger popup, start an animation, etc.).
I wonder how to do this clean, here are my hypothesis and why :
A separate component EventCountdownTimer. I could then benefit the use of LifecycleObserver, but I wonder how to communicate the information back to the activity (I tried extending CountdownTimer and using it in the activity but I have an error and can't get it to compile)
In the Activity itself, it's the simplest but I'm not sure it belongs there as it isn't a UI component and I can't benefit the LifecycleObserver
In the ViewModel. I thought as it's activity related and the CountdownTimer is kinda logic data, it should go in here, but that means also watching the lifecycle of the activity, and holding any Activity related field within ViewModel is bad practice.
What's the best option according to you? And why?
In a MVVM pattern you could have a LiveData observable in your ViewModel which will be observed by the UI and upon value change you update the UI accordingly. How that observable changes value, that is your business logic and all of it should be in your ViewModel or in separate components that will be used by the ViewModel to update the observable state.
This will allow you to separate the UI from the business logic being your observable the bridge of communication between both, without the ViewModel having any knowledge of whats going on in the UI. In simple words it only executes what it is told to execute and updates a variable that is being observed, what then happens in the UI is the UI responsibility and with this you have reached a clear separation of concerns.
A separate component "EventCountdownTimer"
In my opinion, this is the best implementation that you might have in your case. For communicating information back to your activity, you might consider having an interface like the following.
public interface TimerListener {
void onTimerResponse(String response);
}
Modify your EventCountdownTimer to have a constructor which takes TimerListener as a parameter and override the onTimerResponse method in your activity. Now from your EventCountdownTimer, when you are trying to communicate with your activity along with a message, for example, you might just call the function onTimerResponse(msgToDeliver).
Hence your EventCountdownTimer should look something like this.
public class EventCountdownTimer {
public static Context context;
public static TimerListener listener;
public EventCountdownTimer(Context context, TimerListener listener) {
this.context = context;
this.listener = listener;
}
public startCountdown() {
// Start the count down here
// ... Other code
// When its time to post some update to your activity
listener.onTimerResponse(msgToDeliver);
}
}
And from your activity, initialize the EventCountdownTimer like the following.
EventCountdownTimer timer = new EventCountdownTimer(this, new TimerListener() {
#Override
public void onTimerResponse(String message) {
// Do something with the message data
// Update your UI maybe
}
});
I think you have provided good reasons already for not going for other options that you have mentioned.
Google solution : see it on github
/**
* A ViewModel used for the {#link ChronoActivity3}.
*/
public class LiveDataTimerViewModel extends ViewModel {
private static final int ONE_SECOND = 1000;
private MutableLiveData<Long> mElapsedTime = new MutableLiveData<>();
private long mInitialTime;
private final Timer timer;
public LiveDataTimerViewModel() {
mInitialTime = SystemClock.elapsedRealtime();
timer = new Timer();
// Update the elapsed time every second.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final long newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000;
// setValue() cannot be called from a background thread so post to main thread.
mElapsedTime.postValue(newValue);
}
}, ONE_SECOND, ONE_SECOND);
}
public LiveData<Long> getElapsedTime() {
return mElapsedTime;
}
#Override
protected void onCleared() {
super.onCleared();
timer.cancel();
}
}

Using synchronized section in onCreate() in Service

What for do we need to synchronize creating syncadapter in onCreate() in service? As far as I know, there is only one instance of service in system at time. So, there is no change of parallel calls onCreate().
Code from android developers sample:
public class SyncService extends Service {
// Storage for an instance of the sync adapter
private static SyncAdapter sSyncAdapter = null;
// Object to use as a thread-safe lock
private static final Object sSyncAdapterLock = new Object();
public void onCreate() {
/*
* Create the sync adapter as a singleton.
* Set the sync adapter as syncable
* Disallow parallel syncs
*/
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
...
}

Returning an AIDL interface implementation by reference across processes

I have a little Android project going on which involves some IPC where client Activities bind to my service.
I'm using AIDL for IPC and RPC which works pretty good, but I'm having trouble returning a service-side instantiated AIDL interface implementation to the clients:
When the client is running in the same process as the service -- meaning running the service locally -- everything works just fine.
But when client and service are seperated in different processes the method startLogSession, which is defined in ILogDroidBinder.aidl always returns null.
The other method implemented in this interface -- getSessionIds -- which returns a List containing ints, always works (locally and cross-process).
I'm taking a wild guess and suppose my ILogDroidSession implementation should also implement Parcelable, but that wouldn't work, because I can't parcel an object containg a reference to an SQLiteDatabase (or can I?).
Here is the relevant code.
I'd really be glad if someone could help me out here. Maybe I'm just missing a point somewhere, since this is my first Android project and I'm not quite involved yet.
ILogDroidSession.aidl (An implementation of this is what I want to return to the client):
package net.sourceforge.projects.logdroid;
interface ILogDroidSession {
/**
* Logs the given text to the error message channel of the current logging
* session.
* #param text Text to log.
*/
void logError(in String text);
}
ILogDroidBinder.aidl (The IBinder interface passed to the client's onServiceConnected):
package net.sourceforge.projects.logdroid;
import net.sourceforge.projects.logdroid.ILogDroidSession;
interface ILogDroidBinder {
/**
* Starts a new LogDroid session which handles all logging events.
* #param sessionName The name of the session.
* #return An instance of ILogDroidSession.
*/
ILogDroidSession startLogSession(in String sessionName);
/**
* Gets a list with all available LogSession ids.
*/
List getSessionIds();
}
LogDroidService.java (Relevant code from my service):
public class LogDroidService extends Service {
/**
* The binder interface needed for Activities to bind to the
* {#code LogDroidService}.
*/
private final ILogDroidBinder.Stub binder = new ILogDroidBinder.Stub() {
/**
* Starts a new LogDroidSession.
*/
public ILogDroidSession startLogSession(String sessionName) {
return LogDroidService.this.createSession(sessionName);
}
/**
* Gets all available session ids.
*/
public List<Integer> getSessionIds() {
return LogDroidService.this.getSessionIds();
}
};
/**
* The database connection to be used for storing and retrieving log entries.
*/
private LogDroidDb database;
#Override
public void onCreate() {
super.onCreate();
database = new LogDroidDb(getApplicationContext());
try {
database.open(); // opens as writable database
} catch ( SQLException ignorefornow ) {
}
}
#Override
public IBinder onBind(Intent ignore) {
return binder;
}
/**
* Creates a new LogDroidSession which will be returned to the user as a
* AIDL remote object.
* #param sessionName Name of the session.
* #return A new instance of ILogDroidSession
*/
ILogDroidSession createSession(String sessionName) {
LogDroidSession session = new LogDroidSession(database, sessionName);
session.addLoggingOccurredListener(this);
return session;
}
/**
* Retrieves all session ids.
* #return Array containing all LogDroidSession ids.
*/
ArrayList<Integer> getSessionIds() {
return database.getSessionIds();
}
}
MainActivity.java (Relevant client code):
public class MainActivity extends Activity {
private ILogDroidSession session;
private ILogDroidBinder binder;
private ServiceConnection con = new ServiceConnection() {
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
binder = ILogDroidBinder.Stub.asInterface(arg1); // always works
try {
// works locally but always returns null when cross-process
session = binder.startLogSession("TestSession");
// always works
List<Integer> ids = binder.getSessionIds();
} catch ( Exception ex) {
// no exceptions are thrown either when running locally or cross-process
Toast.makeText(getApplicationContext(), ex.getMessage(),
Toast.LENGTH_LONG).show();
}
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
ILogDroidSession can be defined as just interface in java file, it shouldn't be in AIDL.
If the client and LogDroidService are running in different processes, LogDroidSession should be parcelable to send/receive over IPC.
Data that is exchanged across the processes should just be stream of bytes that both sender and receiver understands through a protocol.
I'm taking a wild guess and suppose my ILogDroidSession implementation should also implement Parcelable, but that wouldn't work, because I can't parcel an object containg a reference to an SQLiteDatabase (or can I?).
LogDroidSession can't be parceled here, add new functions to ILogDroidBinder that returns session related information (in the form of plain data types).

Categories

Resources