I'm developing an android app with Unity that requires a foreground service. To do that I've made an android module with android studio. The problem is that my service is never started but at the same time there aren't errors and the aplication still running, for example after my start service I can show a toast.
Unity - C# - AndroidPluginManager.cs
I'm using this class to instantiate the module and call the function to start the service. The function StartAndroidService() is called using the onClick() feature of Unity inspector.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AndroidPluginManager : MonoBehaviour
{
private AndroidJavaObject androidPlugin = null;
private AndroidJavaObject unityContext = null;
private AndroidJavaObject unityActivity = null;
private AndroidJavaClass unityClass = null;
void Start() {
//Get unity activity and context
unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
unityActivity = unityClass.GetStatic<AndroidJavaObject>("currentActivity");
unityContext = unityActivity.Call<AndroidJavaObject>("getApplicationContext");
//Create android plugin object
androidPlugin = new AndroidJavaObject("com.androidplugin.stepcounterlibrary.ApiStepCounter");
//Set activity and context to the module
androidPlugin.Call("setActivity", unityActivity);
androidPlugin.Call("setContext", unityContext);
}
//Start Service
public void StartAndroidService() {
androidPlugin.Call("startStepCounter");
androidPlugin.Call("showMessage", "Hello World");
}
}
Android Studio - Java - ApiStepCounter.java
For the time being I'm using this class only to start the service and create a toast to check if the module is working correctly.
package com.androidplugin.stepcounterlibrary;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class ApiStepCounter{
private Activity activity;
private Context context;
public ApiStepCounter() {}
public void setActivity(Activity activity){
this.activity = activity;
}
public void setContext(Context context){
this.context = context;
}
public void showMessage(String message){
Toast.makeText(this.context, message, Toast.LENGTH_LONG).show();
}
public void startStepCounter(){
Intent serviceIntent = new Intent(this.context, StepCounterService.class);
this.context.startService(serviceIntent);
}
}
Android Studio - Java - StepCounterService.java
This class should start the foreground service and show a notification but it doesn't. It's the first time that I try to use an Android service so I don't know if I'm doing something wrong. Also I've tried to execute some Log.d inside each method (onCreate(), onStartCommand(), onDestroy()) and no one is printed so I guess that the service is not started.
package com.androidplugin.stepcounterlibrary;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
public class StepCounterService extends Service {
public StepCounterService(){}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "TestChannel");
builder.setSmallIcon(R.drawable.ic_android)
.setContentText("Text of notification")
.setContentTitle("Title of notification");
Notification notification = builder.build();
startForeground(1, notification);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Android Studio - Manifest - AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidplugin.stepcounterlibrary">
<application>
<service android:name=".StepCounterService"/>
</application>
</manifest>
I've been trying for 2 days how to create the foreground service using Unity with an Android module so if someone could help me I would appreciate it a lot. Feel free to ask me anything that you need, I think this is all that I need to create a foreground service but maybe I'm missing something.
Related
I'm using a Sunmi K2 POS Checkout Terminal running with Android 7.1.2
I'm developing a POS software with WinDev for Mobile 26, I'm already displaying some windows, now the question is, how can I print on the build-in pos printer??
There is a print-test app on the kiosk installed, this works fine.
In the settings there is a printer settings too, but this says "no device installed"...
In the documentation there is a "AIDL interface" mentioned...
And more, there is a API sample written:
Bound Service
Intent intent = new Intent();
intent.setPackage("com.sunmi.extprinterservice");
intent.setAction("com.sunmi.extprinterservice.PrinterService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
It is necessary to establish a new ServiceConnection service to bind the callback
ServiceConnection serviceConnection = new ServiceConnection() {
#Override public void onServiceConnected(ComponentName name, IBinder service) {
ExtPrinterService ext = ExtPrinterService.Stub.asInterface(service);
}
#Override public void onServiceDisconnected(ComponentName name) { }
};
Use ext object to realize one’s own printing task
ext.printText(“123456\n”);
Unbind the service after the completion of the usage
unbindService(serviceConnection);
Question is, how can I use this Java code in WinDev Mobile??
EDIT:
I managed to print to the printer, but there is a small bug, so sometime it doesn't start immediatly, so therefore is the loop...
import android.content.ComponentName;
import android.content.Context;
import static android.content.Context.BIND_AUTO_CREATE;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.widget.Toast;
import com.sunmi.extprinterservice.ExtPrinterService;
public static void PrintToSunmiPrinter(byte[] cmd) {
getCurrentActivity();
Context context = getApplicationContext();
ExtPrinterService ext;
ServiceConnection serviceConnection = new ServiceConnection() {
#Override public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
try {
ExtPrinterService ext = ExtPrinterService.Stub.asInterface(service);
int ret_code;
int zz=0;
ret_code=ext.printerInit();
while(ret_code==-1){
zz++;
if (zz>100) { Toast.makeText(context, "ERROR! / "+ret_code, Toast.LENGTH_SHORT).show(); break; }
ret_code=ext.printerInit();
}
ext.sendRawData(cmd);
ext.cutPaper(1, 0);
ext.flush();
} catch(Exception ex){
Toast.makeText(getApplicationContext(), "ERROR! "+ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
#Override public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
}
};
Intent intent = new Intent();
intent.setPackage("com.sunmi.extprinterservice");
intent.setAction("com.sunmi.extprinterservice.PrinterService");
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
I am getting a failure when trying to open an android activity while the application is closed. See in the code below that, when I receive a notification of data from firebase, while the app is in the background, I should open an activity using MethodChannel to access java, but I get this error:
No implementation found for method openActivity on channel com.example.service/start
Application.java
package com.example.mobile;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
AndroidManifest.xml
<application
android:name="com.example.mobile.Application"
android:label="mobile"
android:icon="#mipmap/ic_launcher">
MainActivity.java
package com.example.mobile;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.service/start";
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if(call.method.equals("openActivity")){
openActivity();
result.success("open activity");
}
}
);
}
void openActivity(){
Intent i = new Intent(this, SecondActivity.class);
startActivity(i);
}
}
main.dart
_firebaseMessaging.configure(
onMessage: (message) async {
//
},
onLaunch: (message) {
//
},
onResume: (message) {
//
},
onBackgroundMessage: myBackgroundMessageHandler,
);
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
MethodChannel channel = new MethodChannel("com.example.service/start");
if (message.containsKey('data')) {
final dynamic data = message['data'];
var open = await channel.invokeMethod("openActivity");
}
}
Where am I going wrong, and how can I make it work?
In your AndroidManifest.xml file the android:name must be android:name=".Application", And make sure that MainActivity.java and Application.java are in same folder
I want to have a background service, which will stay alive after the app is closed and which I can bind to again when the app is started.
For testing I made it that a counter will increase every time I bind to the service.
So theoretically the app should start, I will create the service, then bind to it -> the counter should move up.
Then I close the app and press the Bind button again and It should log a "1" and move the counter up again.
But it doesn't ...
It will display a 0 every time I restart the app and bind to it ...
This is my current Test - Service - class:
package com.programm.testapp;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class TestService extends Service {
/*
* Service Binder
*/
private final IBinder iBinder = new TestService.LocalConnectionService();
public class LocalConnectionService extends Binder {
public TestService getService(){
return TestService.this;
}
}
/*
* Test var
* It should increase every time the app is started.
*/
private int test;
#Override
public IBinder onBind(Intent intent) {
Log.d("mDEBUG", "Test: " + test);
test++;
return iBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("mDEBUG", "Service: Start Command");
return START_STICKY;
}
}
This is my current Test - Activity:
package com.programm.testapp;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private TestService service;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button createButton = findViewById(R.id.button_create_service);
createButton.setOnClickListener(this::createService);
Button destroyButton = findViewById(R.id.button_destroy_service);
destroyButton.setOnClickListener(this::destroyService);
Button bindButton = findViewById(R.id.button_bind_service);
bindButton.setOnClickListener(this::bindService);
Button unbindButton = findViewById(R.id.button_unbind_service);
unbindButton.setOnClickListener(this::unbindService);
}
private void createService(View v){
Intent intent = new Intent(this.getBaseContext(), TestService.class);
startService(intent);
}
private void destroyService(View v){
Intent intent = new Intent(this.getBaseContext(), TestService.class);
stopService(intent);
}
private void bindService(View v){
Intent intent = new Intent(this.getBaseContext(), TestService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
private void unbindService(View v){
unbindService(serviceConnection);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("mDEBUG", "Connection: on service connected");
MainActivity.this.service = ((TestService.LocalConnectionService) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("mDEBUG", "Connection: on service disconnected");
}
};
}
This is my AndroidManifest.xml - file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.programm.testapp">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".TestService"
android:enabled="true"
android:exported="false"></service>
</application>
</manifest>
This is my output after I ...
Pressed Create Service - Button
Pressed Bind Service - Button
Pressed Unbind Service - Button
Close App and Restart it
Pressed Bind Service - Button
:
.../com.programm.testapp D/mDEBUG: Service: Start Command
.../com.programm.testapp D/mDEBUG: Test: 0
.../com.programm.testapp D/mDEBUG: Connection: on service connected
.../com.programm.testapp D/mDEBUG: Service: Start Command
.../com.programm.testapp D/mDEBUG: Test: 0
.../com.programm.testapp D/mDEBUG: Connection: on service connected
By the way the second "Service: Start Command" is called as I CLOSE the app ... after a few new Logs I noticed, that also the Constructer and the "onCreate" method of the Service - class will be called with it.
Is this normal?
Edit:
When I only minimize the App and not close it via Activity - Menu the behavior is exactly the one I want!!!
Edit 2:
A Foreground service does the job for now ...
I couldn't find any other solution for this
If you actively close the app (by closing it from the Android activity list), Android will most likely kill your service. You can see that in your apps Logcat. The only real way around that is a foreground service.
Furthermore, onBind will not be called every time you bind to the service. From the Android documentation:
You can connect multiple clients to a service simultaneously. However, the system caches the IBinder service communication channel. In other words, the system calls the service's onBind() method to generate the IBinder only when the first client binds. The system then delivers that same IBinder to all additional clients that bind to that same service, without calling onBind() again.
Secondly, just that onStartCommand is called does not mean the service is recreated. It can be called multiple times during the service life cycle. For instance, each time startService is called, onStartCommand is executed, but the service is not necessarily recreated.
Also, it looks like you do not un-bind the service when closing the activity. That makes your activity leak the ServiceConnection and your app crash. It would explain why you see the service re-created every time you close and re-start the app.
Try adding an unbind in your activity's onPause method:
#Override
void onPause() {
super.onPause()
unbindService(this.serviceConnectino)
}
A working configuration could look like below. It implements incrementing the counter using a dedicated service function, rather than onBind:
MyBoundService.kt
package com.test
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
class MyBoundService : Service() {
abstract class MyBinder: Binder() {
abstract fun getService(): MyBoundService
}
val iBinder: MyBinder = object: MyBinder() {
override fun getService(): MyBoundService {
return this#MyBoundService
}
}
private var counter = 0
fun increment() {
counter ++
Log.i("MyBoundService", "Counter: ${counter}")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("MyBoundService", "startCommand");
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(p0: Intent?): IBinder? {
counter++
Log.i("MyBoundService", "Bound: ${counter}")
return iBinder
}
override fun onUnbind(intent: Intent?): Boolean {
Log.i("MyBoundService", "Unbound")
return super.onUnbind(intent)
}
}
MainActivity.kt
package com.test
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import com.test.MyBoundService
class MainActivity : AppCompatActivity() {
private val serviceConnection: ServiceConnection = object: ServiceConnection {
override fun onServiceDisconnected(p0: ComponentName?) {
Log.i("MainActivity", "Service disconnected")
}
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
Log.i("MainActivity", "Service connected")
p1?.let {
(p1 as MyBoundService.MyBinder).getService().increment()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_create.setOnClickListener {
val i = Intent(this#MainActivity, MyBoundService::class.java)
startService(i)
}
btn_bind.setOnClickListener {
val i = Intent(this#MainActivity, MyBoundService::class.java)
bindService(i, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onPause() {
super.onPause()
unbindService(serviceConnection)
}
}
The main thing i just want to do is that i have to check on me webservice when my application is closed when it gets any alert on webservice then it shows notification or activity.
I read about services and make an example but when i remove the application from recent apps then some time the service is restarted or some the service got killed to.
Code:
package com.usamaakmal.startedservice;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.widget.Toast;
public class NewService extends Service {
public NewService() {
}
int i = 0;
Handler handler = new Handler();
Thread runnable = new Thread() {
#Override
public void run() {
i++;
Toast.makeText(getBaseContext(), "Hello World! " + i, Toast.LENGTH_SHORT).show();
handler.postDelayed(this,1000);
}
};
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
handler.postDelayed(runnable,2000);
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
Removing app from recent apps list kills app process. Returning START_STICKY from onStartCommand() means you want the system to restart the service after it was killed (either because you removed the app from recents list or system killed service because it ran out of resources).
If you want to keep your service (and it's process) alive you will need foreground service.
System tries to keep foreground services alive as long as possible.
See: Running service in foreground
I am trying to get my Android app to respond to changes in the local address book and sync with it on a need basis.
I understand that in order to do that, I need to create a service to run in the background. I did.
But it isn't working... when my app runs and I try to initiate the service, it fails with :
[exec] [DEBUG] I/ActivityManager( 60): Starting activity: Intent { cmp=ti.test/my.Activity (has extras) }
[exec] [DEBUG] E/aas ( 1812): (main) [490639,495460] people uri:content://contacts/people
[exec] [DEBUG] W/ActivityManager( 60): Unable to start service Intent { cmp=ti.test/.MyService }: not found
this is how I try to run it:
startService(new Intent(activity, MyService.class));
this is part of my manifest.xml fie:
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest android:versionCode="1" android:versionName="1">
<uses-sdk android:minSdkVersion="4"/>
<supports-screens android:anyDensity="true"
android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
<application>
<service android:name="ti.test.MyService"/>
</application>
</manifest>
</android>
this is the service class:
package ti.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.database.ContentObserver;
import android.provider.Contacts.People;
public class MyService extends Service
{
private static final String TAG = "MyService";
private class MyContentObserver extends ContentObserver {
public MyContentObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.e ("DatabaseTable" , "****************************** contact database change detected *************************************");
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.d("MyService", "starting........");
MyContentObserver contentObserver = new MyContentObserver();
getContentResolver().registerContentObserver (People.CONTENT_URI, true, contentObserver);
}
#Override
public void onDestroy() {
Log.d("MyService", "stopping........");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onStart(Intent intent, int startid) {
Log.d("MyService", "onStart........");
}
}
thanks!
you should not create class in service.
you should define a contectResolver class and register it in an activity or where you want to use it.