Question

In my app I have a broadcast receiver that turns on GPS upon receiving a set string of text. In the onLocationChanged method, I want to pass the GPS data and a value from my shared preferences to a thread in a string.
I have the thread writing to log and can see all the GPS values in the string but the last value from my shared preferences is just showing up as 'prefPhoneNum' which I initialised the string to at the beginning of the receiver class. I have the same code to read the prefPhoneNum from shared preferences in the main class and it works there, can anyone see what I might be doing wrong?

public class SmsReceiver extends BroadcastReceiver implements LocationListener
{
     LocationManager lm;
     LocationListener loc;
     public SharedPreferences sharedpreferences;    
     public static final String US = "usersettings";
     public String prefPhoneNum = "prefPhoneNum";

    @Override
    public void onReceive(Context context, Intent intent) 
    {    
        sharedpreferences = context.getSharedPreferences(US, Context.MODE_PRIVATE);
        prefPhoneNum = sharedpreferences.getString("prefPhoneNum" , "");
        lm = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        loc = new SmsReceiver();

        //---get the SMS message passed in---
        Bundle bundle = intent.getExtras();   
        SmsMessage[] msgs = null;
        String str = ""; 

        if (bundle != null)
        {
            //---retrieve the SMS message received---
            Object[] pdus = (Object[]) bundle.get("pdus");
            msgs = new SmsMessage[pdus.length];            
            for (int i=0; i<msgs.length; i++)
            {
                msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                str += msgs[i].getMessageBody().toString() + "\n";        
            } 

             Toast.makeText(context, str, Toast.LENGTH_SHORT).show();     //Display SMS

            if ((msgs[0].getMessageBody().toString().equals("Enable")) ||
                    (msgs[0].getMessageBody().toString().equals("enable")))
            {             
                enableGPS();
            }    
            else {  /* Do Nothing*/ }
        }              
    }

    public void enableGPS() {  
      //new CountDownTimer(10000, 1000) {       //10 seconds
        new CountDownTimer(300000, 1000) {      //300 secs = 5 mins
            public void onTick(long millisUntilFinished) 
            {
               lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, loc);  
            }
            public void onFinish() 
            {                   
               lm.removeUpdates(loc);       
            }
       }.start();          
    }    

    @Override
    public void onLocationChanged(Location location) {
      String s = "";
         s += location.getLatitude()  + "\n";
         s += location.getLongitude() + "\n";
         s += location.getAltitude()  + "\n";
         s += location.getAccuracy()  + "\n" + prefPhoneNum;

         Thread cThread = new Thread(new SocketsClient(s));
         cThread.start();
   }

    @Override
    public void onProviderDisabled(String provider) {   }
    @Override
    public void onProviderEnabled(String provider)  {   }
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {   }
}  

Here is the logcat for when the application shuts -

D/LocationManager( 3912): requestLocationUpdates: provider = gps, listener = accel.working.TrackGPS@4628bce0  
D/GpsLocationProvider(   96): setMinTime 0  
D/GpsLocationProvider(   96): startNavigating  
D/GpsLocationProvider(   96): TTFF: 3227  
D/AndroidRuntime( 3912): Shutting down VM  
W/dalvikvm( 3912): threadid=1: thread exiting with uncaught exception (group=0x400259f8)  
E/AndroidRuntime( 3912): FATAL EXCEPTION: main  
E/AndroidRuntime( 3912): java.lang.NullPointerException  
E/AndroidRuntime( 3912):    at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:146)  
E/AndroidRuntime( 3912):    at accel.working.TrackGPS.onLocationChanged(TrackGPS.java:63)  
E/AndroidRuntime( 3912):    at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:191)  
E/AndroidRuntime( 3912):    at android.location.LocationManager$ListenerTransport.access$000(LocationManager.java:124)  
E/AndroidRuntime( 3912):    at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:140)  
E/AndroidRuntime( 3912):    at android.os.Handler.dispatchMessage(Handler.java:99)  
E/AndroidRuntime( 3912):    at android.os.Looper.loop(Looper.java:144)  
E/AndroidRuntime( 3912):    at android.app.ActivityThread.main(ActivityThread.java:4937)  
E/AndroidRuntime( 3912):    at java.lang.reflect.Method.invokeNative(Native Method)  
E/AndroidRuntime( 3912):    at java.lang.reflect.Method.invoke(Method.java:521)  
E/AndroidRuntime( 3912):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)  
E/AndroidRuntime( 3912):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)  
E/AndroidRuntime( 3912):    at dalvik.system.NativeStart.main(Native Method)  
Was it helpful?

Solution

You're doing WAY too much in our onReceieve(). From the docs http://developer.android.com/reference/android/content/BroadcastReceiver.html#ReceiverLifecycle :

A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.

This has important repercussions to what you can do in an onReceive(Context, Intent) implementation: anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.

In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the NotificationManager API. For the latter, you can use Context.startService() to send a command to the service.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top