TooTallNate / Java-WebSocket

A barebones WebSocket client and server implementation written in 100% Java.
http://tootallnate.github.io/Java-WebSocket
MIT License
10.53k stars 2.58k forks source link

the crash happen from 1.5.3 to 1.5.4 #1353

Closed unengchen closed 12 months ago

unengchen commented 1 year ago

Describe the bug when I upgrade websocket version, from 1.5.3 to 1.5.4,and no other change,application crash

To Reproduce Steps to reproduce the behavior: everytime running crash for 1.5.4,but 1.5.3 is normal

Debug log: E/AndroidRuntime: FATAL EXCEPTION: main Process: com.zcla.gunbulletcabinet, PID: 4931 java.lang.IncompatibleClassChangeError: The method 'java.lang.Class java.lang.Object.getClass()' was expected to be of type interface but instead was found to be of type virtual (declaration of 'org.java_websocket.drafts.Draft_6455' appears in /data/app/com.zcla.gunbulletcabinet-1/base.apk:classes18.dex) at org.java_websocket.drafts.Draft_6455.(Draft_6455.java:247) at org.java_websocket.drafts.Draft_6455.copyInstance(Draft_6455.java:464) at org.java_websocket.WebSocketImpl.(WebSocketImpl.java:216) at org.java_websocket.client.WebSocketClient.(WebSocketClient.java:234) at org.java_websocket.client.WebSocketClient.(WebSocketClient.java:172) at com.zcla.gunbulletcabinet.socket.SocketManager$SocketClient.(SocketManager.java:1271) at com.zcla.gunbulletcabinet.socket.SocketManager$SocketClient.(SocketManager.java)

my app source code:

  String socketUrl = ConfigManager.getInstance().getWebSocketUrl();
    LogUtils.d(TAG, "init--socketurl", socketUrl);
    URI uri = URI.create(socketUrl);
    mSocketClient = new SocketClient(uri);
    // 取消丢失连接检测
    mSocketClient.setConnectionLostTimeout(0);
    mSocketClient.setReuseAddr(true);
    mSocketClient.connect();

private class SocketClient extends WebSocketClient { private SocketClient(URI serverUri) { super(serverUri); }

    @Override
    public void onOpen(ServerHandshake handshakedata) {

    }

    @Override
    public void onMessage(String message) {
        if (StringUtils.isEmpty(message)) return;
        mReceiveMessages.add(message);
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {

    }

    @Override
    public void onError(Exception ex) {
        LogUtils.d(TAG, "SocketClient-onError", ex.getMessage());
    }
}
PhilipRoman commented 1 year ago

What Android version are you using?

unengchen commented 1 year ago

Android 7.1

PhilipRoman commented 1 year ago

I have a potential fix for this, will try to push tonight. I have a feeling there may be even more issues caused by our build process update on Android, so I will also try to setup a test environment.

PhilipRoman commented 1 year ago

@marci4 Currently I see 2 ways to fix this - either every time we call an Object method (like hashCode, getClass, etc.) on an interface type we must cast to Object like this:

for(IExtension inputExtension : inputExtensions)
    if(((Object)inputExtension).getClass().equals(...))
        ...

or - need to build release with lower JDK version. From my tests JDK 17 should be okay, but JDK 19 already produces the incompatibility. This is the important diff in bytecode: image

What did you use to build the jar in maven central?

marci4 commented 1 year ago

@PhilipRoman I did use JDK 18,

Seeing these changes in 18 and 19, we can expect they are intentional?

PhilipRoman commented 1 year ago

Probably yes, I read the spec and I cannot find any reason why virtual methods could not be invoked using invokeinterface, so it seems valid for "javac" to emit them. This seems to be an issue in Dalvik VM.

marci4 commented 1 year ago

Thanks for taking a look at this.

Doing a quick google with "java.lang.IncompatibleClassChangeError: The method 'java.lang.Class java.lang.Object.getClass()' was expected to be of type interface but instead was found to be of type virtual" I found this https://github.com/android/android-test/issues/1642#issuecomment-1452224722

Seems to be a Dexer (D8) issue.

PhilipRoman commented 1 year ago

Since it is issue on Android side, I think we can close this as wontfix

For those affected: until d8 is fixed, you can either use 1.5.3 (the only difference from 1.5.4 is lack of Java modules - not important for Android) or compile the library yourself (mvn package -DskipTests=true) with older JDK.