Home > Software design >  How fix this null check issue?
How fix this null check issue?

Time:07-02

I am getting the following stacktrace: enter image description here

Here is my source code:

import 'dart:io';
import 'package:xmpp_stone/src/logger/Log.dart';
import 'package:console/console.dart';
import 'dart:async';
import 'dart:convert';
import 'package:xmpp_stone/xmpp_stone.dart' as xmpp;
import 'package:image/image.dart' as image;
final String TAG = 'example';

class ExampleConnectionStateChangedListener implements xmpp.ConnectionStateChangedListener {
  late xmpp.Connection _connection;
  late xmpp.MessagesListener _messagesListener;

  StreamSubscription<String>? subscription;

  ExampleConnectionStateChangedListener(xmpp.Connection connection, xmpp.MessagesListener messagesListener) {
    _connection = connection;
    _messagesListener = messagesListener;
    _connection.connectionStateStream.listen(onConnectionStateChanged);
  }

  @override
  void onConnectionStateChanged(xmpp.XmppConnectionState state) {
    if (state == xmpp.XmppConnectionState.Ready) {
      Log.d(TAG, 'Connected');
     _connection.getMamModule().queryAll();
      var vCardManager = xmpp.VCardManager(_connection);
      vCardManager.getSelfVCard().then((vCard) {
        if (vCard != null) {
          Log.d(TAG, 'Your info'   vCard.buildXmlString());
        }
      });
      var messageHandler = xmpp.MessageHandler.getInstance(_connection);
      var rosterManager = xmpp.RosterManager.getInstance(_connection);
      messageHandler.messagesStream.listen(_messagesListener.onNewMessage);
      sleep(const Duration(seconds: 1));
      var receiver = '[email protected]';
      var receiverJid = xmpp.Jid.fromFullJid(receiver);
      rosterManager.addRosterItem(xmpp.Buddy(receiverJid)).then((result) {
        if (result.description != null) {
          print("TAG, 'add roster'"   result.description!);
        }
      });
      sleep(const Duration(seconds: 1));
      vCardManager.getVCardFor(receiverJid).then((vCard) {
        if (vCard != null) {
          print("TAG, 'Receiver info'"   vCard.buildXmlString());
          if (vCard != null && vCard.image != null) {
            var file = File('test456789.jpg')..writeAsBytesSync(image.encodeJpg(vCard.image!));
            print("TAG, IMAGE SAVED TO: ${file.path}");
          }
        }
      });
      var presenceManager = xmpp.PresenceManager.getInstance(_connection);
      presenceManager.presenceStream.listen(onPresence);

    }
  }

  void onPresence(xmpp.PresenceData event) {
    Log.d(TAG, 'presence Event from '   event.jid!.fullJid!   ' PRESENCE: '   event.showElement.toString());
  }
}

Stream<String> getConsoleStream() {
  return Console.adapter.byteStream().map((bytes) {
    var str = ascii.decode(bytes);
    str = str.substring(0, str.length - 1);
    return str;
  });
}

class ExampleMessagesListener implements xmpp.MessagesListener {

  @override
  void onNewMessage(xmpp.MessageStanza? message) {
    if (message!.body != null) {
      Log.d(TAG ,format(
          'New Message from {color.blue}${message.fromJid!.userAtDomain}{color.end} message: {color.red}${message.body}{color.end}'));
    }
  }

  @override
  void onChatMessage(xmpp.MessageStanza? message) {
    print(message);
    if (message!.body != null) {
      Log.d(TAG,format(
          'New Message from {color.blue}${message.fromJid!.userAtDomain}{color.end} message: {color.red}${message.body}{color.end}'));
    }
  }
}

