Bardo91
Bardo91

Reputation: 585

Android - Unable to instanciate BroadCastReceiver

as the title says, I got a trouble with my broadcast. I got a Service that sends some of intents to the main activity (that receive those messages with the broadcast).

I got this Log:

08-03 18:23:11.581: E/AndroidRuntime(776): FATAL EXCEPTION: main
08-03 18:23:11.581: E/AndroidRuntime(776): java.lang.RuntimeException: Unable to instantiate receiver com.example.proyectjane_smartphone.Main.$IncomingBroadcaster: java.lang.ClassNotFoundException: com.example.proyectjane_smartphone.Main.$IncomingBroadcaster in loader dalvik.system.PathClassLoader[/data/app/com.example.proyectjane_smartphone-1.apk]
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.app.ActivityThread.handleReceiver(ActivityThread.java:1777)
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.app.ActivityThread.access$2400(ActivityThread.java:117)
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985)
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.os.Handler.dispatchMessage(Handler.java:99)
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.os.Looper.loop(Looper.java:130)
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.app.ActivityThread.main(ActivityThread.java:3687)
08-03 18:23:11.581: E/AndroidRuntime(776):  at java.lang.reflect.Method.invokeNative(Native Method)
08-03 18:23:11.581: E/AndroidRuntime(776):  at java.lang.reflect.Method.invoke(Method.java:507)
08-03 18:23:11.581: E/AndroidRuntime(776):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
08-03 18:23:11.581: E/AndroidRuntime(776):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
08-03 18:23:11.581: E/AndroidRuntime(776):  at dalvik.system.NativeStart.main(Native Method)
08-03 18:23:11.581: E/AndroidRuntime(776): Caused by: java.lang.ClassNotFoundException: com.example.proyectjane_smartphone.Main.$IncomingBroadcaster in loader dalvik.system.PathClassLoader[/data/app/com.example.proyectjane_smartphone-1.apk]
08-03 18:23:11.581: E/AndroidRuntime(776):  at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:240)
08-03 18:23:11.581: E/AndroidRuntime(776):  at java.lang.ClassLoader.loadClass(ClassLoader.java:551)
08-03 18:23:11.581: E/AndroidRuntime(776):  at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
08-03 18:23:11.581: E/AndroidRuntime(776):  at android.app.ActivityThread.handleReceiver(ActivityThread.java:1768)
08-03 18:23:11.581: E/AndroidRuntime(776):  ... 10 more

Here is the code of the broadcastReceiver (It's defined as an inner class, so the name in the manifest is "com.example.proyectjane_smartphone.Main.$IncomingBroadcaster", using "$"):

class IncomingBroadcaster extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(ACTION_CHANGE_TEXT_IO)) {
            textIO.setText("OUTUT: " + intent.getStringExtra("text"));
        } else if (action.equals(ACTION_SPEAK)) {
            JANEvoi.speak(intent.getStringExtra("speak"));
        } else if (action.equals(ACTION_CHANGE_LIGHT)) {
            if (intent.getBooleanExtra("light", false)) {
                connLight.setImageResource(R.drawable.lighton);
            } else {
                connLight.setImageResource(R.drawable.lightoff);
            }
        } else if (action.equals(ACTION_ENABLE_BT)) {
            Intent enableBtIntent = new Intent(
                    BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent,
                    BluetoothManager.REQUEST_ENABLE_BT);
        }
    }
}

I need the broadcast to be defined as an inner class, becouse it changes some texts and call a "activityforResult", and it's only possible in the main activity.

The calls are made for the service, here is an example that shoot the exception:

public void enableBluetoothAdapter() {
        if (blueManager.getAdapter() == null) {
            Log.d("BLUETOOTHMANAGER", "There's no bluetooth adapter");
        } else if (!blueManager.getAdapter().isEnabled()) {
            Intent enableBTIntent = new Intent(Main.ACTION_ENABLE_BT);
            sendBroadcast(enableBTIntent);
            Log.d("BLUETOOTH", "BLUETOOTH ENCENDIDO");
        }
    }

What I cant understand, is that (For example with "ACTION_ENABLE_BT") the intent is send and received, becouse while the activity is crashing the "popup-menu" that activate the bluetooth appears... So The broadcast crash before was used (In spite of receiving an error saying that the broadcast cannot be instanciated...).

Finally here is the Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.proyectjane_smartphone"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="10" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.proyectjane_smartphone.Main"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name="com.example.proyectjane_smartphone.Main.$IncomingBroadcaster"
            android:exported="false" >
            <intent-filter>
                <action android:name="es.jane.SpeakAction" />
                <action android:name="es.jane.ChangeTextIOAction" />
                <action android:name="es.jane.ChangeLight" />
                <action android:name="es.jane.EnableBT" />
            </intent-filter>
        </receiver>

        <service android:name="com.example.proyectjane_smartphone.Bluetooth.BluetoothService" >
        </service>
    </application>

</manifest>

I was searching but I didn't find the solution. Thanks in advance

Upvotes: 1

Views: 649

Answers (3)

Bardo91
Bardo91

Reputation: 585

The trouble was thar if I use the BroadcastReceiver, it's neednt to be declarated in the Manifest. Then I declare the broadcast with the following code in the activity:

private BroadcastReceiver inBroadcast = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(ACTION_CHANGE_TEXT_IO)) {
                textIO.setText("OUTUT: " + intent.getStringExtra("text"));
            } else if (action.equals(ACTION_SPEAK)) {
                JANEvoi.speak(intent.getStringExtra("speak"));
            } else if (action.equals(ACTION_CHANGE_LIGHT)) {
                if (intent.getBooleanExtra("light", false)) {
                    connLight.setImageResource(R.drawable.lighton);
                } else {
                    connLight.setImageResource(R.drawable.lightoff);
                }
            } else if (action.equals(ACTION_ENABLE_BT)) {
                Intent enableBtIntent = new Intent(
                        BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent,
                        BluetoothManager.REQUEST_ENABLE_BT);
            }
        }
    };

And finally, it works!

Upvotes: 0

Rich Schuler
Rich Schuler

Reputation: 41972

Even if you fixed the class name it still wouldn't work since the BroadcastReceiver is not a static inner class. An instance of the activity would be required to instantiate it and that isn't possible with how you can define it in the Manifest.

The proper way to use a BroadcastReceiver that works on behalf of an Activity is to register and unregister it in Activity#onResume and Activity#onPause. This way the receiver is listening for events when the Activity is active and can interact with it properly as an inner class.

In onCreate() you need to create an instance of your receiver then in onResume() you'd need something similar to:

final IntentFilter filter = new IntentFilter();
this.populateFilter(filter);
filter.addAction("es.jane.SpeakAction");
...
registerReceiver(this.receiver, filter);

And in onPause():

unregisterReceiver(this.receiver)

Upvotes: 1

esentsov
esentsov

Reputation: 6522

You cannot register an inner class as a receiver (because such class can be instantiated only through instance of outer class, ie such class doesn't have a default constructor). However you can dynamicly register an instance of the class in your activity: link

Upvotes: 1

Related Questions