在调用状态更改期间,无法在Android应用程序中显示浮动窗口

mu0hgdu0  于 2024-01-04  发布在  Android
关注(0)|答案(1)|浏览(129)

我正在开发一个Android应用程序,我需要在调用状态更改期间显示浮动窗口并将事件发送到React Native应用程序。没有浮动窗口,事件发送成功,但当尝试显示浮动窗口时,我遇到以下错误:

  1. FATAL EXCEPTION: main
  2. Process: com.arunkavale.test, PID: 19533
  3. 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
  4. at android.app.ActivityThread.handleReceiver(ActivityThread.java:4905)
  5. at android.app.ActivityThread.-$$Nest$mhandleReceiver(Unknown Source:0)
  6. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2498)
  7. at android.os.Handler.dispatchMessage(Handler.java:106)
  8. at android.os.Looper.loopOnce(Looper.java:230)
  9. at android.os.Looper.loop(Looper.java:319)
  10. at android.app.ActivityThread.main(ActivityThread.java:8893)
  11. at java.lang.reflect.Method.invoke(Native Method)
  12. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
  13. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
  14. 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
  15. at com.arunkavale.test.CallModule.showFloatingWindow(CallModule.java:51)
  16. at com.arunkavale.test.CallModule.sendEvent(CallModule.java:40)
  17. at com.arunkavale.test.CallReceiver.sendEventToReactNative(CallReceiver.java:60)
  18. at com.arunkavale.test.CallReceiver.onReceive(CallReceiver.java:48)
  19. at android.app.ActivityThread.handleReceiver(ActivityThread.java:4896)

字符串
下面是一个AndroidManifest文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools">
  4. <uses-feature
  5. android:name="android.hardware.telephony"
  6. android:required="false" />
  7. <uses-permission android:name="android.permission.INTERNET" />
  8. <uses-permission
  9. android:name="android.permission.READ_EXTERNAL_STORAGE"
  10. android:maxSdkVersion="32" />
  11. <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  12. <uses-permission android:name="android.permission.VIBRATE" />
  13. <uses-permission android:name="android.permission.READ_CALL_LOG" />
  14. <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  15. <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
  16. <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
  17. <uses-permission android:name="android.permission.WAKE_LOCK" />
  18. <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
  19. <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  20. <queries>
  21. <intent>
  22. <action android:name="android.intent.action.VIEW" />
  23. <category android:name="android.intent.category.BROWSABLE" />
  24. <data android:scheme="https" />
  25. </intent>
  26. </queries>
  27. <application
  28. android:name=".MainApplication"
  29. android:allowBackup="true"
  30. android:icon="@mipmap/ic_launcher"
  31. android:label="@string/app_name"
  32. android:roundIcon="@mipmap/ic_launcher_round"
  33. android:theme="@style/AppTheme">
  34. <service
  35. android:name=".CallBackgroundService"
  36. android:enabled="true"
  37. android:exported="false"
  38. android:foregroundServiceType="mediaProjection"
  39. tools:targetApi="q" />
  40. <meta-data
  41. android:name="expo.modules.updates.ENABLED"
  42. android:value="false" />
  43. <meta-data
  44. android:name="expo.modules.updates.EXPO_SDK_VERSION"
  45. android:value="49.0.0" />
  46. <meta-data
  47. android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH"
  48. android:value="ALWAYS" />
  49. <meta-data
  50. android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS"
  51. android:value="0" />
  52. <activity
  53. android:name=".MainActivity"
  54. android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
  55. android:exported="true"
  56. android:label="@string/app_name"
  57. android:launchMode="singleTask"
  58. android:screenOrientation="portrait"
  59. android:theme="@style/Theme.App.SplashScreen"
  60. android:windowSoftInputMode="adjustResize">
  61. <intent-filter>
  62. <action android:name="android.intent.action.MAIN" />
  63. <category android:name="android.intent.category.LAUNCHER" />
  64. </intent-filter>
  65. <intent-filter>
  66. <action android:name="android.intent.action.VIEW" />
  67. <category android:name="android.intent.category.DEFAULT" />
  68. <category android:name="android.intent.category.BROWSABLE" />
  69. <data android:scheme="com.arunkavale.test" />
  70. </intent-filter>
  71. </activity>
  72. <activity
  73. android:name="com.facebook.react.devsupport.DevSettingsActivity"
  74. android:exported="false" />
  75. <receiver
  76. android:name=".CallReceiver"
  77. android:enabled="true"
  78. android:exported="false"
  79. tools:ignore="Instantiatable">
  80. <intent-filter>
  81. <action android:name="android.intent.action.PHONE_STATE" />
  82. </intent-filter>
  83. </receiver>
  84. </application>
  85. </manifest>


