After resolving a startup crash, I’m now facing a new crash when attempting to initiate an outgoing call using @twilio/voice-react-native-sdk.
When I call:
Voice.connect(...)
the app crashes with the following exception:
java.lang.NullPointerException
at java.util.Objects.requireNonNull (Objects.java:233)
at com.twiliovoicereactnative.VoiceApplicationProxy.getMainActivityClass (VoiceApplicationProxy.java:122)
at com.twiliovoicereactnative.NotificationUtility.createOutgoingCallNotificationWithLowImportance (NotificationUtility.java:231)
at com.twiliovoicereactnative.VoiceService.raiseOutgoingCallNotification (VoiceService.java:316)
It appears that getMainActivityClass() returns null during outgoing call notification creation.
Dependencies
React Native: 0.79.x (Expo Prebuild)
Expo SDK: ~53.0.22
Twilio Voice SDK (Android):
com.twilio:voice-android:6.3.0
also tested with 6.2.1
@twilio/voice-react-native-sdk: ^1.7.0
Device: iQOO Neo 10
Android Version: 15
Architecture: New Architecture Enabled (Fabric + TurboModules)
📄 Relevant Android Code
MainActivity.kt
class MainActivity : ReactActivity() {
private val activityProxy = VoiceActivityProxy(
this
) { permission: String -\>
when (permission) {
Manifest.permission.RECORD_AUDIO -\> {
Toast.makeText(
this@MainActivity,
"Microphone permissions needed. Please allow in your application settings.",
Toast.LENGTH_LONG
).show()
}
Manifest.permission.BLUETOOTH_CONNECT -\> {
if (Build.VERSION.SDK_INT \>= Build.VERSION_CODES.S) {
Toast.makeText(
this@MainActivity,
"Bluetooth permissions needed. Please allow in your application settings.",
Toast.LENGTH_LONG
).show()
}
}
Manifest.permission.POST_NOTIFICATIONS -\> {
if (Build.VERSION.SDK_INT \> Build.VERSION_CODES.S_V2) {
Toast.makeText(
this@MainActivity,
"Notification permissions needed. Please allow in your application settings.",
Toast.LENGTH_LONG
).show()
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme)
super.onCreate(null)
}
override fun getMainComponentName(): String = "main"
override fun createReactActivityDelegate(): ReactActivityDelegate {
return ReactActivityDelegateWrapper(
this,
BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
object : DefaultReactActivityDelegate(
this,
mainComponentName,
fabricEnabled
) {}
)
}
override fun invokeDefaultOnBackPressed() {
if (Build.VERSION.SDK_INT \<= Build.VERSION_CODES.R) {
if (!moveTaskToBack(false)) {
super.invokeDefaultOnBackPressed()
}
return
}
super.invokeDefaultOnBackPressed()
}}
MainReactNativeHost.kt
class MainReactNativeHost(application: Application) : DefaultReactNativeHost(application) {
private val voiceReactNativeHost = object : VoiceApplicationProxy.VoiceReactNativeHost(application) {
override fun getUseDeveloperSupport(): Boolean = this@MainReactNativeHost.getUseDeveloperSupport()
override fun getPackages(): List\<ReactPackage\> = this@MainReactNativeHost.getPackages()
override fun getJSMainModuleName(): String = this@MainReactNativeHost.getJSMainModuleName()
}
override fun getUseDeveloperSupport(): Boolean {
return BuildConfig.DEBUG
}
override fun getPackages(): List\<ReactPackage\> {
val packages = PackageList(this).packages
return packages
}
override fun getJSMainModuleName(): String {
return "index"
}
fun getVoiceReactNativeHost(): VoiceApplicationProxy.VoiceReactNativeHost {
return voiceReactNativeHost
}}
MainApplication.kt
class MainApplication : Application(), ReactApplication {
private val innerReactNativeHost = object : DefaultReactNativeHost(this) {
override fun getPackages() = PackageList(this).packages
override fun getJSMainModuleName() = ".expo/.virtual-metro-entry"
override fun getUseDeveloperSupport() = BuildConfig.DEBUG
override val isNewArchEnabled = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled = BuildConfig.IS_HERMES_ENABLED
}
override val reactNativeHost: ReactNativeHost =
ReactNativeHostWrapper(this, innerReactNativeHost)
private val voiceApplicationProxy: VoiceApplicationProxy by lazy {
VoiceApplicationProxy(this)
}
override val reactHost: ReactHost
get() = DefaultReactHost.getDefaultReactHost(this.applicationContext, innerReactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) load()
voiceApplicationProxy.onCreate()
ApplicationLifecycleDispatcher.onApplicationCreate(this)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
}}
AndroidManifest.xml
<application
android:name=".MainApplication"
android:theme="@style/AppTheme"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:exported="true"
android:theme="@style/Theme.App.SplashScreen">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="com.ciptex.clientsdk"/>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
❓ Question
Is my Android configuration missing something that the Twilio Voice React Native SDK requires?
Or is there a known issue where getMainActivityClass() returns null when running in Expo Prebuild with the New Architecture enabled?
Any guidance, missing configuration, or fixes would be greatly appreciated!
Just sharing a quick update — the app is now running smoothly after updating the AndroidManifest.xml with the required permissions and intent filters.
Huge thanks to everyone who helped troubleshoot and provided suggestions! 🙌
Here what i did :
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.App.SplashScreen"
android:exported="true"
android:screenOrientation="portrait">
<!-- Deep Link Intent Filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="com.ciptex.clientsdk"/>
</intent-filter>
<!-- Proper Launcher Intent Filter -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>