首页 > 解决方案 > 带有 React Native 和 Headless JS 的 Socket IO,无法杀死它

问题描述

这是我的第一篇文章,所以我将尝试以清晰的方式展示我的问题,而且我不是具有基本 java 知识的 android 开发人员。

我正在尝试在我的 react-native 应用程序中运行套接字 IO 客户端,因为我询问为了实现这一点,我使用 Headless JS 任务让这个套接字在后台运行,即使应用程序关闭,用户也会收到一个通知。

我的所有流程都运行良好,但唯一的问题是当我执行时Background.stopService(),websocket 不会断开连接,但持久性通知会消失。

index.js(我注册任务的地方)

const MyHeadlessTask = async () => {
  const email = await AsyncStorage.getItem('email');

  if (!!email) {
    try {
      let socket = SocketIOClient('url', {
        transports: ['websocket'],
        query: `email=${email}`
      });

      etc...
    } catch (ex) {
      console.log('[MyHeadlessTask() ex] ', ex)
    }
  }
};

AppRegistry.registerHeadlessTask('Background', () => MyHeadlessTask);

背景事件服务.java

public class BackgroundEventService extends HeadlessJsTaskService {
  @Nullable
  protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
    Bundle extras = intent.getExtras();
    WritableMap data = extras != null ? Arguments.fromBundle(extras) : Arguments.createMap();

    return new HeadlessJsTaskConfig(
      "Background",
      data,
      5000,
      true
    );
  }
}

背景模块.java

public class BackgroundModule extends ReactContextBaseJavaModule {

  public static final String REACT_CLASS = "Background";
  private static ReactApplicationContext reactContext;

  public BackgroundModule(@Nonnull ReactApplicationContext reactContext) {
    super(reactContext);
    this.reactContext = reactContext;
  }

  @Nonnull
  @Override
  public String getName() {
    return REACT_CLASS;
  }

  @ReactMethod
  public void startService() {
    this.reactContext.startService(new Intent(this.reactContext, BackgroundService.class));
  }

  @ReactMethod
  public void stopService() {
    this.reactContext.stopService(new Intent(this.reactContext, BackgroundService.class));
  }
}

背景包.java

public class BackgroundPackage implements ReactPackage {

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return Arrays.<NativeModule>asList(
      new BackgroundModule(reactContext)
    );
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
}

MainApplication.java(添加包)

@Override
      protected List<ReactPackage> getPackages() {
        @SuppressWarnings("UnnecessaryLocalVariable")
        List<ReactPackage> packages = new PackageList(this).getPackages();
        // Packages that cannot be autolinked yet can be added manually here, for example:
        // packages.add(new MyReactNativePackage());
        packages.add(new BackgroundPackage());
        return packages;
      }

这是我第一个编写BackgroundService.java的两种方式

public class BackgroundService extends Service {

  private static final int SERVICE_NOTIFICATION_ID = 12345;
  private static final String CHANNEL_ID = "BACKGROUND";

  private Handler handler = new Handler();
  private Runnable runnableCode = new Runnable() {
    @Override
    public void run() {
      Context context = getApplicationContext();
      Intent myIntent = new Intent(context, BackgroundEventService.class);
      context.startService(myIntent);
      HeadlessJsTaskService.acquireWakeLockNow(context);
      //handler.postDelayed(this, 2000);
    }
  };
  private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      int importance = NotificationManager.IMPORTANCE_DEFAULT;
      NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "BACKGROUND", importance);
      channel.setDescription("CHANEL DESCRIPTION");
      NotificationManager notificationManager = getSystemService(NotificationManager.class);
      notificationManager.createNotificationChannel(channel);
    }
  }

  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }

  @Override
  public void onCreate() {
    super.onCreate();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    this.handler.removeCallbacks(this.runnableCode);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    this.handler.post(this.runnableCode);
    createNotificationChannel();
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
      .setContentTitle("Safe Home")
      .setSmallIcon(R.mipmap.ic_launcher)
      .setContentIntent(contentIntent)
      .setOngoing(true)
      .setPriority(NotificationCompat.PRIORITY_MIN)
      .setVisibility(Notification.VISIBILITY_SECRET)
      .build();
    startForeground(SERVICE_NOTIFICATION_ID, notification);
    return START_STICKY;
  }

}

第二个

public class BackgroundService extends Service {

  private static final int SERVICE_NOTIFICATION_ID = 12345;
  private static final String CHANNEL_ID = "BACKGROUND";
  private volatile boolean running = true;

  private Thread thread;
  private Runnable runnableCode = new Runnable() {
    @Override
    public void run() {
      if (running) {
        Context context = getApplicationContext();
        Intent myIntent = new Intent(context, BackgroundEventService.class);
        context.startService(myIntent);
        HeadlessJsTaskService.acquireWakeLockNow(context);
      } else {
        Context context = getApplicationContext();
        Intent myIntent = new Intent(context, BackgroundEventService.class);
        context.stopService(myIntent);
      }
    }
  };
  private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      int importance = NotificationManager.IMPORTANCE_DEFAULT;
      NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "BACKGROUND", importance);
      channel.setDescription("CHANEL DESCRIPTION");
      NotificationManager notificationManager = getSystemService(NotificationManager.class);
      notificationManager.createNotificationChannel(channel);
    }
  }

  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }

  @Override
  public void onCreate() {
    super.onCreate();
  }

  @Override
  public void onDestroy() {
    running = false;
    this.thread.interrupt();
    super.onDestroy();
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    this.thread = new Thread(this.runnableCode);
    this.thread.start();
    createNotificationChannel();
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
      .setContentTitle("Safe Home")
      .setSmallIcon(R.mipmap.ic_launcher)
      .setContentIntent(contentIntent)
      .setOngoing(true)
      .setPriority(NotificationCompat.PRIORITY_MIN)
      .setVisibility(Notification.VISIBILITY_SECRET)
      .build();
    startForeground(SERVICE_NOTIFICATION_ID, notification);
    return START_STICKY;
  }

}

这个问题让我头疼,我找不到解决方案,我重复一切都很好但似乎BackgroundEventService没有停止并且套接字仍在运行,我尝试了太多方法来停止线程但没有任何效果为了我。也许我使用了错误的概念,或者是我的代码中的错误......

标签: androidreact-nativesocket.io

解决方案


我能够通过全局分配它来关闭套接字。在你的无头任务中:

const headlessTask = async () => {
      global.socket = SocketIOClient('url', {
        transports: ['websocket'],
        query: `email=${email}`
      });
      // Rest...
}

然后,在调用时,stopService我们只需像这样关闭它:

const stopService = () => {
  global.socket.close();
  Background.stopService();
}

推荐阅读