这是一个CallReceiver

  1. package com.arunkavale.test;
  2. // CallReceiver.java
  3. import android.app.ActivityManager;
  4. import android.content.BroadcastReceiver;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.telephony.TelephonyManager;
  8. import android.util.Log;
  9. import android.widget.Toast;
  10. import android.content.BroadcastReceiver;
  11. import android.content.Context;
  12. import android.content.Intent;
  13. import android.telephony.TelephonyManager;
  14. import android.util.Log;
  15. public class CallReceiver extends BroadcastReceiver {
  16. @Override
  17. public void onReceive(Context context, Intent intent) {
  18. String action = intent.getAction();
  19. if (action != null && action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
  20. String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
  21. if (state != null) {
  22. if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
  23. // Handle incoming call
  24. String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
  25. if (phoneNumber != null) {
  26. sendEventToReactNative(context, "INCOMING_CALL", phoneNumber);
  27. }
  28. } else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
  29. // Handle outgoing call or call in progress
  30. String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
  31. if (phoneNumber != null) {
  32. sendEventToReactNative(context, "OUTGOING_CALL", phoneNumber);
  33. }
  34. } else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
  35. // Handle call ended
  36. String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
  37. if (phoneNumber != null) {
  38. sendEventToReactNative(context, "CALL_ENDED", phoneNumber);
  39. }
  40. }
  41. }
  42. }
  43. }
  44. private void sendEventToReactNative(Context context, String event, String phoneNumber) {
  45. Log.d("CallReceiver", "sendEvent " + event + ", phoneNumber: " + phoneNumber);
  46. // Use your custom native module to send events to React Native
  47. CallModule.sendEvent(event, phoneNumber);
  48. // Start the background service only if it hasn't been started yet
  49. if (!isServiceRunning(context, CallBackgroundService.class)) {
  50. Intent serviceIntent = new Intent(context, CallBackgroundService.class);
  51. context.startService(serviceIntent);
  52. }
  53. }
  54. private boolean isServiceRunning(Context context, Class<?> serviceClass) {
  55. ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  56. for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
  57. if (serviceClass.getName().equals(service.service.getClassName())) {
  58. return true;
  59. }
  60. }
  61. return false;
  62. }
  63. }


这里有一个callModule

  1. package com.arunkavale.test;
  2. import android.content.Context;
  3. import android.graphics.PixelFormat;
  4. import android.os.Build;
  5. import android.util.Log;
  6. import android.view.Gravity;
  7. import android.view.LayoutInflater;
  8. import android.view.View;
  9. import android.view.WindowManager;
  10. import android.widget.TextView;
  11. import com.facebook.react.bridge.ReactApplicationContext;
  12. import com.facebook.react.bridge.ReactContextBaseJavaModule;
  13. import com.facebook.react.bridge.ReactMethod;
  14. import com.facebook.react.modules.core.DeviceEventManagerModule;
  15. public class CallModule extends ReactContextBaseJavaModule {
  16. private static ReactApplicationContext reactContext;
  17. private static View overlayView;
  18. public CallModule(ReactApplicationContext context) {
  19. super(context);
  20. reactContext = context;
  21. }
  22. @Override
  23. public String getName() {
  24. return "CallModule";
  25. }
  26. @ReactMethod
  27. public void makeCall(String phoneNumber) {
  28. // Implement code to make outgoing calls
  29. }
  30. static void sendEvent(String eventName, String phoneNumber) {
  31. Log.d("CallModule", "sendEvent " + eventName
  32. + " phoneNumber " + phoneNumber);
  33. showFloatingWindow(phoneNumber);
  34. reactContext
  35. .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
  36. .emit(eventName, phoneNumber);
  37. }
  38. static void showFloatingWindow(String phoneNumber) {
  39. // Get the WindowManager service
  40. WindowManager windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE);
  41. // Inflate the layout for the floating window
  42. LayoutInflater inflater = (LayoutInflater) reactContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  43. overlayView = inflater.inflate(R.layout.floating_window_layout, null);
  44. // Add your custom logic to update the content of the floating window
  45. TextView textView = overlayView.findViewById(R.id.textView);
  46. textView.setText("Calling " + phoneNumber);
  47. // Set window parameters
  48. WindowManager.LayoutParams params;
  49. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  50. params = new WindowManager.LayoutParams(
  51. WindowManager.LayoutParams.WRAP_CONTENT,
  52. WindowManager.LayoutParams.WRAP_CONTENT,
  53. WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
  54. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  55. PixelFormat.TRANSLUCENT);
  56. } else {
  57. params = new WindowManager.LayoutParams(
  58. WindowManager.LayoutParams.WRAP_CONTENT,
  59. WindowManager.LayoutParams.WRAP_CONTENT,
  60. WindowManager.LayoutParams.TYPE_PHONE,
  61. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  62. PixelFormat.TRANSLUCENT);
  63. }
  64. params.gravity = Gravity.TOP | Gravity.START;
  65. params.x = 0;
  66. params.y = 100;
  67. // Add the view to the window manager
  68. windowManager.addView(overlayView, params);
  69. }
  70. }


