My Discovery class extends Service class. When I try to get its singletone from other class this way:
Discovery discovery = Discovery.getInstance();
I get a NullPointerException. This is the Discovery code:
public static Discovery getInstance(){
if (discovery == null){
discovery = new Discovery();
discovery.initDiscovery ();
}
Log.i(TAG, "get discovery instance");
return discovery;
}
public Discovery() {
}
private void initDiscovery(){
mDiscoveredDevices = new ArrayList<String>();
BluetoothManager bluetoothManager = (BluetoothManager) discovery.getSystemService(Context.BLUETOOTH_SERVICE);<--NullPointerException
....
}
This is not Android my friend.
To create a service you need to declare it in manifest:
<service
android:name=".DiscoveryService" />
After which you can instantiate it but never using operator new. Instead you need to call:
startService(context, new Intent(context, DiscoveryService.class);
There are other ways of firing a service intent but this will suffice for now.
The service's construction code should be placed at onCreate:
class DiscoveryService extends Service {
#Override
public void onCreate() {
service construction code here
}
}
And its request handling code in onStartCommand:
public int onStartCommand(Intent intent, int flags, int startId) {
handle incoming intent
}
Now, if you do need to access a service instance probably the simplest way
of achieving it would be to declare and maintain a static instance reference within
the service. Do it like this:
class DiscoveryService extends Service {
private static DiscoveryService inst; // <-----------------
public DiscoveryService getInstance() {
return inst;
}
#Override
public void onCreate() {
service construction code here
inst = this;
}
#Override
public void onDestroy() {
cleanup code here
inst = null;
}
}
This approach has its shortcomings, but unlike yours, it will work. Still use with care.
Finally - years of writing & reviewing Android code have led me to the conclusion
that what most novice developers want when they ask for Service, is in fact an IntentService..
Please read the docs and make sure you got your class right.
Good luck.
Related
I'm trying to create a service which will run a socket for receiver data when the app is closed.
According to this thread on Github Flutter should provide an abstraction for background execution, flutter doesn't have an abstraction that executes a code in the background, so I'm writing a native code.
The service opens up correctly, but as soon as the app is closed, it gets moved to cache services and after approximately 5 minutes it is ended.
I found this background_service MainActivity.java, but I'm not using the notification example contained in that repository. (The service contained in this repository also gets terminated once the app is closed.
The example plugin for this article as well.
I still don't have a concrete plan to make the socket connection in the service. I actually would like to call the socket_io_client function within the service, sort of like a callback, but I'm not sure if it will work.
So I just want to know if it is possible to keep the service running after the app is closed. If yes, how?
MainActivity.java
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.retroportalstudio.www.background_service";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent forService = new Intent(this, MyService.class);
forService.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.equals("startService")) {
startService(forService);
result.success("Service Started");
}
}
});
}
}
public class MyService extends Service {
// #Override
// public int onStartCommand(Intent intent, int flags, int startId) {
// return START_STICKY;
// }
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I have a loop which do call to service:
context.startService(intent);
In and want to get back the result after the service finish its processing for each request. So I pass an unique id to intent to be able to distinguish the response.
But unfortunately, the startService which call to onStartCommand is not thread-safe. This leads to the response is always the last id, as the intent was changed in later call.
The service code is similar:
public class MyService extends Service {
protected Bundle rcvExtras;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
rcvExtras = intent.getExtras();
// Todo with information in rcv Extra
BaseRestClient restClient = new BaseRestClient(rcvExtras.getString(Constants.INTENT_KEY_OBJECT_TYPE));
restClient.post(data, rcvExtras.getString(Constants.INTENT_KEY_URL), new CallBackHandler(this)); // This is an async call
return super.onStartCommand(intent, flags, startId);
}
private class CallBackHandler extends Handler {
private final WeakReference<MyService> myServiceRef;
public CallBackHandler(MyService myService) {
myServiceRef = new WeakReference<>(myService);
}
#Override
public void handleMessage(Message msg) {
Intent result = new Intent(Constants.WS_CALL_BACK);
rcvExtras.putInt(Constants.INTENT_KEY_STATUS, msg.what);
result.putExtras(rcvExtras);
log.info("Broadcast data");
sendBroadcast(result); // Broadcast result, actually the caller will get this broadcast message.
MyService myService = myServiceRef.get();
log.info("Stopping service");
myService.stopSelf(startId);
}
}
}
How can I make service calling thread-safe?
I can see your problem, this is programatic issue not caused by framework. Here from you call startService until you call stopself, your MyService is singleton, and your rcvExtras is a global variable and will be shared between threads.
It is simple to fix:
Move the declaration of rcvExtras to method scope, here is onStartCommand.
Extend the CallBackHandler to allow your rcvExtras, and use it once callback.
At this time you do not have any varable can be shared, and you safe.
Hope this help.
I intended to work like this:
user switches on a feature: let say weather.
now weather data will come from server every 6 hours and will be shown to widget(remoteview), Now user switches off the feature. then widget should not show the weather or even refresh the data every 6 hours.
there are also 3-4 more features like that.
Now i had created a service to get all required data and than i have passed them to remoteview. For starting service i had used this in TimeOut Activity:
i = new Intent(TimeOut.this, TimeService.class);
i.setAction("com.example.Weather.Idle");
startService(i);
same for stopping service in switch off code:
stopService(i)
This code was working fine in API <=19. But in Lollipop it crashes at starting or stoping service.
I searched a lot in SO and also tried code for Binding or unbinding service but didn't help any.
Please help me with some code rather than just links...
Thanks in advance :)
Starting a service from any activity class
Intent intent = new Intent(MainActivity.this, BackgroundService.class);
startService(intent);
Here is service class code
public class BackgroundService extends Service{
public static Context appContext = null;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
if (appContext == null) {
appContext = getBaseContext();
}
Toast.makeText(appContext, "Services Started", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
Add your logic here. You can do some work here using a thread. You can stop service whenever you want and i hope you will not find any crash.
I have faced similar issue with Service in 5.0. This is probably not the correct answer, but it works. You could try. I use EventBus to communicate with my services. So when I want to stop the service I'd send,
EventBus.getDefault().post(new ServiceEvent(ServiceEvent.STOP_SERVICE));
In the service,
public void onEvent(ServiceEvent event) {
if (event.getEvent() == ServiceEvent.STOP_SERVICE) {
methodToStopService();
}
}
private void methodToStopService() {
// do some stuff
stopSelf();
}
Make sure you register your service for events.
private void registerEventBus() {
EventBus eventBus = EventBus.getDefault();
if (!eventBus.isRegistered(this)) {
eventBus.register(this);
}
}
ServiceEvent class - It's my own class which I use with EventBus.
public class ServiceEvent {
private int event;
public static final int STOP_SERVICE = -1;
public ServiceEvent(int event) {
this.event = event;
}
public int getEvent() {
return event;
}
}
So, I have an app that starts a service. This service starts to scan for bluetooth devices with BTAdapter.startDiscovery(). Further I have a broadcastreceiver which listens for the DISCOVERY_FINISHED action. If that occurs I want to call a method from onReceive() in my service that starts the scanning process again. How am I gonna do this?
Here my receiver:
public class PollingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ScanBTService sBTs = new ScanBTService();
sBTs.startScan();
}
}
and here the service:
public class ScanBTService extends IntentService {
private BluetoothAdapter mBTAdapter;
private PollingReceiver mPollingReceiver;
public ScanBTService() {
super("ScanBTService");
}
#Override
protected void onHandleIntent(Intent intent) {
final BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBTAdapter = btManager.getAdapter();
mBTAdapter.startDiscovery();
}
public void startScan() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBTAdapter.startDiscovery();
}
}
In your onReceive()-method, restart your service using the following two lines. I did not tested it out but it should work like that.
#Override
public void onReceive(Context context, Intent intent) {
//ScanBTService sBTs = new ScanBTService();
//sBTs.startScan();
Intent i = new Intent(getApplicationContext(), ScanBTService.class);
startService(i);
}
You can then remove the startScan()-method, too.
Try this to resolve the method:
context.startService(new Intent(context, SimpleWakefulService.class));
Since you are using an IntentService, you will need to create an intent for the started service to handle.
This can be achieved by the following :
Intent intent = new Intent(context, ScanBTService.class);
startService(intent);
As described here : https://developer.android.com/training/run-background-service/send-request.html
Now, if you are looking to have a service that maintains bluetooth connections, discover devices, send & receive data... If this is the case, then in my experience, i would argue the following points :
Perhaps the best way to go about this (depending on what you're doing of course), would be to have a service running in it's own separate process which would be responsible for all of that. Check : http://developer.android.com/guide/topics/manifest/service-element.html and the tag
android:process
Take advantage of Android's IPC communication feature to pass & receive messages between you're main app thread and your service. Tutorial : http://www.survivingwithandroid.com/2014/01/android-bound-service-ipc-with-messenger.html.
Create & Maintain connection quick guide : http://developer.android.com/guide/topics/connectivity/bluetooth.html#ConnectingAsAClient
Hope it helps
Cheers
I've got a Service in my Android application. During onStartCommand, I pass the Service object to another class. Then, from there, there's a thread that after 30 seconds starts another Service. It is something like this:
public class FooService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyClass mc = new MyClass(this);
mc.testMethod();
stopSelf();
return START_NOT_STICKY;
}
}
And this is MyClass:
public class MyClass {
private Service service;
public MyClass(Service service) {
this.service = service;
}
public void testMethod() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(20*1000);
Intent intent = new Intent(service, BarService.class);
service.startService(intent);
}
catch (Exception e) {
// CATCH!
}
}
}).start();
}
}
Now, as you can see, in FooService I call stopSelf() wich destroys that Service object. By the way MyClass has got a copy of that Service that was passed by value. After 20 seconds, we can start BarService from MyClass. I've tested it and it works but I can't understand why! The way I wrote the code is dirty (for me). Is it correct to start another service from one that was destroyed? Thank you for your help.
I've tested it and it works but I can't understand why
It works today on the environments you tested in. It may not work in all environments (e.g., ROM mods) and may not work tomorrow (e.g., Android OS updates). A destroyed Context, such as your stopped service, should not be used for anything. It happens that presently you can still use it to call startService() later, but that behavior is not guaranteed.
Is it correct to start another service from one that was destroyed?
No. In this case, I fail to see why you need two services in the first place.
I've got a copy of that service
No, you do not.