sendmessageforxmpp(){
  var userAtDomain = '[email protected]';

  var password = '123456';
  var jid = xmpp.Jid.fromFullJid(userAtDomain);
  var account = xmpp.XmppAccountSettings(userAtDomain, jid.local, 
 jid.domain, password, 5222, resource: 'xmppstone');
  var connection = xmpp.Connection(account);
  var receiver = '[email protected]';
  var receiverJid = xmpp.Jid.fromFullJid(receiver);
Log.d(TAG, receiverJid.fullJid.toString());
  var messageHandler = 
   xmpp.MessageHandler.getInstance(connection); 
    messageHandler.sendMessage(receiverJid, "str");
}

CodePudding user response:

This error is usually occurring when you use the bang operator (!) on a nullable value that was not properly initialized, like

yourvariable!.somefield

The above assumes that yourvariable will not be null as this point. If it's null, then reality is in conflict with the assumption I have just described.

CodePudding user response:

Your problem is that you are not using xmpp_stone correctly and therefore ends up in a situation where the internal state of xmpp_stone does not match what the developer of the package have intended.

I do, however, think the package are badly designed in such a way that wrong usage are very likely to happen so I would not blame you for getting into trouble.

The problem is the following in your code:

var connection = xmpp.Connection(account);
// ..
var messageHandler = xmpp.MessageHandler.getInstance(connection);
messageHandler.sendMessage(receiverJid, "str");

You are here creating a Connection but the underlying socket are never created. The default value for the internal state of Connection are XmppConnectionState.Idle. But when you are later trying to sendMessage, your code ends up running this from the package:

  void write(message) {
    Log.xmppp_sending(message);
    if (isOpened()) {
      _socket!.write(message);
    }
  }

  bool isOpened() {
    return state != XmppConnectionState.Closed &&
        state != XmppConnectionState.ForcefullyClosed &&
        state != XmppConnectionState.Closing &&
        state != XmppConnectionState.SocketOpening;
  }

The isOpened() ends up returning true since it sees XmppConnectionState.Idle as an open state where messages are allowed to be sent.

But that is not the case here since we never asked Connection to open actually do any connection and therefore _socket ends up being null. Since the package are trying to do ! on null, the application crashes.

For an actual solution, we can get inspired from the example implementation from xmpp_dart:

https://github.com/vukoye/xmpp_dart/blob/master/example/example.dart

We can here see they have a connection.connect(); call. But, I am going to guess this really only works because the example are not going to use the connection right after this call. The problem is that it is implemented like the following:

  void connect() {
    if (_state == XmppConnectionState.Closing) {
      _state = XmppConnectionState.WouldLikeToOpen;
    }
    if (_state == XmppConnectionState.Closed) {
      _state = XmppConnectionState.Idle;
    }
    if (_state == XmppConnectionState.Idle) {
      openSocket();
    }
  }

  Future<void> openSocket() async {
    connectionNegotatiorManager.init();
    setState(XmppConnectionState.SocketOpening);
    try {
      return await Socket.connect(account.host ?? account.domain, account.port)
          .then((Socket socket) {
        // if not closed in meantime
        if (_state != XmppConnectionState.Closed) {
          setState(XmppConnectionState.SocketOpened);

So connect() returns void but calls openSocket() which does return a Future that would be able to tell us when the connection are actually ready.

I would therefore instead suggest using openSocket() directly and make your sendmessageforxmpp() method async so we can await on the connection being open.

So your code should look like:

Future<void> sendmessageforxmpp() async {
  var userAtDomain = '[email protected]';
  var password = '123456';
  
  var jid = xmpp.Jid.fromFullJid(userAtDomain);
  var account = xmpp.XmppAccountSettings(
      userAtDomain, jid.local, jid.domain, password, 5222,
      resource: 'xmppstone');

  var connection = xmpp.Connection(account);
  await connection.openSocket(); // <--- the important change :)

  var receiver = '[email protected]';
  var receiverJid = xmpp.Jid.fromFullJid(receiver);
  Log.d(TAG, receiverJid.fullJid.toString());
  var messageHandler = xmpp.MessageHandler.getInstance(connection);
  messageHandler.sendMessage(receiverJid, "str");
}
  •  Tags:  
  • dart
  • Related