这里有一个callBackgroundService

  1. package com.arunkavale.test;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.IBinder;
  5. import android.util.Log;
  6. // CallBackgroundService.java
  7. import android.app.Service;
  8. import android.content.BroadcastReceiver;
  9. import android.content.Context;
  10. import android.content.Intent;
  11. import android.content.IntentFilter;
  12. import android.os.IBinder;
  13. import android.telephony.PhoneStateListener;
  14. import android.telephony.TelephonyManager;
  15. import android.util.Log;
  16. public class CallBackgroundService extends Service {
  17. private TelephonyManager telephonyManager;
  18. private CustomPhoneStateListener phoneStateListener;
  19. private CallReceiver callReceiver;
  20. @Override
  21. public void onCreate() {
  22. super.onCreate();
  23. // Initialize anything needed for the service here
  24. // Register the broadcast receiver for custom events
  25. registerReceiver();
  26. // Start listening for phone state changes
  27. startListeningForPhoneState();
  28. }
  29. @Override
  30. public int onStartCommand(Intent intent, int flags, int startId) {
  31. // Start your background logic here
  32. sendEventToReactNative("BACKGROUND_EVENT", "Event from background service");
  33. return START_STICKY;
  34. }
  35. @Override
  36. public IBinder onBind(Intent intent) {
  37. return null;
  38. }
  39. @Override
  40. public void onDestroy() {
  41. super.onDestroy();
  42. // Cleanup or release resources if needed
  43. // Unregister the broadcast receiver and stop listening for phone state changes
  44. unregisterReceiver();
  45. stopListeningForPhoneState();
  46. }
  47. private void startListeningForPhoneState() {
  48. telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  49. phoneStateListener = new CustomPhoneStateListener();
  50. telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
  51. }
  52. private void stopListeningForPhoneState() {
  53. if (telephonyManager != null && phoneStateListener != null) {
  54. telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
  55. }
  56. }
  57. private void registerReceiver() {
  58. callReceiver = new CallReceiver();
  59. IntentFilter intentFilter = new IntentFilter("your-custom-event");
  60. registerReceiver(callReceiver, intentFilter);
  61. }
  62. private void unregisterReceiver() {
  63. if (callReceiver != null) {
  64. unregisterReceiver(callReceiver);
  65. }
  66. }
  67. private void sendEventToReactNative(String event, String data) {
  68. // Use your custom native module to send events to React Native
  69. CallModule.sendEvent(event, data);
  70. }
  71. // Custom PhoneStateListener to handle call state changes
  72. private class CustomPhoneStateListener extends PhoneStateListener {
  73. @Override
  74. public void onCallStateChanged(int state, String phoneNumber) {
  75. switch (state) {
  76. case TelephonyManager.CALL_STATE_RINGING:
  77. // Handle incoming call
  78. sendEventToReactNative("INCOMING_CALL", phoneNumber);
  79. break;
  80. case TelephonyManager.CALL_STATE_OFFHOOK:
  81. // Handle outgoing call or call in progress
  82. sendEventToReactNative("OUTGOING_CALL", phoneNumber);
  83. break;
  84. case TelephonyManager.CALL_STATE_IDLE:
  85. // Handle call ended
  86. sendEventToReactNative("CALL_ENDED", phoneNumber);
  87. break;
  88. }
  89. }
  90. }
  91. }


