Search code examples
androidsmsbroadcastreceiver

Changing TextView from BroadcastReceiver crashes


I'm trying to have the broadcastReceiver change my TextView to the message body of the received SMS. When I use Toast to do the alert it works find but as I said Im looking to change the text view.The app launches but then crashes when a text message is received. Please not that it is not possible to directly change the textview from a class that extends broadcastreceiver. All help is appreciate :)

Here is my logcat

01-15 02:14:45.714: D/gralloc_goldfish(2551): Emulator without GPU emulation detected.
01-15 02:15:37.165: D/dalvikvm(2551): newInstance failed: no <init>()
01-15 02:15:37.165: D/AndroidRuntime(2551): Shutting down VM
01-15 02:15:37.165: W/dalvikvm(2551): threadid=1: thread exiting with uncaught exception (group=0xb0eca648)
01-15 02:15:37.165: E/AndroidRuntime(2551): FATAL EXCEPTION: main
01-15 02:15:37.165: E/AndroidRuntime(2551): java.lang.RuntimeException: Unable to instantiate receiver com.androidexample.broadcastreceiver.IncomingSms: java.lang.InstantiationException: can't instantiate class com.androidexample.broadcastreceiver.IncomingSms; no empty constructor
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.app.ActivityThread.handleReceiver(ActivityThread.java:2405)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.app.ActivityThread.access$1500(ActivityThread.java:141)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1332)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.os.Looper.loop(Looper.java:137)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.app.ActivityThread.main(ActivityThread.java:5103)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at java.lang.reflect.Method.invokeNative(Native Method)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at java.lang.reflect.Method.invoke(Method.java:525)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at dalvik.system.NativeStart.main(Native Method)
01-15 02:15:37.165: E/AndroidRuntime(2551): Caused by: java.lang.InstantiationException: can't instantiate class com.androidexample.broadcastreceiver.IncomingSms; no empty constructor
01-15 02:15:37.165: E/AndroidRuntime(2551):     at java.lang.Class.newInstanceImpl(Native Method)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at java.lang.Class.newInstance(Class.java:1130)
01-15 02:15:37.165: E/AndroidRuntime(2551):     at android.app.ActivityThread.handleReceiver(ActivityThread.java:2400)

Here is the IncomingSms class

public class IncomingSms extends BroadcastReceiver {

    final SmsManager sms = SmsManager.getDefault();
    private String senderNum;
    private String message = "No message recieved from bank";
    private TextView textView;

    public IncomingSms(TextView textView) {
        this.textView = textView;
    }

    // Get the object of SmsManager
    public void onReceive(Context context, Intent intent) {

        // Retrieves a map of extended data from the intent.
        final Bundle bundle = intent.getExtras();

        try {

            if (bundle != null) {

                final Object[] pdusObj = (Object[]) bundle.get("pdus");

                for (int i = 0; i < pdusObj.length; i++) {

                    SmsMessage currentMessage = SmsMessage
                            .createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage
                            .getDisplayOriginatingAddress();

                    senderNum = phoneNumber;
                    message = currentMessage.getDisplayMessageBody();

                    Log.i("SmsReceiver", "senderNum: " + senderNum
                            + "; message: " + message);

                    this.textView.setText(message);

                    int duration = Toast.LENGTH_LONG;
                    Toast toast = Toast.makeText(context, "senderNum: "
                            + senderNum + ", message: " + message, duration);
                    toast.show();


                } // end for loop
            } // bundle is null

        } catch (Exception e) {
            Log.e("SmsReceiver", "Exception smsReceiver" + e);

        }
    }
}

Here is my BroadcastNewSms class

public class BroadcastNewSms extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.androidexample_broadcast_newsms);
        new IncomingSms((TextView) findViewById(R.id.message));
    }

}

Solution

  • This line of code has no effect

    new IncomingSms((TextView) findViewById(R.id.message));
    

    Because when a new SMS comes in, Android will create a new instance of IncomingSms class - it will not use your instance.

    That's why you got the error - there is no default constructor for IncomingSms and Android fails to create the receiver.

    You could add one:

    public IncomingSms() {
    }
    

    But then you don't have a reference to the TextView. You should have your receiver broadcast a local intent (LocalBroadcastManager) to any UI elements to update themselves.