I'm developing an application for android on Unity3d engine. This application must connect to my server on PC via network sockets. I found some Unity3d plugin examples for Android. Based on them I've written some code on c# for Unity3d and on Java for Android. I found out that network operations must not run in UI-thread in Android application. So I have to use AsynTask for network requests. Also I've tried to call non-static methods from c# script but they don't return any data. Only static calls return data from java application. So my AsyncTask class is static. But when I call static function with AsyncTask job to get data through network my application is crashed. I receive errors. Could you help me to fix my problems? I see two ways to fix this problem: 1) Change my c# code for Unity3d to get data through Non-Static method calls. Change all methods to Non-Static in Java code. 2) Change my Java code to work with static methods and static AsyncTask.
My c# script AndroidClientPlugin.cs:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class AndroidClientPlugin : MonoBehaviour {
private float TEST;
private AndroidJavaClass cls_UnityPlayer;
private AndroidJavaObject obj_Activity;
private AndroidJavaClass cls_CompassActivity;
// Use this for initialization
void Start () {
AndroidJNI.AttachCurrentThread();
cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
cls_CompassActivity = new AndroidJavaClass("com.lab.Android.AndroidClientPlugin");
cls_CompassActivity.SetStatic<String>("ServerAddressValue", "192.168.1.5");
cls_CompassActivity.SetStatic<String>("ServerPortValue", "8881");
}
void OnGUI() {
GUI.Label(new Rect(Screen.width / 2 -200, Screen.height / 2, 400,100), "x = " + TEST.ToString());
}
void Update()
{
if(cls_CompassActivity.CallStatic<bool>("GetData"))
{
TEST = cls_CompassActivity.CallStatic<float>("getPosX");
}
}
}
My Java script AndroidClientPlugin.java:
package com.lab.Android;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Looper;
import android.os.StrictMode;
import android.util.Log;
import android.content.Context;
import android.content.Intent;
import android.app.Activity;
public class AndroidClientPlugin extends UnityPlayerActivity {
//Server address parameters
public static String ServerAddressValue;
public static String ServerPortValue;
//Tracker parameters
public static String vServerName;
public static String vSensorNumber;
private static SensorData vTaskResult;
public static cTask BackgroundTask;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
//set thread strict mode off
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
vTaskResult = new SensorData();
ServerAddressValue = "192.168.1.5";
ServerPortValue = "8881";
vServerName = "Tracker0";
vSensorNumber = "0";
BackgroundTask = new cTask();
}
@Override
protected void onResume()
{
super.onResume();
}
@Override
protected void onStop()
{
super.onStop();
}
public static boolean GetData()
{
cTaskResult taskResult = new cTaskResult();
taskResult = BackgroundTask.DoAsyncTask(ServerAddressValue, ServerPortValue, vServerName, vSensorNumber);
vTaskResult = taskResult.ResultData;
return taskResult.DataIsReady;
}
public static class cTaskResult
{
public boolean DataIsReady;
public SensorData ResultData;
public cTaskResult()
{
DataIsReady = false;
ResultData = new SensorData();
}
}
public static class cTask
{
public cTask()
{
}
public cTaskResult DoAsyncTask(String serverAddress, String serverPort, String trackerName, String trackerSensorNumber)
{
cTaskResult Result = new cTaskResult();
GetDataTask Task;
Task = new GetDataTask();
Task.execute(serverAddress, serverPort, trackerName, trackerSensorNumber, Result);
try {
Result = Task.get(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Result;
}
public static class GetDataTask extends AsyncTask<Object, Void, cTaskResult>
{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected cTaskResult doInBackground(Object... params) {
cTaskResult TMPData = new cTaskResult();
//doing network requests
//TMPData.DataIsReady = NetClient.getInstance().GetData((String)params[0], (String)params[1], (String)params[2], Integer.valueOf((String)params[3]), TMPData.ResultData);
//TMPData is a result of network operations
TMPData.DataIsReady = true;
TMPData.ResultData = new SensorData();
return TMPData;
}
@Override
protected void onPostExecute(cTaskResult result) {
super.onPostExecute(result);
}
}
}
public static float getPosX()
{
return vTaskResult.posX;
}
}
Debug messages:
09-11 12:56:05.514: E/CMarlinMediator(137): Error : MarlinMediator Failed to get TrustedTime
09-11 12:56:05.594: E/CMarlinMediator(137): Error : MarlinMediator Failed to get TrustedTime
09-11 12:56:12.184: E/Adreno200-EGL(7590): <qeglDrvAPI_eglGetConfigAttrib:484>: EGL_BAD_ATTRIBUTE
09-11 12:56:12.184: E/Adreno200-EGL(7590): <qeglDrvAPI_eglGetConfigAttrib:484>: EGL_BAD_ATTRIBUTE
09-11 12:56:12.184: E/Adreno200-EGL(7590): <qeglDrvAPI_eglGetConfigAttrib:484>: EGL_BAD_ATTRIBUTE
09-11 12:56:14.304: E/AndroidRuntime(7590): FATAL EXCEPTION: GLThread 741
09-11 12:56:14.304: E/AndroidRuntime(7590): java.lang.ExceptionInInitializerError
09-11 12:56:14.304: E/AndroidRuntime(7590): at com.lab.Android.AndroidClientPlugin$cTask.DoAsyncTask(AndroidClientPlugin.java:128)
09-11 12:56:14.304: E/AndroidRuntime(7590): at com.lab.Android.AndroidClientPlugin.GetData(AndroidClientPlugin.java:79)
09-11 12:56:14.304: E/AndroidRuntime(7590): at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
09-11 12:56:14.304: E/AndroidRuntime(7590): at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
09-11 12:56:14.304: E/AndroidRuntime(7590): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1462)
09-11 12:56:14.304: E/AndroidRuntime(7590): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1216)
09-11 12:56:14.304: E/AndroidRuntime(7590): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
09-11 12:56:14.304: E/AndroidRuntime(7590): at android.os.Handler.<init>(Handler.java:121)
09-11 12:56:14.304: E/AndroidRuntime(7590): at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:607)
09-11 12:56:14.304: E/AndroidRuntime(7590): at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:607)
09-11 12:56:14.304: E/AndroidRuntime(7590): at android.os.AsyncTask.<clinit>(AsyncTask.java:190)
09-11 12:56:14.304: E/AndroidRuntime(7590): ... 6 more
09-11 12:56:15.194: E/CMarlinMediator(137): Error : MarlinMediator Failed to get TrustedTime
09-11 12:56:15.234: E/CMarlinMediator(137): Error : MarlinMediator Failed to get TrustedTime
First of all, why you call android code in Update function? Since it will be called every frame.
Maybe you should put some flag that indicates the native-code is being running, and dont called it twice.
And about your problem, as far as I know, AsyncTask creation and task running must be called within the UI-Thread. The problems are:
AndroidClientPlugin.GetData
within the Update
method in Unity. As far as I know, that is not the same thread with the UI-Thread of android. So you called it within Unity's own thread, and:My solution is (you can try it):
public static void GetData()
{
//this will be called on UIThread of Android
com.unity3d.player.UnityPlayer.currentActivity.runOnUiThread(new Runnable(){
public void run(){
cTaskResult taskResult = new cTaskResult();
taskResult = BackgroundTask.DoAsyncTask(ServerAddressValue, ServerPortValue, vServerName, vSensorNumber);
vTaskResult = taskResult.ResultData;
com.unity3d.player.UnityPlayer.currentActivity.SendMessage("YourGameObjectName", "YourMethodName", taskResult.DataIsReady);
//return taskResult.DataIsReady;
}
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With