Updating UI from countDownTimer running in a service - android

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

Related

IBinder between Bound Service and Activity

Maybe I'm just tired. However I am struggling to understand just how the IBinder interface works.
A service has a method call onBind(Intent intent) and this passes some form of the IBinder to a binding activity.
Where I am struggling is how to visualize this. Is an IBinder object a pipe between the service and activity, so that an activity can interact directly with the service? Or is an IBinder an object that is passed to the activity that has information from the Service in it (similar to an intent)?
Edit: The idea is for me to be able to call the getSomeData function from the binding activity. Or rather, the idea is to get the mSomeData obect to the activity.
I have the following classes that I am using, and Im trying to figure out the best way to have the activity request data (serializable if needed though I'd rather not) from the service.
public class MainService extends Service{
private CustomBinder mIBinder;
private SomeDataObject mSomeData;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mIBinder = new CustomBinder();
return super.onStartCommand(intent, flags, startId);
}
#Override
public CustomBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// This is auto generated and I havent changed it yet. I am not
// Experiencing errors as this is not designed to run yet.
throw new UnsupportedOperationException("Not yet implemented");
}
public final SomeDataObject getSomeData(){
return mSomeData;
}
class SomeBinder extends MainEngineBinder{
// All the Auto Generated Stuff
}
}
You don't need to know how IBinder works. Just subclass Binder (Not IBinder) and add some methods to the Binder subclass. These methods can be called from the activity bound to your service.
The easiest way to do this in your case is to have an inner class extend Binder, and that inner class will then be able to access methods/fields from your service and return them to the activity. You look like you're working from a similar template as the Android documentation on bound services, so here's the example from the docs - all the binder does in this case is expose a getter for getting the service itself, and a caller can simply call methods directly on the service:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
The caller calling bind() will need to cast the IBinder to LocalService.LocalBinder. In this case, the IBinder is neither a pipe nor an intent-like object; it is a normal Java-style interface.
Note that all of this holds true if you are not doing IPC. The documentation states that in this case (same application, same process) the call is a normal Java call - you can pass objects, references, whatever you like, because everything is in the same process.
Note that IPC, pipes, marshalling, serialisation and the like are all unnecessary (unless you want your service to be called from other apps or if you use multi-process apps). An Android service is not even run in a separate thread unless you start a new thread explicitly, and does not have it's "own" thread or process - all calls made to binders or the service object itself are made in the same thread as the caller.
In your case, the exact example from the Android documentation seems like the best approach.

Binding and starting a service when application starts

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.

Pass large string-arrays to another Activity

I am trying to pass a large string-array of maybe 2 or 3 MB to another activity. The chunk is not passed and the only thing I can see in the logcat about what happend is ...
!!! FAILED BINDER TRANSACTION !!!
I tried to create my own class which implements Serializable, put a mutator there in which I put my String, then pass the Object reference to intent.putExtra(key, Serializable obj)
the code:
MyClass mc = new MyClass();
Intent intent = new Intent(MainActivity.this, CalculationsActivity.class);
intent.putExtra("mc", mc);
Is there an easy solution to this issue - that is to pass a large string-array to another activity?
class MyClass implements Serializable {
private String[] str;
public void setString(String[] str) {
this.str = str;
}
public String[] getString() {
return this.str;
}
}
I thought that passing just a reference wouldnt cause this. Reference is not more than a memoryaddress
As others have stated, using Local files (in your sandboxed directory) or database entries will probably be the way to go. However, if you want to do remote (e.g. ftp) hosting of the file and still load them when the application loads you should use a Service. (see the Docs).
I have two previous answers explaining services in more depth that you should look at.
How to use threads and services. Android
and
Android Service with multiple Threads
Essentially though there are two types, a bound thread (which lives with either an activity or the application) and intent services (which can always be active, or only active when the app is open). What you would want is probably the former which would look something like what is in the first link.
here is a snippet
public class BoundService extends Service {
private final BackgroundBinder _binder = new BackgroundBinder();
//Binding to the Application context means that it will be destroyed (unbound) with the app
public IBinder onBind(Intent intent) {
return _binder;
}
//TODO: create your methods that you need here (or link actTwo)
// Making sure to call it on a separate thread with AsyncTask or Thread
public class BackgroundBinder extends Binder {
public BoundService getService() {
return BoundService.this;
}
}
}

get Activity's reference from a Service

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

Android: How to communicate and exchange objects with a Local Service?

So far I've a class for a simple local service. What I want to do is to send requests to this local service from different activies in my application. Depending on the parameters of these requests the service will connect via HttpClient to a webserver and receive a JSONObject and return it to the activity. All the HTTP communication is already working within my activity, but I'd like it to run in a separate thread in my local service now.
The source code of my very simple local service looks like this so far:
// BackgroundService.java
package com.test.localservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class BackgroundService extends Service
{
#Override
public void onCreate() {
super.onCreate();
Log.i("BackgroundService", "onCreate()");
Thread thr = new Thread(null, new RunThread(), "BackgroundService");
thr.start();
}
class RunThread implements Runnable
{
public void run() {
Log.i("BackgroundService", "run()");
/* Here the HTTP JSON communication is going to happen */
//BackgroundService.this.stopSelf();
}
}
#Override
public void onDestroy()
{
Log.i("BackgroundService", "onDestroy()");
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId) {
Log.i("BackgroundService", "onStart()");
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
Log.i("BackgroundService", "onBind()");
return null;
}
}
The problem I'm facing now (due to a lack of knowledge) is the communication part Activities <--> Local Service. I found some communication and object exchange examples for a Remote Service using AIDL (Android Interface Definiton Language). However, I'm not sure if I've to go this path. I would simply like to exchange either my own defined objects or if that is not possible then just a JSONObject or if it makes things much easier even just simple String Arrays would work (for now).
Can someone please point me in the right direction and if possible give a simple example of an Object exchange (both ways) with a local service.
Thanks in advance.
I'd suggest using an AsyncTask instead of your own thread in this scenario.
To give an activity a reference to your BackgroundService object, your options are:
Use a singleton (i.e., make your BackgroundService object available from a static context, such as a public static data member, nulling out that static reference in onDestroy()).
Follow the LocalServiceBinding API sample to return an IBinder that simply gives access to the BackgroundService object.
From there, everything is in the same JVM, so you can just pass objects around. Be careful, though, not to hold onto anything past the component lifetime (e.g., Activity should not cache the singleton).

Categories

Resources