androidfirebasewebsocketandroid-jobschedulerfirebase-job-dispatcher

How to use Firebase JobDispatcher for async reconnection of nv-websocket-client?


In an Android app (with minSdkVersion 16) I use the nv-websocket-client library:

public class MainActivity extends AppCompatActivity {

    private WebSocket mWs;
    private WebSocketFactory mWsFactory = new WebSocketFactory();
    private WebSocketListener mWsListener = new WebSocketAdapter() {
        @Override
        public void onConnected(WebSocket ws, Map<String, List<String>> headers) throws Exception {
            mReconnectDispatcher.cancelAll();
        }

        // connection on the background thread has failed (onDisconnected will not be called!)
        @Override
        public void onConnectError(WebSocket ws, WebSocketException ex) throws Exception {
            reconnect();
        }

        @Override
        public void onDisconnected(WebSocket ws,
                                   WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame,
                                   boolean closedByServer) throws Exception {
            reconnect();
        }

        @Override
        public void onTextMessage(WebSocket ws, String str) throws Exception {
            // here the WebSockets communication happens
        }
    };

    @Override
    public void onResume() {
        super.onResume();
        reconnect();
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mWs != null) {
            mWs.disconnect();
        }
    }

And Firebase JobDispatcher (version 0.8.4) for reconnecting the WebSocket:

private FirebaseJobDispatcher mReconnectDispatcher;

public class ReconnectService extends JobService {
    @Override
    public boolean onStartJob(JobParameters job) {
        try {
            mWs = mWsFactory.createSocket("ws://demos.kaazing.com/echo");
            mWs.addListener(mWsListener);
            mWs.connectAsynchronously(); // uses background thread
        } catch (Exception ex) {
            Log.w(TAG, "Can not create WebSocket connection", ex);
        }

        return true; // Answers the question: "Is there still work going on?"
    }

    @Override
    public boolean onStopJob(JobParameters job) {
        return false; // Answers the question: "Should this job be retried?"
    }
}

private void reconnect() {
    Job myJob = mReconnectDispatcher.newJobBuilder()
        .setService(ReconnectService.class)
        .setTag(ReconnectService.class.getSimpleName())
        .setRecurring(false)
        .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
        .setTrigger(Trigger.executionWindow(0, 60))
        .setReplaceCurrent(true)
        .setConstraints(Constraint.ON_ANY_NETWORK)
        .build();

    mReconnectDispatcher.mustSchedule(myJob);
}

And in AndroidManifest.xml I have:

<service
    android:name=".MainActivity$ReconnectService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
    </intent-filter>
</service>

Unfortunately, I get the runtime error after 60 seconds:

16998-16998/de.slova E/AndroidRuntime: FATAL EXCEPTION: main
   Process: de.slova, PID: 16998
   java.lang.RuntimeException: Unable to instantiate service de.slova.MainActivity$ReconnectService: java.lang.InstantiationException: java.lang.Class<de.slova.MainActivity$ReconnectService> has no zero argument constructor
       at android.app.ActivityThread.handleCreateService(ActivityThread.java:3389)
       at android.app.ActivityThread.-wrap4(Unknown Source:0)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1683)
       at android.os.Handler.dispatchMessage(Handler.java:105)
       at android.os.Looper.loop(Looper.java:164)
       at android.app.ActivityThread.main(ActivityThread.java:6541)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
    Caused by: java.lang.InstantiationException: java.lang.Class<de.slova.MainActivity$ReconnectService> has no zero argument constructor
       at java.lang.Class.newInstance(Native Method)
       at android.app.ActivityThread.handleCreateService(ActivityThread.java:3386)
       at android.app.ActivityThread.-wrap4(Unknown Source:0) 
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1683) 
       at android.os.Handler.dispatchMessage(Handler.java:105) 
       at android.os.Looper.loop(Looper.java:164) 
       at android.app.ActivityThread.main(ActivityThread.java:6541) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
1472-1487/? I/ActivityManager: Showing crash dialog for package de.slova u0

I have tried adding a default constructor to my class, but the error persists:

public class ReconnectService extends JobService {
    public ReconnectService() {
    }

Does anybody please have an idea, what is wrong here?

I have also submitted my problem as issue #189.


Solution

  • Put ReconnectService in its own class (not an inner class to MainActivity). Alternatively, try making the inner class static.

    The way non-static inner classes work in Java is that they can only be instantiated by an instance of the outer class. This is what allows instances of the inner class to access members of the outer class. So, when Android is trying to create an instance of the inner class, it can't do so because it doesn't know what instance of the outer class it should be a part of. The error message is a bit misleading.