I am using A Singleton Class for holding some data through out of application Which is some Queue.
I am creating Singleton Class Instance onCreate
method of my Application
Class.
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
mContext = getApplicationContext();
Queue.getInstance(); // this is my singleton class instance
}
After this I am adding data inside of this singleton class in my activities
Queue.getInstance().addItem(qItem);
Log.d(Constants.TAG, "Added Item Queue Size: "+Queue.getInstance().getQueueList().size());
Until this Everything working fine. I can access data in my Activities
and ListView Adapter
however When I Start Service and try to access data onCreate
of service
Log.d(Constants.TAG, "Playing Item Queue Size: "+Queue.getInstance().getQueueList().size()+" Current Item No. "+Queue.getInstance().getCurrentPlayingItem());
String url = Queue.getInstance().getQueueItem(Queue.getInstance().getCurrentPlayingItem()).getLinkUrl();
My Singleton Instance become null and my singleton creates new instance. Which cause my data loss inside of Service.
Following is flow of my error.
Following is the code of my Singleton Class
import java.util.ArrayList;
import com.taazi.utils.Constants;
import android.util.Log;
public class Queue {
private ArrayList<QueueItem> mQueueList;
static Queue mInstance;
private int currentPlayingItem=0;
private Queue(){
mQueueList = new ArrayList<QueueItem>();
}
public static Queue getInstance(){
if(mInstance == null){
mInstance = new Queue();
Log.d(Constants.TAG, "New Instance");
}
return mInstance;
}
public void addItem(QueueItem item){
mQueueList.add(item);
}
public void removeItem(int position){
mQueueList.remove(position);
}
public ArrayList<QueueItem> getQueueList(){
return mQueueList;
}
public QueueItem getQueueItem(int position){
return mQueueList.get(position);
}
public int getCurrentPlayingItem() {
return currentPlayingItem;
}
public void setCurrentPlayingItem(int currentPlayingItem) {
this.currentPlayingItem = currentPlayingItem;
}
}
AudioPlayBackService.Java
package com.taazi.services;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;
import com.taazi.helper.NotificationHelperNew;
import com.taazi.models.Queue;
import com.taazi.models.QueueItem;
import com.taazi.utils.Constants;
public class AudioPlayBackService extends Service {
/**
* Called to go toggle between pausing and playing the music
*/
public static final String TOGGLEPAUSE_ACTION = "com.taazi.services.togglepause";
/**
* Called to go to pause the playback
*/
public static final String PAUSE_ACTION = "com.taazi.services.pause";
/**
* Called to go to stop the playback
*/
public static final String STOP_ACTION = "com.taazi.services.stop";
/**
* Called to go to the previous track
*/
public static final String PREVIOUS_ACTION = "com.taazi.services.previous";
/**
* Called to go to the next track
*/
public static final String NEXT_ACTION = "com.taazi.services.next";
/**
* Used to build the notification
*/
private NotificationHelperNew mNotificationHelper;
@Override
public IBinder onBind(Intent intent) {
return null;
}
MediaPlayer player;
@Override
public void onCreate() {
super.onCreate();
// Initialize the notification helper
mNotificationHelper = new NotificationHelperNew(this);
Log.d(Constants.TAG, "Playing Item Queue Size: "+Queue.getInstance().getQueueList().size()+" Current Item No. "+Queue.getInstance().getCurrentPlayingItem());
String url = Queue.getInstance().getQueueItem(Queue.getInstance().getCurrentPlayingItem()).getLinkUrl();
player = MediaPlayer.create(this, Uri.parse(url));
player.setLooping(false); // Set looping
updateNotification();
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return 1;
}
public void onStart(Intent intent, int startId) {
// TO DO
}
public IBinder onUnBind(Intent arg0) {
// TO DO Auto-generated method
return null;
}
public void onStop() {
}
public void onPause() {
}
@Override
public void onDestroy() {
mNotificationHelper.killNotification();
player.stop();
player.release();
}
@Override
public void onLowMemory() {
}
/**
* Updates the notification, considering the current play and activity state
*/
private void updateNotification() {
QueueItem item = Queue.getInstance().getQueueItem(Queue.getInstance().getCurrentPlayingItem());
mNotificationHelper.buildNotification("", item.getArtist(),
item.getTitle(), (long)50, null, true);
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.taazi.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="20" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name="com.taazi.app.AppController"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Apptheme" >
<activity
android:name="com.taazi.activities.MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Music service -->
<service
android:name="com.taazi.services.AudioPlayBackService"
android:label="@string/app_name"
android:process=":main" />
</application>
</manifest>
You can make the new instance of the Singleton class by changing the constructor visibility as public in run-time and create new instance using that constructor.
To create the singleton class, we need to have static member of class, private constructor and static factory method. Static member: It gets memory only once because of static, itcontains the instance of the Singleton class. Private constructor: It will prevent to instantiate the Singleton class from outside the class.
Singleton patten means only one instance is allowed. So there is no question of creating multiple instances. Though there are some hacks and workarounds like Serializing the Object and De Serializing it back or using different Class loaders but again it violates the basic principle why Singleton pattern is created for.
Well-designed singleton can have only one instance per application. Creating of multiple instances is a mistake in the application design. It might happen in some cases, e.g.: Non thread safe singleton with lazy initialization: several threads are trying to get an instance and creates multiple instances.
You are running your Service
in a different process which is not the same as your Application
context, that's why the Queue turns out to by null in the new process.
Remove the following from your Service
in manifest and you are good to go:
android:process=":main"
Moreover, I would suggest you to make use of HandlerThread
in your service to offload some heavy operations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With