My app includes a sync adapter; it has been working fine for years, but recently I've received a very strange crash report:
Samsung Galaxy S6 Edge+ (zenlte), 4096MB RAM, Android 7.0
java.lang.ClassCastException:
1. at com.myappid.android.ScormApplication.getApplication (ScormApplication.java:34)
2. at com.myappid.android.sync.SyncAdapter.onPerformSync (SyncAdapter.java:98)
3. at android.content.AbstractThreadedSyncAdapter$SyncThread.run (AbstractThreadedSyncAdapter.java:272)
ScormApplication.java:
public class ScormApplication extends Application {
//...
public static ScormApplication getApplication(Context context) {
if (context instanceof ScormApplication) {
return (ScormApplication) context;
}
return (ScormApplication) context.getApplicationContext(); // this is a line 34
}
//...
}
SyncAdapter.java:
public class SyncAdapter extends AbstractThreadedSyncAdapter {
//...
#Override
public void onPerformSync(Account account,
Bundle extras,
String authority,
ContentProviderClient provider,
SyncResult syncResult) {
// sync code here...
ScormApplication.getApplication(getContext()).sendOttoEvent(ottoEvent); // this is a line 98
}
}
Thus, it means that the returned object from the method getApplicationContext called on SyncAdapter's context is not an instance of my application class. But how can it be possible?
Here is a bit more of my code:
AndroidManifest.xml (no separate process is declared) :
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
<application ...>
...
<service
android:name="com.myappid.android.sync.SyncService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="#xml/syncadapter" />
</service>
...
SyncService.java:
public class SyncService extends Service {
private static SyncAdapter sSyncAdapter = null;
private static final Object sSyncAdapterLock = new Object();
#Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
#Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
#xml/syncadapter:
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="#string/content_authority"
android:accountType="#string/account_type"
android:userVisible="true"
android:supportsUploading="true"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true" />
Related
I am still new to Android development, but I’m creating an app that sendings an HTTP request when it receives a broadcast (from a barcode scan). I have tried to implement some other solutions that I read on here but haven't quite figured it out yet. It would be great if someone could help me get going in the right direction.
Essentially, the end goal is for the app to keep running in the background forever so that even if the app is not open and a barcode is scanned and sent to the app via broadcast signal, the HTTP request is still sent.
This is what I have so far:
MainActivity.java
public class MainActivity extends Activity implements CompoundButton.OnCheckedChangeListener {
public static final String BARCODE_BROADCAST = "...";
private final BarcodeReceiver barcodeReceiver = new BarcodeReceiver();
private TextView mTextView;
private String scannedBarcode;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.barcode);
}
#Override
public void onResume() {
super.onResume();
registerBarcodeScanner();
}
#Override
public void onPause() {
super.onPause();
unregisterBarcodeScanner();
// this code needs to keep the app running
Intent restartService = new Intent("RestartService");
this.startService(restartService);
sendBroadcast(restartService);
}
public void onDestroy() {
super.onDestroy();
// this code needs to keep the app running
Intent restartService = new Intent("RestartService");
this.startService(restartService);
sendBroadcast(restartService);
}
private void registerBarcodeScanner() {
registerReceiver(barcodeReceiver, new IntentFilter(BARCODE_BROADCAST));
}
private void unregisterBarcodeScanner() {
unregisterReceiver(barcodeReceiver);
}
private void displayBarcode() {
if (scannedBarcode == null) return;
String text = getString(R.string.barcode_scanned, scannedBarcode);
mTextView.setText(text);
/* SEND HTTP REQUEST */
}
private class BarcodeReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BARCODE_BROADCAST)) {
String barcode = intent.getStringExtra("Barcode");
if (barcode != null) {
scannedBarcode = barcode;
displayBarcode();
}
}
}
}
}
RestartService.java
public class RestartService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, MainActivity.class));
}
}
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="..."
android:versionCode="1">
<uses-sdk android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
tools:replace="android:label">
<activity
android:name="...MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="...RestartService"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="RestartService" />
</intent-filter>
</receiver>
</application>
</manifest>
I'm trying to create a service that handles networking in my app. I followed all the steps in https://developer.android.com/reference/android/app/Service and https://developer.android.com/guide/components/bound-services#Binding, but the activity doesn't seem to connect with the service.
Here is the SocketService, which has the TcpClient object that connects to my server using sockets:
public class SocketService extends Service{
TcpClient tcpClient = new TcpClient();
private final IBinder mBinder = new LocalBinder();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
SocketService getService() {
// Return this instance of LocalService so clients can call public methods
return SocketService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/*
* Client methods
*/
public void connect(MyCallback callback, String ip, int port){
tcpClient.connect(callback, ip, port);
}
public String disconnect(){
return tcpClient.disconnect();
}
public String send(String data){
return tcpClient.send(data);
}
public String recv(){
return tcpClient.recv();
}
}
Here is my main activity:
public class MainActivity extends AppCompatActivity {
SocketService socketService;
boolean bound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart(){
super.onStart();
Intent serviceIntent = new Intent(MainActivity.this, SocketService.class);
if(bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)){
Toast.makeText(getApplicationContext(), "bound", Toast.LENGTH_LONG);
}
if(bound) {
socketService.connect(this, ip, port);
} else {
Toast.makeText(getApplicationContext(), "not bond", Toast.LENGTH_LONG).show();
}
}
/*
* Service callback
*/
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
Toast.makeText(getApplicationContext(), "ServiceConnection", Toast.LENGTH_LONG);
socketService = ((SocketService.LocalBinder) service).getService();
bound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
socketService = null;
bound = false;
}
};
}
And the AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.drrbarrera.home">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".SocketService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
As I said before, the MainActivity doesn't seem to connect. "socketService" (in the MainActivity) stays null and "bound" stays false, like if "mConnection" wasn't being executed. Any idea of what might be wrong?
Any help is appreciated.
Thanks in advance!
The call to bindService() returns a boolean result telling you whether the bind is successful (really, it means in progress). The operation is asynchronous, even within the same process (which is what your LocalBinder is.)
In other words, the binding is not complete until your ServiceConnection.onServiceConnected() is called back. Once that callback is hit and you get the service binder, then you can call through to the backing service.
A few other notes to help you out:
Since your Service is running in the same process as your Activity, the calls are direct and not using binder threads. This will have an effect on your Activity code.
Blocking calls should not be main on your main (UI) thread. This means if your Activity code is going to call socketService.connect(), it will need to do it from a background thread. Otherwise, you will get an exception as Android now prevents network I/O on the main thread. Other types of blocking operations can result in an ANR, which will result in your app crashing.
If your network I/O is for REST or other HTTP related traffic, look at using Retrofit or Volley as they are highly performant, extensible and deal with network and HTTP related heavy lifting for you.
I'm successfully using Permission Dispatcher to handle Marshmallow permissions.
Is there a way to use it on a static class, where I cannot access to "this" in ${className}PermissionDispatcher.${method}WitchCheck(this) ?
Working example:
#RuntimePermissions
public class ContactsMapFragment extends mFragment implements OnMapReadyCallback {
#Override
public void onMapReady(#NonNull GoogleMap googleMap) {
if (DeviceUtils.isMarshmallow())
ContactsMapFragmentPermissionsDispatcher.setMapLocationWithCheck(this, googleMap);
else
googleMap.setMyLocationEnabled(true);
}
#NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
protected void setMapLocation(#NonNull GoogleMap gmap) {
gmap.setMyLocationEnabled(true);
}
}
But in this other snippet I have a static class, so this doesn't exist:
#RuntimePermissions
public class PhoneUtils
{
#NeedsPermission(Manifest.permission.CALL_PHONE)
public static void makeCall(String szNumber)
{
Intent intent = new Intent(
Intent.ACTION_CALL, // place a phone call directly
Uri.parse("tel:" + szNumber)
);
mActivity activity = Muffin.getInstance().getActivity();
if (activity == null)
return;
if (DeviceUtils.isMarshmallow())
PhoneHelperPermissionDispatcher.startCallWithCheck(this, activity, intent);
else
activity.startActivity(intent);
}
protected void startCall(mActivity activity, Intent intent)
{
activity.startActivity(intent);
}
}
I have not used Permission Dispatcher library but I think you can pass the Activity or Context to methods of static class and can use it.
or
You can use Application class to keep context global.
for example:
public class MyDemoApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyDemoApplication .context = getApplicationContext();
}
public static Context getAppContext() {
return MyDemoApplication.context;
}
}
You have to call MyDemoApplication.getAppContext() to get your application context statically from any class file.
Note: If you are extending Application class in your project then don't forget to specify its name in application tag of manifest file.
<?xml version="1.0" encoding="utf-8"?>
<activity android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
See below links for more details:
https://developer.android.com/reference/android/app/Application.html
http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/
Static class can't be annotated because only Activity and Fragment are allowed, as Android Framework just provides requestPermission method on Activity and Fragment
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.
On Start of Application Create Instance - Working
Add data from Activities and Adapters - Working
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;
}
}
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>
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.
Please read about the Initialization-on-demand holder idiom. It's very clear and simple article about right Singleton in the Java programming language.
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
}
}