Problem with start/bind service (Plugin)

Hey folks,

I have an issue that I couldn’t solve in the last two days (I tried various suggestions found with google…), so I hope you guys can help me with that :slight_smile:

I’m trying to implement a service which is started by Unity when a button is pushed. If the button is pushed again the service shall stop. Since I need to communicate (tracking steps of the user, but only call amount when needed for further computation) I wanted to use the bindService() function, so I have easy access to the service.

What I tried to accomplish that (I will post the customized AndroidManifest as I used it):
At first I created a new Activity “TActivity” which extends the UnityPlayerActivity with two test method getInt (static) and getFixedInt(non-static)
TActivity

package com.tracker;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class TActivity extends UnityPlayerActivity {

    private TService mBoundService;
    private boolean mServiceBound = false;

    public static Context sContext;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            TService.MyBinder myBinder = (TService.MyBinder) service;
            mBoundService = myBinder.getService();
            mServiceBound = true;
            Log.d("Unity", "Service connected");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBoundService = null;
            mServiceBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        sContext = this;
    }

    public void startTracking() {
        Intent intent = new Intent(this, TService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        Log.d("Unity", "Service bound");
    }

    public int getPowerValue() {
        return mBoundService.getPower();
    }

    public void getPower() {
        UnityPlayer.UnitySendMessage("Canvas", "ReceivePowerInfo", String.valueOf(mBoundService.getPower()));
    }

    public static int getInt() {
        return 123;
    }

    public int getFixedInt() {
        return 999;
    }

    public void stopTracking() {
        if (mServiceBound) {
            unbindService(mServiceConnection);
            mServiceBound = false;
        }
    }
}

Here I got the first problem: The onCreate-Method is never called at all → sContext will not be set and I get NullPtr exception via logcat when I use the following code:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tracker" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
  <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
  <application android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner">

    <activity android:name="com.tracker.TActivity" android:label="@string/app_name" android:screenOrientation="reverseLandscape" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>

    <service
      android:name="com.tracker.TService"
      android:enabled="true"
      android:exported="true">
    </service>

  </application>

  <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22" />
  <uses-feature android:glEsVersion="0x00020000" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
</manifest>
var javaClass = new AndroidJavaClass("com.tracker.TActivity");
Debug.Log(javaClass.CallStatic<int>("getInt"));  // works perfectly fine

var activity = javaClass.GetStatic<AndroidJavaObject>("sContext");  // NullPtr exception in Android (logcat)
Debug.Log(activity.Call<int>("getFixedInt"));  // not called at all

Then I tried to use the UnityPlayer class:

var javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
Debug.Log(javaClass.CallStatic<int>("getInt"));  // Method not found exception

var activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  // No NullPtr exception :)
Debug.Log(activity.Call<int>("getFixedInt"));  // Method not found exception

Finally I tried a mixture of both:

var javaClass = new AndroidJavaClass("com.tracker.UnityPlayer");
Debug.Log(javaClass.CallStatic<int>("getInt"));  // Perfectly fine

var activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  // Field not found exception
Debug.Log(activity.Call<int>("getFixedInt"));  // Method not called

Since all of that failed I thought “fine… you don’t really need to extend the base class, just use a bridge :roll_eyes:”. So I changed my Code to a more or less static variant (w/o the int test methods due to the absence of an object):“static” TActivity

package com.tracker;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class TActivity {

    private static TService mBoundService;
    private static boolean mServiceBound = false;

    private static ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            TService.MyBinder myBinder = (TService.MyBinder) service;
            mBoundService = myBinder.getService();
            mServiceBound = true;
            Log.d("Unity", "Service connected: " + mBoundService);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBoundService = null;
            mServiceBound = false;
        }
    };

    public static void startTracking(Activity unity) {
        Intent intent = new Intent(unity, TService.class);
        unity.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public static int getPowerValue() {
        return mBoundService.getPower();
    }

    public static void getPower() {
        UnityPlayer.UnitySendMessage("Canvas", "ReceivePowerInfo", String.valueOf(mBoundService.getPower()));
    }

    public static void stopTracking(Activity unity) {
        if (mServiceBound) {
            unity.unbindService(mServiceConnection);
            mServiceBound = false;
        }
    }
}

So far so good. To bind the service to unity I now need the activity. Easy going since it’s already provided:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tracker" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
  <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
  <application android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner">
  
    <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:screenOrientation="reverseLandscape" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>

    <service
      android:name="com.tracker.TService"
      android:enabled="true"
      android:exported="true">
    </service>

  </application>

  <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22" />
  <uses-feature android:glEsVersion="0x00020000" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
</manifest>
var javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
var activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  // Unity Activity

var bridge = new AndroidJavaClass("com.tracker.TActivity");  // My bridge class
bridge.CallStatic("startTracking", activity);  // Is called!

At this point the second problem arrived. Now when I finally want to bind the service to the Unity Activity I get:

Normally I would say “alright, I messed something up with the paths. Let’s recheck them all and try again.” But I can’t find the problem here… (TActivity and TService are in the same package, provided via .jar file in Assets/Plugins/Android → here lies the AndroidManifest too). After that I tried it with startService(intent), but that throws the same error. Maybe someone of you has any ideas?

Help is much appreciated! :stuck_out_tongue:
Thanks

Hey,

I finally solved one of my problems :slight_smile: My stuff wasn’t in the “Assets/Plugins/Android” folder, but in the “Assets/Assets/Plugins/Android” folder :sweat_smile:

Now I Unity finds my Service class and onCreate is called! What still does not work is the “currentActivity”, but I’m fine with my own static:

javaClass = new AndroidJavaClass("com.tracker.TActivity");
//activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  <- this still throws FieldNotFound
activity = javaClass.GetStatic<AndroidJavaObject>("sContext"); // this one works now

I post my current working Acitivity class and AndroidManifest in case someone has similar porblems :slight_smile:
TActivity

package com.tracker;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.unity3d.player.UnityPlayerActivity;

public class TActivity extends UnityPlayerActivity {

    private TService mBoundService;
    private boolean mServiceBound = false;

    public static Context sContext;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            TService.MyBinder myBinder = (TService.MyBinder) service;
            mBoundService = myBinder.getService();
            mServiceBound = true;
            Log.d("Unity", "Service connected: " + mBoundService);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mServiceBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        sContext = this;
    }

    public void startTracking() {
        Intent intent = new Intent(this, TService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public int getPowerValue() {
        return mBoundService.getPower();
    }

    public void stopTracking() {
        if (mServiceBound) {
            unbindService(mServiceConnection);
            mServiceBound = false;
        }
    }
}

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tracker" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
  <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
  <application android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner">
  
    <activity android:name="com.tracker.TActivity" android:label="@string/app_name" android:screenOrientation="reverseLandscape" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>
  
    <service
      android:name="com.tracker.TService"
      android:enabled="true"
      android:exported="true">
    </service>
  
  </application>

  <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22" />
  <uses-feature android:glEsVersion="0x00020000" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
</manifest>

Greetings