我怀疑这个问题与ReactApplicationContext在调用状态更改期间为null有关。当尝试显示浮动窗口时,如何确保ReactApplicationContext有效?
此外,是否有更好的方法来处理调用状态更改,并在与React Native集成的Android应用程序中显示浮动窗口?
任何帮助或指导都很感激。谢谢!

4jb9z9bj

4jb9z9bj1#

问题似乎与分配ReactApplicationContext和示例化CallModule的顺序有关。CallModule可能未正确初始化,导致CallReceiver调用sendEventToReactNative方法时ReactApplicationContext为null。
确保CallModule在使用前初始化是解决此问题的一种方法。在尝试使用它之前,您可以更改CallModule类以确保ReactApplicationContext已设置。

这是为您更新的CallModule:

  1. package com.arunkavale.test;
  2. import android.content.Context;
  3. import android.graphics.PixelFormat;
  4. import android.os.Build;
  5. import android.util.Log;
  6. import android.view.Gravity;
  7. import android.view.LayoutInflater;
  8. import android.view.View;
  9. import android.view.WindowManager;
  10. import android.widget.TextView;
  11. import com.facebook.react.bridge.ReactApplicationContext;
  12. import com.facebook.react.bridge.ReactContextBaseJavaModule;
  13. import com.facebook.react.bridge.ReactMethod;
  14. import com.facebook.react.modules.core.DeviceEventManagerModule;
  15. public class CallModule extends ReactContextBaseJavaModule {
  16. private static ReactApplicationContext reactContext;
  17. private static View overlayView;
  18. public CallModule(ReactApplicationContext context) {
  19. super(context);
  20. reactContext = context;
  21. }
  22. @Override
  23. public String getName() {
  24. return "CallModule";
  25. }
  26. @ReactMethod
  27. public void makeCall(String phoneNumber) {
  28. // Implement code to make outgoing calls
  29. }
  30. static void initialize(ReactApplicationContext context) {
  31. reactContext = context;
  32. }
  33. static void sendEvent(String eventName, String phoneNumber) {
  34. Log.d("CallModule", "sendEvent " + eventName + " phoneNumber " + phoneNumber);
  35. // Ensure that the ReactApplicationContext is not null
  36. if (reactContext == null) {
  37. Log.e("CallModule", "ReactApplicationContext is null. CallModule may not have been properly initialized.");
  38. return;
  39. }
  40. showFloatingWindow(phoneNumber);
  41. reactContext
  42. .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
  43. .emit(eventName, phoneNumber);
  44. }
  45. static void showFloatingWindow(String phoneNumber) {
  46. // Get the WindowManager service
  47. WindowManager windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE);
  48. // Inflate the layout for the floating window
  49. LayoutInflater inflater = (LayoutInflater) reactContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  50. overlayView = inflater.inflate(R.layout.floating_window_layout, null);
  51. // Add your custom logic to update the content of the floating window
  52. TextView textView = overlayView.findViewById(R.id.textView);
  53. textView.setText("Calling " + phoneNumber);
  54. // Set window parameters
  55. WindowManager.LayoutParams params;
  56. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  57. params = new WindowManager.LayoutParams(
  58. WindowManager.LayoutParams.WRAP_CONTENT,
  59. WindowManager.LayoutParams.WRAP_CONTENT,
  60. WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
  61. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  62. PixelFormat.TRANSLUCENT);
  63. } else {
  64. params = new WindowManager.LayoutParams(
  65. WindowManager.LayoutParams.WRAP_CONTENT,
  66. WindowManager.LayoutParams.WRAP_CONTENT,
  67. WindowManager.LayoutParams.TYPE_PHONE,
  68. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  69. PixelFormat.TRANSLUCENT);
  70. }
  71. params.gravity = Gravity.TOP | Gravity.START;
  72. params.x = 0;
  73. params.y = 100;
  74. // Add the view to the window manager
  75. windowManager.addView(overlayView, params);
  76. }
  77. }

字符串
这是调用CallModule的时候了。在调用sendEvent之前,请确保您的应用程序使用有效的ReactApplicationContext调用initialize(reactContext)。您可能希望在适当的应用程序入口点或主应用程序类的onCreate方法中完成此操作。

  1. @Override
  2. public void onCreate() {
  3. super.onCreate();
  4. // Other initialization code
  5. CallModule.initialize(new ReactApplicationContext(this));
  6. }

展开查看全部

相关问题