javaandroidexecutor

How to share data between ExecutorService.execute() from another class with ExecutorService while networking. Android


For context, I am making an app which communicates with TCP server hosted currently on my local pc. I am using SSLSocket for secure connection. All communications are ok. I have made a special class for handling all communications with the server. I know that I have to do it on background thread so I have already initialized ExecutorService and Handler in the class. Before explaining the problem, let me show the code of the class and when I call it.

Communication Class :-

private ExecutorService executor = Executors.newSingleThreadExecutor();
private Handler handler = new Handler(Looper.getMainLooper());


    public String genAuthToken(String userName, String passwd) throws Exception{
        if(!connected)return null;
        JSONObject json = new JSONObject();
        json.put("func","genAuthToken");
        json.put("userName",userName);
        json.put("passwd",passwd);
        JSONObject finalJSON = json;
        final String[] reply = {""};
  
        pw.println(finalJSON);
        reply[0] = scan.nextLine();
        
        json = new JSONObject(reply[0]);
        if((int) json.get("code") == 200){
            this.authToken = json.get("authToken").toString();
            return (String) json.get("authToken");
        }
        return String.valueOf((int) json.get("code"));
    }

MainActivity.java :-

LedGo client = new LedGo();
        boolean connected = client.connect("192.168.1.4",1304,cf,()->{},()->{Snackbar.make(view,"Couldn't connect to server",Snackbar.LENGTH_SHORT).setAnchorView(R.id.fab).show();});
        String authToken = client.genAuthToken("userName","passwd",true,()->{},()->{});

Now the problem is that I want to get authToken in MainActivity.java. I know that I cannot get it like I wrote above but I want to know how can I extract some data from ExecutorService. I thought of one option of making a public variable which could be accessed by MainActivity but that is not practical because app would be sending many requests from different thread at same time. In short, I want to know how to get variable from ExecutorService.execute() in MainActivity.java. If anything is not clear please tell me, I would really appreciate the help.

How to get some values from ExecutorService.execute() function in Android Development, Java


Solution

  • Simply make it event driven just like many things done in Android framework. It is clean, reliable and nonblocking.
    To implement, create an interface called AuthTokenListener:

    interface AuthTokenListener {
        onAuthTokenReady(String authToken)
    }
    

    Add a AuthTokenListener parameter to the genAuthToken method and invoke it when the token is ready. See the following snippet: Communication class:

    private ExecutorService executor = Executors.newSingleThreadExecutor();
    private Handler handler = new Handler(Looper.getMainLooper());
    
    public void genAuthToken(String userName, String passwd, AuthTokenListener listener) throws Exception{
        if(!connected)return null;
        JSONObject json = new JSONObject();
        json.put("func","genAuthToken");
        json.put("userName",userName);
        json.put("passwd",passwd);
        JSONObject finalJSON = json;
        final String[] reply = {""};
    
        pw.println(finalJSON);
        reply[0] = scan.nextLine();
    
        json = new JSONObject(reply[0]);
        if((int) json.get("code") == 200){
            this.authToken = json.get("authToken").toString();
            return (String) json.get("authToken");
        }
    
        // Looks like your token is ready to publish here
        // Then invoke the onAuthTokenReady method of the listener so that
        // it gets processed in the main activity
        listener.onAuthTokenReady(String.valueOf((int) json.get("code")));
    }
    

    Finally in your main activity you should implement the AuthTokenListener either making the activity class implement it or anonymly (in place) implementing:

    1. Make Activity Class Implement

    class MainActivity extends AppCompatActivity implements AuthTokenListener {
    
        @Override
        void onAuthTokenReady(String authToken) {
            // Process the authToken here
        }
    
        // Somewhere in your main MainActivity's method
        private void mainActivityMethodYouCreateLedGoClient() {
            LedGo client = new LedGo();
            boolean connected = client.connect("192.168.1.4",1304,cf,()->{},()->{Snackbar.make(view,"Couldn't connect to server",Snackbar.LENGTH_SHORT).setAnchorView(R.id.fab).show();});
            client.genAuthToken("userName","passwd", this);
        }
    }
    

    2. Implement In Place

    class MainActivity extends AppCompatActivity implements AuthTokenListener {
    
        // Somewhere in your main MainActivity's method
        private void mainActivityMethodYouCreateLedGoClient() {
            LedGo client = new LedGo();
            boolean connected = client.connect("192.168.1.4",1304,cf,()->{},()->{Snackbar.make(view,"Couldn't connect to server",Snackbar.LENGTH_SHORT).setAnchorView(R.id.fab).show();});
            client.genAuthToken("userName","passwd", new AuthTokenListener {
                @Override
                void onAuthTokenReady(String authToken) {
                    // Process the authToken here
                }
            });
        }
    }
    

    However I did not see you used the executor anywhere in Communication class.
    Also I'd like to remind you that if you invoke the listener method from a background thread, and you wanna modify some UI element then you should do it in main thread.