Changing a TextView in another class from a Service - java

I have two classes, one is a standard activity, the other a service. I would like the service to update a TextView (via .setText()) of the Activity on certain events that happen within the service.
I've tried to achieve this by programming something like a setter-method inside the Activity-Class, but TextViews don't accept a static-reference, and when I try invoking an instance of the Activity-class instead (via MyActivityClassName aVariable = new MyActivityClassName();), I get a NullPointer Exception, even though the View in questions is visible at the time of the call.
Could anyone tell me what I'm doing wrong :-)? It is probably more of a basic Java question than an Android-one, but since it might have to do with the nature of Android-services, I've still added the android-tag.
Thanks for your help!

I would like the service to update a
TextView (via .setText) of the
Activity on certain events that happen
within the service.
I would strongly recommend greater logical decoupling. Have the Service tell the Activity to update the TextView.
You can do that via a broadcast Intent, or by having the Activity provide a listener object that the Service calls. In either case, be sure the Activity detaches from the service (unregisters the listener or broadcast Intent receiver), lest you cause a memory leak.
And, of course, this only works if the activity is actually running.

Related

Why does intent requires context?

While working with different activities and starting them for results I have no choice but to use intent. Now intent requires context and that makes no sense to me. I know that context allows access to the application resources but
why do you need to know about the application resources when an intent is just a simple messenger?
Also, I am not so sure why some people create intent with the getApplicationContext() while other use this for the activity context????
Lastly, I am not so sure how the activity that calls for startActivityResult() receive a call back on the method onActivityResult() when I don't pass the "this" for the context but instead the application context. I thought that you have to use the "this" or passing in the current activity context that called startActivityResult() in order to receive a callback. That is just straight up java right? If you pass in a class then the other activity class will have a reference to your class and hence allows it to call the method in your class which is onActivityForResult(). However, this is not the case so what am I missing?
Intent itself does not need the Context. The constructor Intent#Intent(Context, Class) is just a convenience constructor, that internally uses the provided arguments to derive a ComponentName. ComponentName is in turn just a package name of your app and a class name to target. So ComponentName might be something like:
com.foo.bar/com.foo.bar.ui.activity.MyActivity
However, you can as well just use an empty constructor Intent#Intent() and provide ComponentName yourself (Intent#setComponentName(ComponentName)).
Therefore it doesn't matter if you provide your Application's or your Activity's context (the latter is just simpler to type). Also keep in mind that classes that require application context can call Context#getApplicationContext themselves, so this is not something you need to worry about.
About startActivityForResult() - Android manages internally a stack of your Activity records. Therefore it delivers the result to the previous Activity on the stack. It is the same way it knows where to return, when you click "back".
Please note it doesn't mean it maintains a stack of your Activity instances. These instances might be long gone - destroyed and garbage collected to free the memory. However the stack contains the information that allows to recreate them and to restore their state.
Intent does not need Context for itself but as you yourself pointed out that Intent is just a messenger. It also passes the current state of application/object to the newly created object so that it can understand that what exactly is going on in the application. And that is why we need to pass the context.
And, I believe that you want to ask about startActivityForResult(). Android itself takes care of the callback in the same way other callbacks are handled. You can take the example of Activity Life-cycle. Whenever it is started onCreate(), onStart(), onResume() are called itself by Android.
Not been much deep into Android development but still let me try with an explanation. So basically, context is a reference to linking your resources to your program. Each object is given its own context, which contains the resources required to set that object up. It is required for many objects to be created, and to get program identifying information, among other purposes. This makes it invaluable to set up new views and activities, but it can also be used for other purposes. See this Android Context for more information.
According to this page Activity inherits context. Thus, if you are in an activity, you only need to pass itself to use the context. It also contains a pointer to getBaseContext(). You might occasionally need to reference that, if you need the entire application context, but most likely you won't for a while.
There are ways of creating an Intent which do not require a Context. But if you want to target a specific class of a specific package, then providing a context for the target package is a ready way to do that. Refer this Context Lesson.
As explained by snctin in his answer getApplicationContext() offers application context. Basically the Application context is associated with the Application and will always be the same throughout the life cycle of your app. Also refer this post.
See Android - How to start (display) a new Activity. According to it
startActivity(new Intent(this, ProjectsActivity.class));
assumes your current class extends one of the Android Activity classes, which gives you access to the startActivity method.
According to Getting result from a activity, Starting another activity doesn't have to be one-way. You can also start another activity and receive a result back. To receive a result, call startActivityForResult() (instead of startActivity()).
For example, your app can start a camera app and receive the captured photo as a result. Or, you might start the People app in order for the user to select a contact and you'll receive the contact details as a result. This post will help you understand the same more better way.
Hope that helps. And also thanks becuase of your question I had a refresh with Android.:)

