Muaz Usmani
Muaz Usmani

Reputation: 1318

Singleton object is recreating

I am facing a problem, that I created a class Controller it is singleton but its object is recreating when I access in different activity of same application,

Main_Activity is my launching activity

public class Main_Activity extends Activity{
       private Controller simpleController;
       protected void onCreate(Bundle savedInstanceState) {
                 super.onCreate(savedInstanceState);
                 setContentView(R.layout.main);
                 simpleController = Controller.getInstance(this);
       }
}

This is my Controller it is singleton, in it I am setting alarm which is of 10sec from now and my MyMainLocalReciever receives that alarm and notify using notification.

public class Controller {
       private MediaPlayer mp;
       public Context context;
       private static Controller instance;

       public static Controller getInstance(Context context) {
              if (instance == null) {
                    instance = new Controller(context);
              }
              return instance;
       }

      private Controller(Context context) {
            Log.d("TAG", "Creating Controller object");
            mp = null;
            this.context = context;
            setAlarm(10);
        }

     public void setAlarm(int position) {
        Intent intent = new Intent(context, MyMainLocalReciever.class);
        intent.putExtra("alarm_id", "" + position);
        PendingIntent sender = PendingIntent.getBroadcast(context,
                position, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        // Get the AlarmManager service
        AlarmManager am = (AlarmManager) context
                .getSystemService(Activity.ALARM_SERVICE);
        am.cancel(sender);
        am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                + (position*1000), sender);
    }

}

This is my receiver MyMainLocalReciever it notify and I am binding an intent which starts an activity called NotificationDialog

public class MyMainLocalReciever extends BroadcastReceiver {
private NotificationManager notificationManager;
private int alarmId = 0;

@Override
public void onReceive(Context context, Intent intent) {
    if (notificationManager == null) {
        notificationManager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
    }
    Bundle bundle = intent.getExtras();

    String alarm_Id = bundle.getString("alarm_id");

        try {
        alarmId = Integer.parseInt(alarm_Id);
    } catch (Exception e) {
        Log.d("Exception", "exception in converting");
    }

    Controller myC = Controller.getInstance(context);
    if ((myC.getMp() != null)) {
        myC.getMp().stop();
        myC.setMp(null);
    }
    if (myC.getMp() == null) {

            myC.setMp(MediaPlayer.create(context , R.id.mpFile));
            myC.getMp().start();
    }

    NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
            .setTicker("Its Ticker")
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle("Its Title")
            .setContentText("Its Context")
            .setAutoCancel(true)
            .setContentIntent(
                    PendingIntent.getActivity(context, 0, new Intent(context,
                            NotificationDialog.class)
                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                                    | Intent.FLAG_ACTIVITY_CLEAR_TASK), 0));

    notificationManager.notify("interstitial_tag", alarmId,
            builder.getNotification());

}

}

Till now(before NotificationDialog) code is working perfect MediaPlayer object which is in Controller class is working fine too, but when I access my singleton Controller here in NotificationDialog, it is creating new object of Controller, it should not do that, it should retain that Controller object which is singleton.

public class NotificationDialog extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.notification_dialog);

}

public void onViewContent(View v) { //this method is invoked when I click on a button binded in xml file

    Controller myC = Controller.getInstance(getApplicationContext());

    if (myC.getMp() != null) {
        myC.getMp().stop();
        myC.setMp(null);
    }
    finish();
}

}

Kindly help me regarding this, I will appreciate your help. Regards

EDIT: Here is my Manifest

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".Main_Activity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name="test.SettingsActivity"
        android:label="@string/app_name" />
    <activity
        android:name="test.NotificationDialog"
        android:label="@string/app_name" />
    <service android:name="test.MyService" >
    </service>

    <receiver
        android:name="test.MyMainLocalReciever"
        android:process=":remote" />
</application>

Upvotes: 4

Views: 2001

Answers (3)

David Wasser
David Wasser

Reputation: 95578

Your process is getting killed by Android when it is idle in the background. Android will kill off your process if there are no active components (Activities, Services, etc.) or when it needs the memory (even if you have active components).

When the user uses your notification, Android creates a new process for you. That is why the Singleton is gone and needs to get recreated.

EDIT:

After you posted your manifest I immediately saw the problem. This is it:

<receiver
    android:name="test.MyMainLocalReciever"
    android:process=":remote" />

Your process isn't getting killed. Your BroadcastReceiver is running in another separate process. In that process, the singleton hasn't been set up yet.

Remove android:process=":remote" from your <receiver> tag in the manifest.

Upvotes: 6

DroidBender
DroidBender

Reputation: 7892

As the Singleton will be a static object used by many Activities, you don't have to pass the Context to the constructor. Passing it to the methods which will need it, is a better option.

public class Controller {

        private static volatile Controller instance = null;

        private Controller () {   }

        public static Controller getInstance() {
                if (instance == null) {
                        synchronized (Controller .class)
                                if (instance == null) {
                                        instance = new Controller();
                                }
                }
                return instance;
        }

        public void setAlarm(Context context, int position) {
             // do stuff
        }
}

Upvotes: 0

Dmytro Chyzhykov
Dmytro Chyzhykov

Reputation: 1854

Please read about the Initialization-on-demand holder idiom. It's very clear and simple article about right Singleton in the Java programming language.

Upvotes: 1

Related Questions