pusher-channels-flutter icon indicating copy to clipboard operation
pusher-channels-flutter copied to clipboard

How do i detect if pusher has already been initialized.

Open demola33 opened this issue 3 years ago • 18 comments

I keep getting a platform exception on android that pusher has already been initialized.

demola33 avatar Apr 14 '22 17:04 demola33

Hello, did you solve the problem?

Natanchik89 avatar Apr 18 '22 07:04 Natanchik89

Can you paste the error message and the stacktrace?

Could you also provide more information on how to reproduce this? Normally you should not be getting this exception.

benjamin-tang-pusher avatar Apr 20 '22 21:04 benjamin-tang-pusher

This happens when i hot restart my Flutter application

E/MethodChannel#pusher_channels_flutter( 6421): Failed to handle method call E/MethodChannel#pusher_channels_flutter( 6421): java.lang.IllegalArgumentException: Already subscribed to a channel with name trade-updates E/MethodChannel#pusher_channels_flutter( 6421): at com.pusher.client.channel.impl.ChannelManager.validateArgumentsAndBindEvents(ChannelManager.java:198) E/MethodChannel#pusher_channels_flutter( 6421): at com.pusher.client.channel.impl.ChannelManager.subscribeTo(ChannelManager.java:85) E/MethodChannel#pusher_channels_flutter( 6421): at com.pusher.client.Pusher.subscribe(Pusher.java:246) E/MethodChannel#pusher_channels_flutter( 6421): at com.pusher.channels_flutter.PusherChannelsFlutterPlugin.subscribe(PusherChannelsFlutterPlugin.kt:150) E/MethodChannel#pusher_channels_flutter( 6421): at com.pusher.channels_flutter.PusherChannelsFlutterPlugin.onMethodCall(PusherChannelsFlutterPlugin.kt:68) E/MethodChannel#pusher_channels_flutter( 6421): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262) E/MethodChannel#pusher_channels_flutter( 6421): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:296) E/MethodChannel#pusher_channels_flutter( 6421): at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$DartMessenger(DartMessenger.java:320) E/MethodChannel#pusher_channels_flutter( 6421): at io.flutter.embedding.engine.dart.-$$Lambda$DartMessenger$TsixYUB5E6FpKhMtCSQVHKE89gQ.run(Unknown Source:12) E/MethodChannel#pusher_channels_flutter( 6421): at android.os.Handler.handleCallback(Handler.java:938) E/MethodChannel#pusher_channels_flutter( 6421): at android.os.Handler.dispatchMessage(Handler.java:99) E/MethodChannel#pusher_channels_flutter( 6421): at android.os.Looper.loop(Looper.java:223) E/MethodChannel#pusher_channels_flutter( 6421): at android.app.ActivityThread.main(ActivityThread.java:7656) E/MethodChannel#pusher_channels_flutter( 6421): at java.lang.reflect.Method.invoke(Native Method) E/MethodChannel#pusher_channels_flutter( 6421): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) E/MethodChannel#pusher_channels_flutter( 6421): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) I/flutter ( 6421): Pusher ERR: PlatformException(PusherChannelsFlutter, Pusher Channels already initialized., null, null)

This is my root class where i make calls to initialize pusher and also connect to pusher

 @override
  void initState() {
    _getInitialUser();
    **resultLoaded = _initializeApp();**
    super.initState();
  }

  Future<void> _initializeApp() async {
    Future.delayed(const Duration(seconds: 4)).then((value) {
      **_pusherService.initializePusher();**
      return _auth.checkAndUpdateAuthStatus();
    });
  }