Android: How do you pass a callback to an activity?

I am creating a library. It creates an activity, starts it, and finishes it after a button is pressed.
After the button is pressed, I'd like to execute some client code through an interface.
That is, users of this library pass in an implementation, and I simply execute it.
The analogous iOS code would be:
MyViewController *vc = [[MyViewController alloc] init];
vc.callOnComplete = ...
[self presentViewController:vc animated:YES completion:nil];
The desired code is:
public interface SaveStuff {
void save();
}
In my library's Activity:
private SaveStuff saveStuff;
public void onButtonClicked(View v) {
saveStuff.save() // client passes in saveStuff obj
finish();
}
I've tried serialization. I made a class that implemented the interface and also implemented Serializable, and tried passing that in and extracting it with getSerializableExtra. I end up getting a non serializable exception. So far as I know, this is probably not a viable solution, as it would mean that clients must use only serializable classes in their implementations. Correct me if I am wrong on this. In my trials, the client activity was showing up in the non serializable exception.
I've tried using a static reference. Basically global state/singleton. As it turns out, according to a variety of answers such as this one: Java Static Variable becomes null, I cannot rely on any static state, and any solutions that do work with static state are just working coincidently.
I haven't tried simply starting the activity with startActivityForResult, because that would require me to cast the context we are accepting as a parameter to an Activity.
The activity is started inside the library with this:
private void startNewActivity(int newVersion) {
Intent i = new Intent(context, MyActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i); // context variable is passed in by the client
}
As you can see, context is passed in by the client. I could perhaps cast the context to an Activity in order to call startActivityForResult, but I don't want to do this, as I have read that is not always safe. I could require that an Activity be passed in. This is perhaps a reasonable requirement, but I don't want to go there just yet.
I've looked at the existing questions and something I haven't tried is using broadcasts. Correct me if I am thinking about this incorrectly, but the best I could do is have the object that holds the reference to client interface implementation be a broadcast receiver, and then send a broadcast in the activity that I create. I am worried that the receiver will not persist, however. What happens if my library shows the user the activity, but then the user kills the app before pressing the button? The reference to the client code was held by the previous activity. Will nothing happen upon pressing the button?
Should I just broadcast a message, and leave it up to the client to handle these cases? How would the client handle this? Register something on app start up/shutdown? Do I have to force the user to do this to use my library? Is it reasonable?
Update:
As it turns out, we are just going to have the activity in the library access a singleton.
To avoid all the issues with state being wiped based on activities being killed and the like, users of this library will be required to run a 'setup' method on the singleton, and pass in the callback obj there. They will run this on some sort of base Activity's onResume.
I'll wait a little before answering self-answering this question to see if there any alternatives.
Why don't you use same process for android as well i.e.
1) make an interface
2) declare a public variable of interface in your activity.
3) user will initialize that interface when start to launch your activity.
4) use the interface variable to call the method.

Why use Intents in Android? [duplicate]

