pusher-channels-flutter
pusher-channels-flutter copied to clipboard
How do i detect if pusher has already been initialized.
I keep getting a platform exception on android that pusher has already been initialized.
Hello, did you solve the problem?
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.
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();
}
You can do the same check as in your connect() function?
if (pusher.connectionState != "CONNECTED") {
await _pusher.init( ... );
}
@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 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 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())); } }
@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?
Getting the same issue after hot restart of the app
@benjamin-tang-pusher Any Ideas?

@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:
@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
I can reproduce this; I will ask around and find out why this is.
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.
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:
- 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. - It looks like every single parameter in the
PusherChannelsFlutteris 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 overriddendispose()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?
@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?
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?