@override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<AuthProvider>(
      create: (context) => locator<AuthProvider>(),
      child: Selector<AuthProvider, AuthState>(
        selector: (_, auth) => auth.appState,
        builder: (context, authState, child) => ScreenUtilInit(
          designSize: const Size(392.7, 737.4),
          minTextAdapt: true,
          builder: () {
            authState.maybeMap(
              orElse: () {},
              authenticated: (_) async {
                **_pusherService.connect();**
                _appRouter.pushAndPopUntil(const HomeRoute(),
                    predicate: (route) => false);
              },
              unauthenticated: (_) async {
                _appRouter.pushAndPopUntil(const WelcomeScreen(),
                    predicate: (route) => false);
                **await _pusherService.disconnect();**
              },
            );

**These methods are inside my Pusher class **

** on hot restart my initialized method is called again and i get that stack trace.

Future<void> initializePusher() async {
void onConnectionStateChange(String currentState, String previousState) {
}

void onError(String message, int? code, dynamic e) {
}

try {
  await Future.wait([
    _pusher.init(
      apiKey: appKey,
      cluster: cluster,
      logToConsole: true,
      onConnectionStateChange: onConnectionStateChange,
      onError: (message, code, error) {
        print("errMsg:$message");
        print("errCode:$code");
        print("err:$error");
        return;
      },
      onSubscriptionSucceeded: (channelName, data) {
        print("Subscribed to $channelName");
        print("data: $data");
      },
      onSubscriptionError: (message, error) =>
          print("OnSubErr" + error + " " + message),
    ),
    _subscribe(),
  ]);
  } catch (e) {
  print("Pusher ERR: $e");
 }
}

 Future<void> connect() async {
  final connectionState = _pusher.connectionState;
  print("CSTATE: $connectionState");
  if (connectionState != "CONNECTED") {
    print("Connecting to pusher");
    await _pusher.connect();
  }

demola33 avatar Apr 25 '22 17:04 demola33

You can do the same check as in your connect() function?

if (pusher.connectionState != "CONNECTED") {
  await _pusher.init( ... );
}

proggen-com avatar May 10 '22 13:05 proggen-com

@proggen-com Hello, I have a pusher initialization when logging into the system, there is also a "pusher.disconnect ()" when logout. When I reauthorize my pusher.connectionState == DISCONNECTED and still I get "ERROR: PlatformException(PusherChannelsFlutter, Pusher Channels already initialized., null, null)"

Natanchik89 avatar May 30 '22 10:05 Natanchik89

@Natanchik89 could you share the code in use? Are you using the check suggested in https://github.com/pusher/pusher-channels-flutter/issues/38#issuecomment-1122413406?

benw-pusher avatar Jun 14 '22 13:06 benw-pusher

@benw-pusher function Login

`void _login() async{ setState(() { _isLoading = true; }); var data = { 'email' : email, 'password' : password };

var res = await Network().authData(data, '/login');

var body = json.decode(res.body);
if(body['success']){
  SharedPreferences localStorage = await SharedPreferences.getInstance();
  localStorage.setString('token', body['token']);
  localStorage.setString('user', body['user_name']);
  Navigator.push(
    context,
    new MaterialPageRoute(
        builder: (context) => Home()
    ),
  );
}else{
  _showMsg(body['message']);
}

setState(() {
  _isLoading = false;
});

}`

Class Home and init pusher

`class _HomeState extends State<Home>{ PusherChannelsFlutter pusher = PusherChannelsFlutter.getInstance(); String _log = 'output:\n'; final _listViewController = ScrollController(); var name, _socketId; @override void initState(){ _loadUserData(); _initPusher(); super.initState(); }

_loadUserData() async{ SharedPreferences localStorage = await SharedPreferences.getInstance(); var user = localStorage.getString('user'); if(user != null) { setState(() { name = user; }); } }

_initPusher() async{ print(pusher.connectionState); // this == DISCONNECTED try { if (pusher.connectionState != "CONNECTED") { await pusher.init( apiKey: apiKey, cluster: 'ap1', onConnectionStateChange: onConnectionStateChange, onError: onError, onSubscriptionSucceeded: onSubscriptionSucceeded, onEvent: onEvent, onAuthorizer: onAuthorizer ); } await pusher.subscribe(channelName: 'presence-api-channel'); await pusher.connect(); } catch (e) { print("ERROR: $e"); } }`

function Logout

void logout() async{ await pusher.disconnect(); var res = await Network().logoutApi('/logout'); var body = json.decode(res.body); if(body['success']){ SharedPreferences localStorage = await SharedPreferences.getInstance(); localStorage.remove('user'); localStorage.remove('token'); Navigator.push( context, MaterialPageRoute(builder: (context)=>Login())); } }

Natanchik89 avatar Jun 15 '22 03:06 Natanchik89

@benw-pusher After the first round (login, pusher initialization and logout) upon re-login, the pusher is already initialized and although pusher.connectionState = "DISCONNECTED ", the connection state implies that the busher has already been initiated, so the check is meaningless. How to check exactly the initialization or destroy it on logout?

Natanchik89 avatar Jun 15 '22 03:06 Natanchik89

Getting the same issue after hot restart of the app @benjamin-tang-pusher Any Ideas? image

GauravCalidig avatar Jun 22 '22 11:06 GauravCalidig

@GauravCalidig You have a problem in the next stage after initialization, that is, in subscribe to the channel, and we have it in initialization. Or I misunderstood. Here is my error: screen

Natanchik89 avatar Jun 22 '22 13:06 Natanchik89

image @Natanchik89 i guess the author is having the same issue as well, Moreover ig the solution will be the same to check if it has been already initialized

GauravCalidig avatar Jun 22 '22 15:06 GauravCalidig

I can reproduce this; I will ask around and find out why this is.

benjamin-tang-pusher avatar Jun 27 '22 18:06 benjamin-tang-pusher

The root cause is dart vm restart after hot-restart but plugin not.

https://github.com/flutter/flutter/issues/10437 For now, we couldn’t be informed hot-restart is coming. Maybe we could check the init parameters in native side to decide whether re-initialize a PusherClient or reuse it rather than throw an exception. And move initialized check to dart side.

JimmyTai avatar Jul 25 '22 19:07 JimmyTai

I think I might have some new information about that problem.

Since the getInstance() method is static, I just put it in my main, which resolved the error when loading the screen that tries to make a connection to Pusher. However, it did not solve it whenever I was popping the screen and pushing it back. Because of this, I tried putting my init(apiKey : {apiKey}, cluster {cluster}) into my main too, but then whenever I tried hot restarting the app, it showed me this error:

I/PusherChannelsFlutter(11358): Start com.pusher.client.Pusher@4ce4e43
W/System.err(11358): SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
W/System.err(11358): SLF4J: Defaulting to no-operation (NOP) logger implementation
W/System.err(11358): SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

I never saw this thing in my entire life, but I'm pretty sure it has something to do with the fact that the _instance variable is static to the class. I would suggest taking a look at the SharedPreferences package at https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences, as it also uses that static instance, but I have no problem making a custom class like the following and calling the initSharedPreferences() method in my main before even calling runApp():

class SharedPreferencesController {
  static late SharedPreferences sharedPreferences;

  static Future<void> initSharedPreferences() async =>
      sharedPreferences = await SharedPreferences.getInstance();

  String get token => sharedPreferences.getString('token') ?? '';

  set token(String newToken) => sharedPreferences.setString('token', newToken);

  int get companyId => sharedPreferences.getInt('companyId') ?? 0;

  set companyId(int companyId) =>
      sharedPreferences.setInt('companyId', companyId);
}

UPDATE

I managed to find out that the subscription works out fine, but the connection just doesn't work. There are 2 weird things that are happening:

  1. I printed a statement after calling the connect() method and it printed before the SLF4J error. At first, I thought it was caused by something happening during the connection, preventing it from completing successfully, but since it appears only after the print statement which in turn is after the connection method is called, I'm not sure anymore.
  2. It looks like every single parameter in the PusherChannelsFlutter is static, as I got a message telling me that I was already subscribed to a channel when popping and pushing back the page that created that subscription at its initialization. This error went away as soon as I unsubscribed in the overridden dispose() method.

Something also came to mind while writing this update: I'm using the self-hosted Laravel Websockets package instead of using Pusher's native web socket, since the latter won't let me send data of more than 100 Kb (or something along those lines). This means that even the real Pusher dashboard gets a hold of my events since I'm hosting this websocket server onto my own port 6001. Would this be why somehow can't seem to connect?

SniffyMcTasty avatar Aug 29 '22 15:08 SniffyMcTasty

@JimmyTai @benjamin-tang-pusher Any updates on this? This has been a pain in the ass when developing.

Maybe a dispose method that can be called before initialization after a hot reload which cleans up the resources?

RootSoft avatar Sep 04 '22 21:09 RootSoft

6 months...wow, did you guys move to another package?

We have just merged https://github.com/pusher/pusher-channels-flutter/pull/80 which allow for re-initialization. Does that help in your case?

proggen-com avatar Oct 28 '22 05:10 proggen-com