Callin a method in an Android plugin that will spawn a process in another thread (via runOnUiThread)

Hi guys, I’ve got a threading problem here. How do you call a method in an Android plugin that will spawn a process in another thread (via runOnUiThread)? AFAIK if you use the AndroidJavaObject.Call, won’t it lock the game until the thread in the plugin finished what it is doing? So what do I do to make the call go back to the Unity main thread until the plugin’s thread finishes its process? Using coroutines? But how?

Thanks.

Not sure what you’re asking. You can use C# to kick off System.Threading-style threads all day long and call whatever you want in them… EXCEPT Unity methods/objects. You can only call Unity methods/objects from the main thread.

These threads should work on all platforms, but I do not recommend using them unless you must.

If you instead set up a Coroutine via a Unity Monobehavior, that will behave in many ways like a separate thread, but it will actually not be a thread. Coroutines run on the main thread, which means you have first class access to all of Unity’s methods/objects. But you must yield regularly from a Coroutine, because until you yield, nothing else will run.

I’m not sure what your application is but if you don’t NEED multithreading, don’t even think about it. It is the source of massive headaches once you realize the hoops you have to go through in order to pass data back to your main thread. Almost everything you could want is doable via coroutines. You can set up a coroutine to interoperate with a native plugin, or do really anything you need. Coroutines are awesome.

To elaborate more, I’ve got this code in a .jar file that needs to be called from Unity:

    public String setScore(String data) {
        if (RUNNING_MEDIA_SESSION_ID == null) {
            RUNNING_MEDIA_SESSION_ID = LibUtil.getLocalSessionID(activity.getBaseContext());
        }
        Status status = null;
        String resp = OK;
        try {

                // Try to submit scores to server
                StringBuffer sb = new StringBuffer("{\"key\": \"" + secretKey + "\", \"data\": [");
                List<Scores> list = scoresDao.loadAll();
                for (int x = 0; x < list.size(); x++) {
                    Scores sc = list.get(x);
                    sb.append("{");
                    sb.append("\"type\": \"" + sc.getIdType() +"\",");
                    sb.append("\"unique\": \"" + sc.getUserId() +"\",");
                    sb.append("\"score\": " + sc.getScore());
                    sb.append("}");
                    if (x < (list.size() - 1)) {
                        sb.append(", ");
                    }
                }
                sb.append("]}");

                final String json = sb.toString();
                final String uidType = type;
                //Log.info(">>> TO SEND: " + json);
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Response res = null;
                        try {
                            String url = LibConstant.Url.reward_service;
                            if (uidType.equalsIgnoreCase(LibConstant.Type.PHONE)) {
                                url = url.concat("/") + LibConstant.Url.API.api_scores_submit_by_phone;
                            } else {
                                url = url.concat("/") + LibConstant.Url.API.api_scores_submit_by_email;
                            }
                            MediaType JSON = MediaType.parse("application/json; charset=utf-8");
                            OkHttpClient client = getNewHttpClient();
                            RequestBody body = RequestBody.create(JSON, json);
                            Request req = buildRequest(RUNNING_MEDIA_SESSION_ID).url(url).post(body).build();
                            res = client.newCall(req).execute();
                            if (res.isSuccessful()) {
                                String respString = res.body().string();
                                if (!respString.contains("error")) {
                                    JSONObject data = new JSONObject(respString);
                                    JSONArray ja = data.getJSONArray("data");
                                    if (ja.length() > 0) {
                                        scoresDao.deleteAll();
                                        for (int x = 0; x < ja.length(); x++) {
                                            JSONObject jo = ja.getJSONObject(x);
                                            Scores o = new Scores();
                                            o.setIdType(jo.getString("type"));
                                            o.setUserId(jo.getString("unique"));
                                            o.setScore(jo.getLong("score"));
                                            scoresDao.insert(o);
                                        }
                                    }
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            if (res != null) {
                                res.close();
                            }
                        }
                    }
                });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resp;
    }

I need this native Android plugin to bind the game with a background service. The thing is, as you can see, the plugin needs to call the web from another thread. Now how do I call this method from Unity? I know we can use AndroidJavaObject.Call to call a method from a native java plugin. But can it call the method when it has a multithreaded job inside of it?

That blob of code it supplies is just the function that will be run in the other thread. That code above should do its thing, fire that thread off and instantly return to you. That is a very common use pattern for these sorts of things.

Sometimes its a bit more convoluted, particularly if you need to actually get some piece of data back from that spawned thread later, in which case generally you supply a delegate that gets called with the data, but that doesn’t matter here.

I don’t really understand what you meant by that. Okay, if I call it using "AndroidJavaObject.Call(“setScore”, scoreObj.ToJSON()); ", it will call the method in the jar file, right? But when the jar method spawns another thread like above, wouldn’t the method return the “OK” constant response (since ‘resp’ was declared as “String resp = OK;” earlier) directly because the http request thread still hasn’t done its thing yet? If so, how do I get the return value of the thread when it has finished doing what it’s supposed to be doing? Because from my understanding, we are already inside the Unity’s main thread when the jar’s threading job is finished.

bump