我正在升级 Flutter 库,以使用与 ConnectionService 集成的 Twilio Voice 进行本机呼叫管理。为了实现这一点,必须扩展
ConnectionService
(从 Service
继承并具有相关的通信方法,即 Intent
s)
要接收来电(通过 FCM 等通知管理器),需要使用
通知
ConnectionService
有来电
override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
super.onCreateIncomingConnection(connectionManagerPhoneAccount, request)
Log.d(TAG, "onCreateIncomingConnection")'
// ... code goes here
// return new Connection(...)
}
有了这个
onCreateIncomingConnection
,它将要求本机“电话”应用程序显示带有您提供的参数的来电,例如使用 Connection.setAddress 显示来电号码
可以通过使用密钥
TelecomManager.EXTRA_INCOMING_CALL_EXTRAS(对于来电)将其添加到额外
ConnectionService
,将这些附加信息传递到 Bundle
,然后将其添加到新的 Bundle
,例如
private fun handleFCMCallInvite(callInvite: CallInvite, notificationId: Int) {
// Get telecom manager
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
// Get PhoneAccountHandle
val caller = callInvite.from!!.toString()
val componentName = ComponentName(applicationContext.packageName, TwilioVoiceConnectionService::class.java.name)
val phoneAccountHandle = PhoneAccountHandle(componentName, caller)
// Create my Bundle containing information e.g. notificationId and callInvite
val myBundle = Bundle()
myBundle.putInt(Constants.INCOMING_CALL_NOTIFICATION_ID, notificationId)
myBundle.putParcelable(Constants.INCOMING_CALL_INVITE, callInvite)
// Add new incoming call to the telecom manager
telecomManager.addNewIncomingCall(phoneAccountHandle, Bundle().apply {
putBundle(EXTRA_INCOMING_CALL_EXTRAS, myBundle)
putParcelable(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
})
}
当尝试从
unparcel()
的 onCreateIncomingConnection实现中的 Bundled extras 中获取
CallInvite
时,就会出现问题,如下所示:
ConnectionService
如上面的代码片段所示,每当使用有效密钥调用
override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
super.onCreateIncomingConnection(connectionManagerPhoneAccount, request)
Log.d(TAG, "onCreateIncomingConnection")
val connection: Connection = VoipConnection(applicationContext)
connection.extras = request?.extras
var ci: CallInvite? = null
val myBundle: Bundle? = connection.extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
if(myBundle != null) {
Log.d(TAG, "onCreateIncomingConnection: myBundle is not null")
/// --------------------------------------------------------
/// This next line throws the ClassNotFoundException occurs
/// --------------------------------------------------------
if (myBundle.containsKey(Constants.INCOMING_CALL_INVITE) ) {
Log.d(TAG, "onCreateIncomingConnection: myBundle contains INCOMING_CALL_INVITE")
ci = myBundle.getParcelable(Constants.INCOMING_CALL_INVITE)
} else {
Log.d(TAG, "onCreateIncomingConnection: myBundle does not contain INCOMING_CALL_INVITE")
}
} else {
Log.d(TAG, "onCreateIncomingConnection: myBundle is null")
}
...
}
时,我都会绑定一个
CallInvite对象。
myBundle.containsKey()
,包含和访问器按预期工作。将 notificationId
包含在捆绑包中时,出现以下异常:
*注:CallInvite
实现
CallInvite
Parcelable
图书馆等Class not found when unmarshalling: com.twilio.voice.CallInvite
java.lang.ClassNotFoundException: com.twilio.voice.CallInvite
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:454)
at android.os.Parcel.readParcelableCreator(Parcel.java:3403)
at android.os.Parcel.readParcelable(Parcel.java:3337)
at android.os.Parcel.readValue(Parcel.java:3239)
at android.os.Parcel.readArrayMapInternal(Parcel.java:3636)
at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
at android.os.BaseBundle.unparcel(BaseBundle.java:236)
at android.os.BaseBundle.containsKey(BaseBundle.java:516)
at com.twilio.twilio_voice.connectionservice.TwilioVoiceConnectionService.onCreateIncomingConnection(TwilioVoiceConnectionService.kt:43)
at android.telecom.ConnectionService.createConnection(ConnectionService.java:2061)
at android.telecom.ConnectionService.access$400(ConnectionService.java:96)
at android.telecom.ConnectionService$2$1.loggedRun(ConnectionService.java:914)
at android.telecom.Logging.Runnable$1.run(Runnable.java:37)
at android.telecom.ConnectionService.onAccountsInitialized(ConnectionService.java:3272)
at android.telecom.ConnectionService.access$5000(ConnectionService.java:96)
at android.telecom.ConnectionService$5$1.loggedRun(ConnectionService.java:2577)
at android.telecom.Logging.Runnable$1.run(Runnable.java:37)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8663)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by: java.lang.ClassNotFoundException: com.twilio.voice.CallInvite
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:1358)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:1418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:454)
at android.os.Parcel.readParcelableCreator(Parcel.java:3403)
at android.os.Parcel.readParcelable(Parcel.java:3337)
at android.os.Parcel.readValue(Parcel.java:3239)
at android.os.Parcel.readArrayMapInternal(Parcel.java:3636)
at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
at android.os.BaseBundle.unparcel(BaseBundle.java:236)
at android.os.BaseBundle.containsKey(BaseBundle.java:516)
at com.twilio.twilio_voice.connectionservice.TwilioVoiceConnectionService.onCreateIncomingConnection(TwilioVoiceConnectionService.kt:43)
at android.telecom.ConnectionService.createConnection(ConnectionService.java:2061)
at android.telecom.ConnectionService.access$400(ConnectionService.java:96)
at android.telecom.ConnectionService$2$1.loggedRun(ConnectionService.java:914)
at android.telecom.Logging.Runnable$1.run(Runnable.java:37)
at android.telecom.ConnectionService.onAccountsInitialized(ConnectionService.java:3272)
at android.telecom.ConnectionService.access$5000(ConnectionService.java:96)
at android.telecom.ConnectionService$5$1.loggedRun(ConnectionService.java:2577)
at android.telecom.Logging.Runnable$1.run(Runnable.java:37)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8663)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
(模块)
build.gradle
更新1buildscript {
ext.kotlin_version = '1.8.0'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
...
android {
compileSdk 33
...
}
...
dependencies {
...
implementation("com.twilio:voice-android:6.2.1")
implementation("com.google.firebase:firebase-messaging-ktx:23.2.1")
...
}
Kotlin 也会发生同样的情况,尽管我得到
public class VoiceFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(final RemoteMessage remoteMessage) {
// ... valid message
handleInvite(callInvite, notificationId);
//...
}
private void handleInvite(CallInvite callInvite, int notificationId) {
Parcel p = Parcel.obtain();
p.writeParcelable(callInvite, 0);
ClassLoader classLoader = getApplicationContext().getClassLoader();
CallInvite ci = p.readParcelable(classLoader); // <----------------- ci is always null
if(ci != null){
Log.d(TAG, "handleInvite: " + ci.getCallSid());
} else {
Log.d(TAG, "handleInvite: null");
}
}
}
表示
ClassNotFoundException
。到目前为止,我已将问题追溯到 Parcel.readParcelableCreator(@Nullable ClassLoader)(适用于 Android 11,API 30)
CallInvite
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
String name = readString();
if (name == null) {
return null;
}
Parcelable.Creator<?> creator;
始终为 null,因此在调用
name
时返回 null。Android 11 和 12(API 30、31)上都会出现这种情况
上详细探讨的一个问题记录了一个 Parcel 问题,并提供了有关问题原因的一些见解。 原因是当前线程的
Parcel.readParcelable
与初始化(加载)语音 SDK 的类加载器不同,正如
ClassLoader
在他的评论这里中所建议的那样 SO 的 David Wasser 对此进行了确认和详细讨论。读取包时添加简单的行即可添加要找到的类,即
@afalls-twilio