我正在开发一个Android应用程序,我需要在调用状态更改期间显示浮动窗口并将事件发送到React Native应用程序。没有浮动窗口,事件发送成功,但当尝试显示浮动窗口时,我遇到以下错误:
FATAL EXCEPTION: main
Process: com.arunkavale.test, PID: 19533
java.lang.RuntimeException: Unable to start receiver com.arunkavale.test.CallReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.facebook.react.bridge.ReactApplicationContext.getSystemService(java.lang.String)' on a null object reference
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4905)
at android.app.ActivityThread.-$$Nest$mhandleReceiver(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2498)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at android.app.ActivityThread.main(ActivityThread.java:8893)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.facebook.react.bridge.ReactApplicationContext.getSystemService(java.lang.String)' on a null object reference
at com.arunkavale.test.CallModule.showFloatingWindow(CallModule.java:51)
at com.arunkavale.test.CallModule.sendEvent(CallModule.java:40)
at com.arunkavale.test.CallReceiver.sendEventToReactNative(CallReceiver.java:60)
at com.arunkavale.test.CallReceiver.onReceive(CallReceiver.java:48)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4896)
字符串
下面是一个AndroidManifest文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme">
<service
android:name=".CallBackgroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection"
tools:targetApi="q" />
<meta-data
android:name="expo.modules.updates.ENABLED"
android:value="false" />
<meta-data
android:name="expo.modules.updates.EXPO_SDK_VERSION"
android:value="49.0.0" />
<meta-data
android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH"
android:value="ALWAYS" />
<meta-data
android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS"
android:value="0" />
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.App.SplashScreen"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</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.arunkavale.test" />
</intent-filter>
</activity>
<activity
android:name="com.facebook.react.devsupport.DevSettingsActivity"
android:exported="false" />
<receiver
android:name=".CallReceiver"
android:enabled="true"
android:exported="false"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
</application>
</manifest>
型
这是一个CallReceiver
package com.arunkavale.test;
// CallReceiver.java
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.util.Log;
public class CallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null && action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state != null) {
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
// Handle incoming call
String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if (phoneNumber != null) {
sendEventToReactNative(context, "INCOMING_CALL", phoneNumber);
}
} else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
// Handle outgoing call or call in progress
String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if (phoneNumber != null) {
sendEventToReactNative(context, "OUTGOING_CALL", phoneNumber);
}
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
// Handle call ended
String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if (phoneNumber != null) {
sendEventToReactNative(context, "CALL_ENDED", phoneNumber);
}
}
}
}
}
private void sendEventToReactNative(Context context, String event, String phoneNumber) {
Log.d("CallReceiver", "sendEvent " + event + ", phoneNumber: " + phoneNumber);
// Use your custom native module to send events to React Native
CallModule.sendEvent(event, phoneNumber);
// Start the background service only if it hasn't been started yet
if (!isServiceRunning(context, CallBackgroundService.class)) {
Intent serviceIntent = new Intent(context, CallBackgroundService.class);
context.startService(serviceIntent);
}
}
private boolean isServiceRunning(Context context, Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
型
这里有一个callModule
package com.arunkavale.test;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class CallModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;
private static View overlayView;
public CallModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
@Override
public String getName() {
return "CallModule";
}
@ReactMethod
public void makeCall(String phoneNumber) {
// Implement code to make outgoing calls
}
static void sendEvent(String eventName, String phoneNumber) {
Log.d("CallModule", "sendEvent " + eventName
+ " phoneNumber " + phoneNumber);
showFloatingWindow(phoneNumber);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, phoneNumber);
}
static void showFloatingWindow(String phoneNumber) {
// Get the WindowManager service
WindowManager windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE);
// Inflate the layout for the floating window
LayoutInflater inflater = (LayoutInflater) reactContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
overlayView = inflater.inflate(R.layout.floating_window_layout, null);
// Add your custom logic to update the content of the floating window
TextView textView = overlayView.findViewById(R.id.textView);
textView.setText("Calling " + phoneNumber);
// Set window parameters
WindowManager.LayoutParams params;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
} else {
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
}
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 100;
// Add the view to the window manager
windowManager.addView(overlayView, params);
}
}
型
这里有一个callBackgroundService
package com.arunkavale.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
// CallBackgroundService.java
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class CallBackgroundService extends Service {
private TelephonyManager telephonyManager;
private CustomPhoneStateListener phoneStateListener;
private CallReceiver callReceiver;
@Override
public void onCreate() {
super.onCreate();
// Initialize anything needed for the service here
// Register the broadcast receiver for custom events
registerReceiver();
// Start listening for phone state changes
startListeningForPhoneState();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Start your background logic here
sendEventToReactNative("BACKGROUND_EVENT", "Event from background service");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
// Cleanup or release resources if needed
// Unregister the broadcast receiver and stop listening for phone state changes
unregisterReceiver();
stopListeningForPhoneState();
}
private void startListeningForPhoneState() {
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
phoneStateListener = new CustomPhoneStateListener();
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
private void stopListeningForPhoneState() {
if (telephonyManager != null && phoneStateListener != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
}
private void registerReceiver() {
callReceiver = new CallReceiver();
IntentFilter intentFilter = new IntentFilter("your-custom-event");
registerReceiver(callReceiver, intentFilter);
}
private void unregisterReceiver() {
if (callReceiver != null) {
unregisterReceiver(callReceiver);
}
}
private void sendEventToReactNative(String event, String data) {
// Use your custom native module to send events to React Native
CallModule.sendEvent(event, data);
}
// Custom PhoneStateListener to handle call state changes
private class CustomPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String phoneNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
// Handle incoming call
sendEventToReactNative("INCOMING_CALL", phoneNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// Handle outgoing call or call in progress
sendEventToReactNative("OUTGOING_CALL", phoneNumber);
break;
case TelephonyManager.CALL_STATE_IDLE:
// Handle call ended
sendEventToReactNative("CALL_ENDED", phoneNumber);
break;
}
}
}
}
型
我怀疑这个问题与ReactApplicationContext在调用状态更改期间为null有关。当尝试显示浮动窗口时,如何确保ReactApplicationContext有效?
此外,是否有更好的方法来处理调用状态更改,并在与React Native集成的Android应用程序中显示浮动窗口?
任何帮助或指导都很感激。谢谢!
1条答案
按热度按时间4jb9z9bj1#
问题似乎与分配ReactApplicationContext和示例化CallModule的顺序有关。CallModule可能未正确初始化,导致CallReceiver调用sendEventToReactNative方法时ReactApplicationContext为null。
确保CallModule在使用前初始化是解决此问题的一种方法。在尝试使用它之前,您可以更改CallModule类以确保ReactApplicationContext已设置。
这是为您更新的CallModule:
字符串
这是调用CallModule的时候了。在调用sendEvent之前,请确保您的应用程序使用有效的ReactApplicationContext调用initialize(reactContext)。您可能希望在适当的应用程序入口点或主应用程序类的onCreate方法中完成此操作。
型