I have a service in Android that encapsulates a framework that has a start method. The service boils down to something like this, many things omitted:
public class MyService extends Service {
private IBinder thisBinder;
public MyService(){
thisBinder = new LocalBinder();
}
#Override
public IBinder onBind(Intent intent) {
return thisBinder;
}
public void start(Map<String, Object> options)
{
getDriverManager().start(options);
}
}
I also have a bridging class that makes calls to the service:
public class MyServiceBridge implements ServiceConnection {
private boolean started = false;
private boolean bound = false;
private MyService myService;
public MyServiceBridge(Context context){
this.context = context;
}
public void bindService(){
Intent intent = new Intent(getContext(), MyService.class);
getContext().bindService(intent, this, getContext().BIND_AUTO_CREATE);
getContext().startService(intent);
}
// Here's a sample call, and the one that is relevant
public void start(Map<String, Object> options){
setOptions(options);
if(bound == true){
getMyService().start(options);
}
else{
started = true;
}
}
}
I call the bridge's start method in order to run the service. This works fine, except in this particular situation (so far). The MyApplication class calls the bridge's start method on onCreate:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
getServiceBridge().start(null);
}
}
This, according to the docs is "Called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created.". Indeed it appears to be so, because the service does not start, and instead starts when I close the app (odd, at least). This works if I move the call to an activity's onCreate method, but that's not ideal because I can also stop the service, and I want the service to run for the lifetime of the app. That is, the service should start when the app starts and stop when the app terminates. Does this make sense? Is there another way to do this?
In my opinion, you did a good job when you decided to run service in Application onCreate. but it is strange to hear that service is started when you close the app.
I have done this several times, and service starts in application onCreate which must be called.
Have you checked if your application is alive and run in background? Please make sure that you killed you application before testing. Use Task Killer or something like that, to be sure that application is always freshly started.
Sadly, Android does not have appropriate mechanism to notify you when application is exited, because it is still alive until system decides to kill it and free resources.
Related
I am building an SDK and need to implement callbacks between activities, without actually finish an activity. I previously used onActivityResult to provide results back to caller activity. However, this closes activity and I need to deliver callback, without finishing activity from SDK. My current implementation:
fun initializeSDK(){
SDK.getInstance().initialize(resultsCallbackImpl)
}
val resultsCallbackImpl:ResultsCallback = object : ResultsCallback {
override fun response1() {
}
override fun response2() {
}
};
For example, the client calls initializeSDK() from his activity after the button click. Then the client passes interface as parameter, which is set as a property in SDK singleton. Then I use that interface to return results.
The problem occurs after process death. The interface becomes null, because it is not serialized and I can't return callback to client anymore. How should I edit my code to tackle this issue? Is it even possible?
I know that client can initialize SDK in the application class, then it will be re-set after process death. However, such an approach will result in difficulty for the client to communicate results back to activity from application class.
Update:
Do a right click on the project tree and add a new AIDL file called IMyAidlInterface.aidl:
package com.test.aidlsample;
import com.test.aidlsample.MyData;
interface IMyAidlInterface {
List<MyData> getData(long id);
}
If you need to return objects to your client you need to declare and define them as parcelable and import them in aidl file too, here is the MyData.aidl that should be beside the other aidl file:
package com.test.aidlsample;
// Declare MyData so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable MyData;
and this is MyData.java in the java folder:
public class MyData implements Parcelable {
private long productId;
private String productName;
private long productValue;
public MyData(long productId, String productName, long productValue) {
this.productId = productId;
this.productName = productName;
this.productValue = productValue;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.productId);
dest.writeString(this.productName);
dest.writeLong(this.productValue);
}
protected MyData(Parcel in) {
this.productId = in.readLong();
this.productName = in.readString();
this.productValue = in.readLong();
}
public static final Parcelable.Creator<MyData> CREATOR = new Parcelable.Creator<MyData>() {
#Override
public MyData createFromParcel(Parcel source) {
return new MyData(source);
}
#Override
public MyData[] newArray(int size) {
return new MyData[size];
}
};
}
Now build the project so Stub class gets built. After a successful build continue with the service:
public class SdkService extends Service {
private IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
#Override
public List<MyData> getData(long id) throws RemoteException {
//TODO: get data from db by id;
List<MyData> data = new ArrayList<>();
MyData aData = new MyData(1L, "productName", 100L);
data.add(aData);
return data;
}
};
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
}
and add the service to the sdk manifest. If you are adding sdk as a dependency to the client like: implementation project(':sdk') you don't need to add AIDL files to client. If not, you have to add them and build the client application. Now, only remains to implement the client activity:
public class MainActivity extends AppCompatActivity {
IMyAidlInterface mService;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
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. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IMyAidlInterface.Stub.asInterface(service);
try {
List<MyData> data = mService.getData(1L);
updateUi(data);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
}
};
private void updateUi(List<MyData> data) {
//TODO: Update UI here
}
#Override
protected void onResume() {
if (mService == null) {
Intent serviceIntent = new Intent();
//CAREFUL: serviceIntent.setComponent(new ComponentName("your.client.package", "your.sdk.service.path"));
serviceIntent.setComponent(new ComponentName("com.test.sampleclient", "com.test.aidlsample.SdkService"));
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
} else {
try {
updateUi(mService.getData(1L));
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onResume();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
every time your client activity gets visibility, it gets data from sdk service. Just build your logic over this template. In sdk activity save data to a database and in service query them from database. I've used simple parameters in this sample.
I assumed your sdk is a library in the client app. If not, you need to do some small modifications maybe. And as I mentioned before you can find more details here: Android Interface Definition Language (AIDL). There are lots of samples and even more Q/A here in the SO on the subject. Good luck.
Original: You need to get callbacks from an activity that is currently invisible since your SDK activity is in front, right? To do that you can create a database for your SDK, persist data to your database and get data via an AIDL in the starting activity:
SdkService sdkService;
CallbackData callbackData
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
sdkService = SdkService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
sdkService = null;
}
};
in onCreate:
Intent i = new Intent()
i.setClassName("your.sdk.packageName", "your.sdk.service.path.and.name");
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
and in whenever needed:
if(sdkService != null){
callbackData = sdkService.getCallbacks();
updateUI();
}
Just be careful getting a binder is an async job so if you call bindService and right after call sdkService.getCallbackData you get a NullPointerException. So you might want to move getCallbacks and updateUI inside the onServiceConnected and call bindService in onResume so every time activity becomes visible you would check if there is CallbackData so you can update your UI or whatever.
You cannot use interfaces directly to communicate between activities.
As soon as you start a new activity and new activity becomes visible android OS can kill 1st activity anytime (you can try this with a flag inside developer option "Don't keep activities"). So user of your SDK will complain about certain random "null pointer exception".
So, Now if you want to share data between current and previous screen, you might have to rethought your solution using Fragments.
Exposing your UI using a fragment and communicating back your result to activity which then would update proper fragment which needs the data.
I faced similar issue in one existing app which I was asked to fix. I switched entire app to fragments and single activity, first to release a hot fix.
The problem occurs after process death. The interface becomes null, because it is not serialised and I can't return callback to client anymore. How should I edit my code to tackle this issue? Is it even possible?
This is not possible. If the client process dies, all of its executing code - including your SDK - gets wiped away.
I know that client can initialise SDK in the application class, then it will be re-set after process death. However, such approach will result in difficulty for client to communicate results back to activity from application class.
So what? If the client Activity is restarted, it should call the SDK again to set a new callback instance which you can use from that point forward.
You can use a sharedviewmodel that is bound to both activities; have a mutablelivedata variable that you can observe from the two activities.
ideally on the first activity you can just put the value inside the mutablelivedata variable. Then on the second activity get the activity.
Follow the following link to give you a guideline.
ViewModel Overview
I have an app that the MainActivity has a method (called doUpdate()) that is called from a button hit. This uses the MainActivity's public variables to access a database, perform some updates, and update some records. This works well.
We now need to automate this with a PeriodicTask as well.
I created a GCMTaskManager service as follows:
public class MyPeriodicTaskService extends GcmTaskService {
public MyPeriodicTaskService() {
}
#Override
public int onRunTask(TaskParams taskParams) {
Log.i("MYLOG","Task Running...");
return 0;
}
}
In my MainActivity, onCreate(), I setup the PeriodicTask as follows:
GcmNetworkManager networkManager=GcmNetworkManager.getInstance(this);
PeriodicTask task=new PeriodicTask.Builder()
.setService(MyPeriodicTaskService.class)
.setPeriod(60)
.setFlex(30)
.setRequiresCharging(true)
.setTag("UpdateSchedule")
.build();
networkManager.schedule(task);
By watching the LOG, I know that the onRunTask() fires periodically as I hoped.
Now I need to call my MainActivity method... doUpdate(). Because this method is declared PUBLIC VOID and not STATIC, I can't call it from the services doRunTask(). If I attempt to make it a STATIC PUBLIC VOID then the MainActivity variables can't be accessed properly as needed for the internal processing steps.
How do I get around this... any recommendations?
So, from what I understand, I should use intents to update the UI of an activity from a service.
But, I am a bit concerned about the efficiency of what I want to do.
Theoretically, it shouldn't really pose a serious problem, but I don't want to have a situation in which I wake up my phone, and have 20-30 onTick-s running at once, each time generating a new intent.
What I want (if possible) is for each onTick's intent to overwrite the previous one, so that the receiver only has process one at a time.
I know pending intents have FLAG_UPDATE_CURRENT, but doing things the right way means (if I understand correctly) using a local broadcast, which doesn't work with pending intents.
This turned out to be surprisingly simple.
A pskink suggested in his comment, all you need is a binder:
Create a class that extends the Binder class. Keep in mind that the Binder class implements IBinder, which is why you can pass an instance of your new class as an instance of IBinder (which is something you will be doing later on)
In this class, implement various functions you want your main activity to be able to use. If you want your service to be able to run things from the activity as well, you can pass an instance of an inner class of the activity, which has the relevant functions in it, or even pass a pointer to the activity (this).
Keep in mind: Do these things at your own risk. If, for example, your activity is destroyed - for example, due a change of orientation, the pointers might become worthless and errors will be thrown
I recommend implementing an invalidate inside your new class to help you handle these situations, but perhaps there are better practices I am not personally familiar with.
Now, you have a two-way communication channel with your activity.
Below is some sample code. Note that if you don't want your service to be destroyed when the activity is, you need (as far as I understand it) to also start it using startService (in addition to using bindService)
public class MyService extends Service {
private IBinder yourBinder; //A class that will connect your service and your activity
#Override
public void onCreate() {
super.onCreate();
yourBinder = new YourBinderClass();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return yourBinder;
}
Your binder class:
public class YourBinderClass extends Binder {
private Service thisService;
public YourBinderClass(Service myService)
{
thisService = myService;
}
public Service getService()
{
return thisService;
}
And finally, inside your main activity:
private YourBinderClass yourBinderClass;
private Service yourService;
private ServiceConnection sc = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
yourBinderClass = (YourBinderClass) service;
yourService = yourBinderClass.getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
And to launch the service:
getBaseContext().bindService(new Intent(getBaseContext(),MyService.class), sc, 0)
Note that the service won't be ready immediately, so if your activity is dependent on it, you might want to signal that you are ready to do stuff in onServiceConnected
This is my second post today as fixing the 1st post lead to another problem which I am really struggling on. I want to use Broadcasts and intent to send data packets back from a service to the UI. I managed to bind and start the service successfully
see my other post if you want history and code
The null pointer exception comes on the sendBroadcast() inside the service. The service class does have its constructor re-called after binding the UI to the service. This happens from another class so Context can not be easily used. So I guess the sendBroadcast has no-where to go :(
I suspect this is my problem...the re-calling of the Service constructor after the initial binding. I have onDestroy, onPause and onResume covered with binding an unbinding.
Any ideas or suggestion would be great or maybe I am just going about this wrong is there another way?
EDIT
The previous problem of binding is now solved and due to me being new to the forums a slight delay in accepting the answer...sorry.
The class diagram is like this (it is ported C#code)
Activity doBind (on Curve.class)---> Activity starts a worker class (not treated as a service) Comm.class with a Handler for some comms--> the Comm starts another worker class --> previous worker class finally calls new Curve.class.
It is this last stage Curve.class where the sendBroadcastReceiver() then throws a nullpointer ref becasue the binder is lost.
I tested the broadcastreceiver just with a simple timer cutting out the worker classes in between and it works fine. Problems start when the Curve.class is recalled later further down the hierarchy and the binder gets nulled or "lost".
I removed all references of the binder from Curve except in onBind(). This might not be a good idea. Again the code below does work if used with a simple timer started directly from the UI (no other worker classes).
Some more code here:
The service
public class Curve extends Service
{
private NewCurvePointEventArgs newpoint = null;
private static final String TAG = "Curve";
Intent intent;
int counter = 0;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
IBinder mBinder = new LocalBinder<Curve>(this);
return mBinder;
}
#Override
public void onStart(Intent intent, int startId) {
}
#Override
public void onDestroy() {
}
public Curve(){
}
private void refreshintent(NewCurvePointEventArgs tmp)
{
ArrayList<String> thepoint = new ArrayList<String>();
thepoint.add()//various things added here
Bundle bundle = new Bundle();
// add data to bundle
bundle.putStringArrayList("New_Point", thepoint);
intent = new Intent();
intent.setAction(NEWCURVE_POINT);
intent.putExtra("NEW_POINT", bundle
sendBroadcast(intent);
}
The activity has this code now. the doBind() is called after the onCreate of the activity.
private BroadcastReceiver CurveReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Curve.NEWCURVE_POINT)) {
displaynewpoint(intent);
}
}
};
private ServiceConnection CurveServiceConncetion = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
CurveService = ((LocalBinder<Curve>) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
CurveService = null;
}
};
#Override
public synchronized void onResume() {
super.onResume();
if(D) Log.e(TAG, "+ ON RESUME +");
}
#Override
public synchronized void onPause() {
super.onPause();
if(D) Log.e(TAG, "- ON PAUSE -");
}
#Override
public void onStop() {
super.onStop();
if(D) Log.e(TAG, "-- ON STOP --");
}
#Override
public void onDestroy() {
super.onDestroy();
if(D) Log.e(TAG, "--- ON DESTROY ---");
unregisterReceiver(CurveReceiver);
unbindService(CurveServiceConncetion);
}
public void doBind(){
Boolean tmp;
tmp = bindService(new Intent(this, Curve.class), CurveServiceConncetion, Context.BIND_AUTO_CREATE);//Context.BIND_AUTO_CREATE
IntentFilter filter = new IntentFilter(Curve.NEWCURVE_POINT);
registerReceiver(CurveReceiver, filter);
}
This problem to me is because the Curve.class has its constructor called again after the initial doBind().
Surely there must be a way around this otherwise I have to load my worker classes closer in hierarchy to the UI with the code from Curve.class???
EDIT
Curve is an object that processes data, constants etc sent from an external machine and contains the processed data in arrays.
The logCat did of course exist I just wasn't looking in the right place here it is
ARN/System.err(10505): java.lang.NullPointerException
WARN/System.err(10505): at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:271)
WARN/System.err(10505): at pi.droid.Core.Curve.refreshintent(Curve.java:206)
WARN/System.err(10505): at pi.droid.Core.Curve.AddPoint(Curve.java:400)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$CommMeasurement.AddPoint(CommMeasure.java:363)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$GenericCommMeasurement.TryProcessData(CommMeasure.java:168)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$CommMeasurement.ProcessData(CommMeasure.java:302)
WARN/System.err(10505):at pi.droid.Core.Comm.ClientConnection$timer_tick.run(ClientConnection.java:164)
WARN/System.err(10505): at java.util.Timer$TimerImpl.run(Timer.java:289)
You can also see the chain of the 2 other worker classes I use. The constructor of Curve is called after the bind from CommMeasure.
So this is my problem.
Do I need to totally change how my program is set up or is there another way around this?
FINAL EDIT
This code is brought from c# and Curve used eventhandlers to pass data around. I got rid of all them(java listeners) and used android Handler and broadcastreceiver.
It has been suggested that I should pass the CurveService around but this will be problematic as Curve has multiple constructors. The no parameter 1 for the service and then 1 like this
public Curve(Unit XUnit, Unit YUnit)
{ this.Title = "Curve";
this.finished = false;
this.XUnit = XUnit;
this.YUnit = YUnit;
this.YDirection = CurveDirection.Unspecified;
}
so surely instantiating that would be a problem with CurveService, which has to be like this: public Curve(){} ??
Anyway many thanks for all your help and advice.
Final Edit +1..lol
The UI creates a new instance of ClientConnection, that in turns creates a new instance of CommMeasure and finally CommMeasure creates a new instance of Curve to access Curve.addpoint.
I think this thread and the other linked 1 goes beyond a simple android problem and highlights the difficulties of code porting. Any .Net developer for example reading this will learn some peculiarities of android a lot quicker than I did. There is also good working code in there.
Thanks to all who helped especially Justin Breitfeller
The best thing for you to do is follow the example from the Android APIDemos.
A service to be used like you want to use it:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html
Look at the Binding class inside of this file to see a class that does binding like you should:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html
Finally, if your constructor is being called twice on your service, you aren't binding to your service properly, or perhaps you are unbinding from it and binding to it again unexpectedly.
EDIT
From the stack trace, it appears that CommMeasure needs to have a reference to the instance of Curve that you receive in onServiceConnected.
EDIT 2
If you really want to make your life simple, pass getApplicationContext() to your CommMeasure class and just appContext.sendBroadcast() from that class to send out your point. This will prevent you from requiring a reference to the service in your long-running task.
I need to get a reference to the main Activity from a Service.
This is my design:
MainActivity.java
public class MainActivity extends Activity{
private Intent myIntent;
onCreate(){
myIntent=new Intent(MainActivity.this, MyService.class);
btnStart.setOnClickListener(new OnClickListener(){
public void onClick(View V){
startService(myIntent);
});
}}
MyService.java
class MyService extends Service{
public IBinder onBind(Intent intent) {
return null;
}
onCreate(){
//Here I need to have a MainActivity reference
//to pass it to another object
}
}
How can I do this?
[EDIT]
Thanks to all for the answers!
This app is a web server, that at this moment works only with threads, and I want to use a service instead, to make it work also in the background.
The problem is that I have a class that is responsible for getting the page from assets, and to do this operation I need to use this method:
InputStream iS =myActivity.getAssets().open("www/"+filename);
At this moment my project has only one Activity and no services, so I can pass the main activity's reference directly from itself:
WebServer ws= new DroidWebServer(8080,this);
So, in order to make this app work with a service, what should I change in my design?
You didn't explain why you need this. But this is definitely bad design. Storing references to Activity is the first thing you shouldn't do with activities. Well, you can, but you must track Activity lifecycle and release the reference after its onDestroy() is called. If you are not doing this, you'll get a memory leak (when configuration changes, for example). And, well, after onDestroy() is called, Activity is considered dead and is most likely useless anyway.
So just don't store the reference in Service. Describe what you need to achieve instead. I'm sure there are better alternatives out there.
UPDATE
Ok, so you do not actually need reference to Activity. Instead you need reference to Context (which in your case should be ApplicationContext to not keep reference to Activity or any other component for that matter).
Assuming you have a separate class that handles WebService request:
class WebService
{
private final Context mContext;
public WebService(Context ctx)
{
//The only context that is safe to keep without tracking its lifetime
//is application context. Activity context and Service context can expire
//and we do not want to keep reference to them and prevent
//GC from recycling the memory.
mContext = ctx.getApplicationContext();
}
public void someFunc(String filename) throws IOException
{
InputStream iS = mContext.getAssets().open("www/"+filename);
}
}
Now you can create & use WebService instance from Service (which is recommended for such background tasks) or even from Activity (which is much trickier to get right when web service calls or long background tasks are involved).
An example with Service:
class MyService extends Service
{
WebService mWs;
#Override
public void onCreate()
{
super.onCreate();
mWs = new WebService(this);
//you now can call mWs.someFunc() in separate thread to load data from assets.
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
To communicate between your service and activity you should use AIDL.
More info on this link:
EDIT: (Thanks Renan Malke Stigliani)
http://developer.android.com/guide/components/aidl.html
The AIDL is overkill unless the activity and service are in seperate apks.
Just use a binder to a local service.
(full example here: http://developer.android.com/reference/android/app/Service.html)
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
Agree with inazaruk's comments. But, In terms of communicating between an Activity and a Service, you have a few choices - AIDL (as mentioned above), Messenger, BroadcastReicever, etc. The Messenger method is similar to AIDL but doesn't require you to define the interfaces. You can start here:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html