Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Problem with start/bind service (Plugin)

Discussion in 'Android' started by Fragfire, Aug 14, 2015.

  1. Fragfire

    Fragfire

    Joined:
    Apr 15, 2014
    Posts:
    9
    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 :)

    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)
    Code (CSharp):
    1. package com.tracker;
    2.  
    3. import android.app.Activity;
    4. import android.content.ComponentName;
    5. import android.content.Context;
    6. import android.content.Intent;
    7. import android.content.ServiceConnection;
    8. import android.os.Bundle;
    9. import android.os.IBinder;
    10. import android.util.Log;
    11.  
    12. import com.unity3d.player.UnityPlayer;
    13. import com.unity3d.player.UnityPlayerActivity;
    14.  
    15. public class TActivity extends UnityPlayerActivity {
    16.  
    17.     private TService mBoundService;
    18.     private boolean mServiceBound = false;
    19.  
    20.     public static Context sContext;
    21.  
    22.     private ServiceConnection mServiceConnection = new ServiceConnection() {
    23.         @Override
    24.         public void onServiceConnected(ComponentName componentName, IBinder service) {
    25.             TService.MyBinder myBinder = (TService.MyBinder) service;
    26.             mBoundService = myBinder.getService();
    27.             mServiceBound = true;
    28.             Log.d("Unity", "Service connected");
    29.         }
    30.  
    31.         @Override
    32.         public void onServiceDisconnected(ComponentName componentName) {
    33.             mBoundService = null;
    34.             mServiceBound = false;
    35.         }
    36.     };
    37.  
    38.     @Override
    39.     protected void onCreate(Bundle bundle) {
    40.         super.onCreate(bundle);
    41.         sContext = this;
    42.     }
    43.  
    44.     public void startTracking() {
    45.         Intent intent = new Intent(this, TService.class);
    46.         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    47.         Log.d("Unity", "Service bound");
    48.     }
    49.  
    50.     public int getPowerValue() {
    51.         return mBoundService.getPower();
    52.     }
    53.  
    54.     public void getPower() {
    55.         UnityPlayer.UnitySendMessage("Canvas", "ReceivePowerInfo", String.valueOf(mBoundService.getPower()));
    56.     }
    57.  
    58.     public static int getInt() {
    59.         return 123;
    60.     }
    61.  
    62.     public int getFixedInt() {
    63.         return 999;
    64.     }
    65.  
    66.     public void stopTracking() {
    67.         if (mServiceBound) {
    68.             unbindService(mServiceConnection);
    69.             mServiceBound = false;
    70.         }
    71.     }
    72. }

    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:

    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tracker" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
    3.   <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
    4.   <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">
    5.  
    6.     <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">
    7.       <intent-filter>
    8.         <action android:name="android.intent.action.MAIN" />
    9.         <category android:name="android.intent.category.LAUNCHER" />
    10.         <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    11.       </intent-filter>
    12.       <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    13.     </activity>
    14.  
    15.     <service
    16.       android:name="com.tracker.TService"
    17.       android:enabled="true"
    18.       android:exported="true">
    19.     </service>
    20.  
    21.   </application>
    22.  
    23.   <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22" />
    24.   <uses-feature android:glEsVersion="0x00020000" />
    25.   <uses-permission android:name="android.permission.INTERNET" />
    26.   <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    27.   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    28.   <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    29.   <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
    30.   <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
    31. </manifest>
    Code (CSharp):
    1. var javaClass = new AndroidJavaClass("com.tracker.TActivity");
    2. Debug.Log(javaClass.CallStatic<int>("getInt"));  // works perfectly fine
    3.  
    4. var activity = javaClass.GetStatic<AndroidJavaObject>("sContext");  // NullPtr exception in Android (logcat)
    5. Debug.Log(activity.Call<int>("getFixedInt"));  // not called at all
    Then I tried to use the UnityPlayer class:

    Code (CSharp):
    1. var javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    2. Debug.Log(javaClass.CallStatic<int>("getInt"));  // Method not found exception
    3.  
    4. var activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  // No NullPtr exception :)
    5. Debug.Log(activity.Call<int>("getFixedInt"));  // Method not found exception
    Finally I tried a mixture of both:

    Code (CSharp):
    1. var javaClass = new AndroidJavaClass("com.tracker.UnityPlayer");
    2. Debug.Log(javaClass.CallStatic<int>("getInt"));  // Perfectly fine
    3.  
    4. var activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  // Field not found exception
    5. 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 :rolleyes:". 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):
    Code (CSharp):
    1. package com.tracker;
    2.  
    3. import android.app.Activity;
    4. import android.content.ComponentName;
    5. import android.content.Context;
    6. import android.content.Intent;
    7. import android.content.ServiceConnection;
    8. import android.os.Bundle;
    9. import android.os.IBinder;
    10. import android.util.Log;
    11.  
    12. import com.unity3d.player.UnityPlayer;
    13. import com.unity3d.player.UnityPlayerActivity;
    14.  
    15. public class TActivity {
    16.  
    17.     private static TService mBoundService;
    18.     private static boolean mServiceBound = false;
    19.  
    20.     private static ServiceConnection mServiceConnection = new ServiceConnection() {
    21.         @Override
    22.         public void onServiceConnected(ComponentName componentName, IBinder service) {
    23.             TService.MyBinder myBinder = (TService.MyBinder) service;
    24.             mBoundService = myBinder.getService();
    25.             mServiceBound = true;
    26.             Log.d("Unity", "Service connected: " + mBoundService);
    27.         }
    28.  
    29.         @Override
    30.         public void onServiceDisconnected(ComponentName componentName) {
    31.             mBoundService = null;
    32.             mServiceBound = false;
    33.         }
    34.     };
    35.  
    36.     public static void startTracking(Activity unity) {
    37.         Intent intent = new Intent(unity, TService.class);
    38.         unity.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    39.     }
    40.  
    41.     public static int getPowerValue() {
    42.         return mBoundService.getPower();
    43.     }
    44.  
    45.     public static void getPower() {
    46.         UnityPlayer.UnitySendMessage("Canvas", "ReceivePowerInfo", String.valueOf(mBoundService.getPower()));
    47.     }
    48.  
    49.     public static void stopTracking(Activity unity) {
    50.         if (mServiceBound) {
    51.             unity.unbindService(mServiceConnection);
    52.             mServiceBound = false;
    53.         }
    54.     }
    55. }

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

    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tracker" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
    3.   <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
    4.   <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">
    5.  
    6.     <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">
    7.       <intent-filter>
    8.         <action android:name="android.intent.action.MAIN" />
    9.         <category android:name="android.intent.category.LAUNCHER" />
    10.         <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    11.       </intent-filter>
    12.       <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    13.     </activity>
    14.  
    15.     <service
    16.       android:name="com.tracker.TService"
    17.       android:enabled="true"
    18.       android:exported="true">
    19.     </service>
    20.  
    21.   </application>
    22.  
    23.   <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22" />
    24.   <uses-feature android:glEsVersion="0x00020000" />
    25.   <uses-permission android:name="android.permission.INTERNET" />
    26.   <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    27.   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    28.   <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    29.   <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
    30.   <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
    31. </manifest>
    Code (CSharp):
    1. var javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    2. var activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  // Unity Activity
    3.  
    4. var bridge = new AndroidJavaClass("com.tracker.TActivity");  // My bridge class
    5. 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! :p
    Thanks
     
  2. Fragfire

    Fragfire

    Joined:
    Apr 15, 2014
    Posts:
    9
    Hey,

    I finally solved one of my problems :) My stuff wasn't in the "Assets/Plugins/Android" folder, but in the "Assets/Assets/Plugins/Android" folder :oops:

    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:
    Code (CSharp):
    1. javaClass = new AndroidJavaClass("com.tracker.TActivity");
    2. //activity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  <- this still throws FieldNotFound
    3. activity = javaClass.GetStatic<AndroidJavaObject>("sContext"); // this one works now
    I post my current working Acitivity class and AndroidManifest in case someone has similar porblems :)
    Code (CSharp):
    1. package com.tracker;
    2.  
    3. import android.content.ComponentName;
    4. import android.content.Context;
    5. import android.content.Intent;
    6. import android.content.ServiceConnection;
    7. import android.os.Bundle;
    8. import android.os.IBinder;
    9. import android.util.Log;
    10.  
    11. import com.unity3d.player.UnityPlayerActivity;
    12.  
    13. public class TActivity extends UnityPlayerActivity {
    14.  
    15.     private TService mBoundService;
    16.     private boolean mServiceBound = false;
    17.  
    18.     public static Context sContext;
    19.  
    20.     private ServiceConnection mServiceConnection = new ServiceConnection() {
    21.         @Override
    22.         public void onServiceConnected(ComponentName componentName, IBinder service) {
    23.             TService.MyBinder myBinder = (TService.MyBinder) service;
    24.             mBoundService = myBinder.getService();
    25.             mServiceBound = true;
    26.             Log.d("Unity", "Service connected: " + mBoundService);
    27.         }
    28.  
    29.         @Override
    30.         public void onServiceDisconnected(ComponentName componentName) {
    31.             mServiceBound = false;
    32.         }
    33.     };
    34.  
    35.     @Override
    36.     protected void onCreate(Bundle bundle) {
    37.         super.onCreate(bundle);
    38.         sContext = this;
    39.     }
    40.  
    41.     public void startTracking() {
    42.         Intent intent = new Intent(this, TService.class);
    43.         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    44.     }
    45.  
    46.     public int getPowerValue() {
    47.         return mBoundService.getPower();
    48.     }
    49.  
    50.     public void stopTracking() {
    51.         if (mServiceBound) {
    52.             unbindService(mServiceConnection);
    53.             mServiceBound = false;
    54.         }
    55.     }
    56. }
    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tracker" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
    3.   <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
    4.   <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">
    5.  
    6.     <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">
    7.       <intent-filter>
    8.         <action android:name="android.intent.action.MAIN" />
    9.         <category android:name="android.intent.category.LAUNCHER" />
    10.         <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    11.       </intent-filter>
    12.       <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    13.     </activity>
    14.  
    15.     <service
    16.       android:name="com.tracker.TService"
    17.       android:enabled="true"
    18.       android:exported="true">
    19.     </service>
    20.  
    21.   </application>
    22.  
    23.   <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22" />
    24.   <uses-feature android:glEsVersion="0x00020000" />
    25.   <uses-permission android:name="android.permission.INTERNET" />
    26.   <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    27.   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    28.   <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    29.   <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
    30.   <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
    31. </manifest>

    Greetings