This question already has answers here:
What is an Intent in Android?
(14 answers)
Closed 9 years ago.
I still don't understand what is so special about Intents. Why not just use a new thread or just call the function? I think I got the whole idea about intents wrong. A simple code showing why Intents are better or when are needed would be great!
Intents are get widely used in android to switch from one activity to other . it is good practice to use intents . Using intents we can pass/send values from one activity to another. So it can be used as value passing mechanism. Also its syntax is very simple.so why to think about threads ?
Intents are asynchronous messages which allow application components to request functionality from other Android components. Intents allow you to interact with components from the own and other applications. For example an activity can start an external activity for taking a picture.
Intents are objects of the android.content.Intent type. Your code can send them to the Android system defining the components you are targeting. For example via the startActivity() method you can define that the intent should be used to start an activity.
An intent can contain data via a Bundle. This data can be used by the receiving component.
To start an activity use the method startActivity(intent). This method is defined on the Context object which Activity extends.
The following code demonstrates how you can start another activity via an intent.
# Start the activity connect to the
# specified class
Intent i = new Intent(this, ActivityTwo.class);
startActivity(i);
Why not just use a new thread or just call the function?
No matter what Thread you use, there would still need to be a mechanism to direct the message which is what an Intent does. It is a way to send a message. Now, it needs to be called on the UI Thread or have an appropriate Context passed because it needs this to send the message. Call what function? You are. It calls a constructor of the Intent class.
A simple code showing why Intents are better or when are needed would be great!
I don't have a simple code to compare to because I'm not sure what you want to see. No one is saying that it is better than something else. As to why Intents are used opposed to something else? I don't know...you would have to ask the developers of the Android platform. That is what they decided to use.
When they are needed is when you want to pass a message from one Activity to another or from one application to another. From the docs
An intent is an abstract description of an operation to be performed
I said "message" but the docs say "description of an operation to be performed" (I guess can mean the same thing). You can use them to start an Activity, pass data between Activities, and more such as telling the OS what to do at boot time. Why is it better? Better than what? That is what the developers decided to use so I guess you would have to ask them but maybe they didn't think it was better rather different.
while I don't have an example, Intents encourage coupling components loosely.
It negates the necessity of you building your own Observer design patterns and enables Inter/Intra-Application communication.

FindViewById in a class type Service?

I'm working with the sample media player given by the android sdk. MainActivity starts Service MusicService with startService(new Intent(MusicService.ACTION_PLAY)).
I need to find a view by ID inside the Service but I don't know how to do it:
findViewById(R.id.playbutton).setVisibility(View.GONE);
I've found some similar questions but none provide a simple solution (the most similar question's accepted answer is actually "no you can't" and I'm sure it's possible). How can I make this line work inside the Service? Do I have to pass the context from MainActivity to it, how do I do it?
Since the service handles media playback the interface should be updated directly before playing/pausing that's why I need to update the ui from it
No, you do not. You need to let the UI know, if it exists, about the state change. The UI will affect its own changes how it sees fit. There may not be any UI at all, depending upon what the user has done.
For letting any affected bits of UI know about the state change, you can:
send a regular broadcast Intent, or
use LocalBroadcastManager to send a "narrowcast" Intent (works a lot like a broadcast, but it is completely within your process), or
use Otto as an event bus

How do a share a large object within an activity in Android?

I have a Network Client class that is receiving a large binary block and parsing it into a usable Java object. The Network Client is on a separate thread from the app's View. What is the best way to make this object available to the View? I've come up with the following solutions, but I feel like none of them are the correct one:
Create the object in the Network Client and let the View access it directly
I would send a small message in a Handler telling the View that the data has been updated
Con: requires that I synchronize the object between the threads to ensure that the Network Client doesn't replace the object while the View is accessing it
Serialize (Parcel?) the object in the Network Client and send it through a Handler to the View
Pro: there are no questions of ownership of the data
Con: would probably be a huge performance drain on the app
Create a reference to the object and pass that to the View
I come from a C++ background, and I'm not sure if this is even possible in Java. I C++, I could just send the View a pointer to the object and let it take care of it. That seems like something Java wouldn't let me do. Is this feasible?
Are any of these solutions advisable, or should I approach the problem in a completely different way?
If you don't want to keep downloading when the activity is in the background, then use non-blocking IO, not threads.
If you do want to keep downloading when the activity is in the background, you probably want to use a service. You can make the object Parcelable or so; I think the underlying service implementation passes pointers around if your activity and service are within the same process (I think they are by default, but ICBW).
If the object is really big and you don't feel comfortable returning it with a get method, maybe you could put its contents into an SQLite database and optionally expose it as a ContentProvider. You could also send an Intent and either cause the View to then go and grab the payload or attach it to the Intent.
Look at the application class subclassing this class and referencing this within your manifest will enable you to store the reference to the service/download controller at a central position that will be available in every activity of your app. This enables you to keep the data in memory and reduce the need of recreating the big object if you need it in more places then just one activity.
For the download you can use a local service that communicates with your activity through a binder object. Keep in mind that a service is not a thread. If you want have the download running in the background you need to create a thread in the oncreate method of your service.
Also keep in mind that it is good practice to have an annotation show the user that a service is doing something and let him access the service and cancel it or view it status.

Categories

Resources