简体   繁体   中英

Android Service Creating new Instance of Singleton Class

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.

  1. On Start of Application Create Instance - Working
  2. Add data from Activities and Adapters - Working
  3. Start Service and Access Data - Not Working because Singleton Instance become null inside service

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 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.

Ok, here is the thread-safe version.

    // package, imports ...

    public class Queue {

        // Make 100% sure your QueueItem class is immutable! Otherwise it will not be thread safe!
        // Also, it makes more sense (more readable) to put your QueueItem together with your Queue class 
        public static class QueueItem {
            //...
        }

        // changed the below line - was: private ArrayList<String> mQueueList; 
        // (search for programming against an interface rather than an implementation)
        private List<QueueItem> mQueueList; 
        static Queue mInstance;
        private int currentPlayingItem=0;

        private Queue() {
            // See javadoc on the synchronizedList() method. 
            mQueueList = Collections.synchronizedList(new ArrayList<QueueItem>());
        }

        // Added synchronized keyword below 
        public synchronized static Queue getInstance(){
            if(mInstance == null){
                mInstance = new Queue();
                Log.d(Constants.TAG, "New Instance");
            }
            return mInstance;
        }

        // Thread safe as it is (provided that QueueItem is immutable)
        public void addItem(QueueItem item){
            mQueueList.add(item);
        }

        // Thread safe as it is
        public void removeItem(int position){
            mQueueList.remove(position);
        }

        // This method is actually inherently flawed, remove this method - you should never need 
        // mQueueList - if you do need this method then your code is structured wrong.
        // changed the below line - was: public ArrayList<QueueItem> getQueueList(){
        /*public List<QueueItem> getQueueList(){
            return mQueueList;
        }*/

        // Thread safe as it is (provided that QueueItem is immutable) 
        public QueueItem getQueueItem(int position){
            return mQueueList.get(position);
        }

        // Made this method synchronized (and thus thread safe) - very unlikely to cause performance issue.
        public synchronized int getCurrentPlayingItem() {
            return currentPlayingItem;
        }

        // Made this method synchronized (and thus thread safe) - very unlikely to cause performance issue.
        public synchronized void setCurrentPlayingItem(int currentPlayingItem) {
            this.currentPlayingItem = currentPlayingItem;
        }


    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM