If I do startActivity() on an Activity in another process; is there a good way to receive a callback at the starting point (starting the new Activity form a background service) that the new Activity really has started? I mean I can make a broadcast but that seems lame. Is there a better way for this?
Basically what you are asking about is ipc. Google it for more info.
To achieve this you need to create a sevice class and bind the two activities to it.
An example Service class would look like this.
public class MyService extends Service{
//create a handler that will be used to handle messages
//this is just an example. Use static handlers
final Handler handler=new Handler(){
public void handleMessage(Message msg){
//I've just created a static feild.Check below.
MyFirstActivity.activity.seconActivityStarted();
}
}
//create a Messenger object
Messenger messenger=new Messenger(handler);
#Override
public IBinder onBind(Intent intent){
return messenger.getBinder()
}
}
Now things are simple.
Now you have to bind the first activity with the servie.
public class MyFirstActivity extends AppCompatActivity{
//for the time being I'll just create a static field that will be used.
//you can use an interface
static MyFirstActivity activity;
//create a ServiceConnection
ServiceConnection connection=new ServiceConnection(/*contents of the service connection */);
public void onStart(){
super.onStart();
activity=this;
bindService(new Intent(this,MyService.class),connection,Context.BIND_AUTO_CREATE));
}
//following method will be called by the handler in the service
public void secondActivityStarted(){
//some code
}
//you have to handle the activity lifecycle with the bound service.
//right now its described here
}
Now the second activity
public class SecondActivity extends AppCompatActivity{
//create a ServiceConnection
ServiceCOnnection serviceConnection=new ServiceConnection(){
//call the handler at onServiceConnected
public void onServiceCOnnected(ComponentName name, IBinder service){
Messenger messenger=new Messenger(service);
//compose a Message object as you like
messenger.send(new Message());
}
};
//bind this activity to the same service
public void onStart(){
super.onStart();
bindService(new Intent(this,com.package.name.MySerice.class),serviceConnection,Context.BIND_AUTO_CREATE);
}
}
That's it. Modify this according to the requirements.
P.S.Above mentioned code is just working structure.
If you do startActivity() on an Activity in same or another process (and assuming the second activity starts), the calling Activity will go in to first PAUSED, and then STOPPED state. Which means no callback would be processed. However, you can call startActivityForResult() instead of startActivity() and receive onActivityResult() callback.
Add a class MyApplication that extends Application and mention in your Manifest too and than put two boolean variables in it.
private static boolean activityVisible;
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
public static boolean isActivityVisible() {
return activityVisible;
}
and so now you can use call activityPaused() when you do startActivity() on onPause() and onStop() of current activity, and when you return back to same activity do call activityResumed() in overriden onResume() method.
Now, by using MyApplication.isActivityVisible() you can get to know whether your Activity is running or paused.
Related
I started an activity with the following code from my service:
Intent cmActivityIntent = new Intent(getApplicationContext(), CallManagementActivity.class);
cmActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(cmActivityIntent);
In the app's manifest the launchMode of the activity is defined as android:launchMode="singleTask".
I've been looking for ways of stopping the activity on the service's onDestroy() but I haven't been able to find any documentation that can help me. And I've only found one way of doing it.
I've seen the aproach of checking on the serviceif the activity is instanced using an activity-class-static-property (public static boolean isInstanced) and sending another intent to the activity with a FINISH_ACTIVITY extra if it is. Then, on the activity's onNewIntent() the flag would be checked and the activity would be finished if the flag was included.
This approach doesn't feel right to me, since intents are supposed to be used start activities and not stopping them.
Does someone know any other way of how to acomplish this?
Many alternatives have been given to solve this issue:
Use an Intent and set an Extra.
Send a broadcast intent from the Service and register a BroadcastReceiver on the Activity.
Use the LocalBroadcastManager (similar to previous alternative)
Bind the Activity to the Service and pass it a Messenger so the Service can use it to communicate with the Activity.
None of them have I found a good solution, in fact, since the service is only accesible from the package, the solution I'm sticking with is the Observer Pattern.
Next you'll find the code I used. The activities implement the Observer interface (my own) and the service the abstract class ObservableService.
ObservableService:
public abstract class ObservableService extends Service {
#Override
public void onCreate() {
super.onCreate();
serviceStarted = true;
observers = new ArrayList<Observer>();
}
#Override
public void onDestroy() {
super.onDestroy();
serviceStarted = false;
}
//---------------------------------------------------------
//
// Singleton methods
//
//---------------------------------------------------------
/**
* Flag set to <code>true</code> if the service is running and
* <code>false</code> if not.
*/
private static boolean serviceStarted;
/**
* Used to check if the service is started.
* #return <code>true</code> if the service is running and <code>false</code>
* if not.
*/
public static boolean isServiceStarted() {
return serviceStarted;
}
//---------------------------------------------------------
//
// Observable methods an constants
//
//---------------------------------------------------------
/**
* Used to notify the observers that the service has been stopped.
*/
public static String UPDATE_TYPE_SERVICE_STOPPED = "service_stopped";
protected static ArrayList<Observer> observers;
public static void addObserver(Observer observer) {
observers.add(observer);
}
public static void removeObserver(Observer observer) {
observers.remove(observer);
}
protected static void notifyObservers(String updateType) {
for (Observer observer : observers) {
observer.onObservableUpdate(updateType);
}
}
}
Observer:
public interface Observer {
public void onObservableUpdate(String updateType);
}
The activities just register themselves as observers at onResume() and unregister at onPause(). The isServiceStarted() is used to finish the activity if the service is already stopped.
#Override
protected void onResume() {
super.onResume();
if (InCallService.isServiceStarted()) {
InCallService.addObserver(this);
} else {
this.finish();
}
}
#Override
protected void onPause() {
super.onPause();
InCallService.removeObserver(this);
}
public void onObservableUpdate(String updateType) {
if (updateType.equals(ObservableService.UPDATE_TYPE_SERVICE_STOPPED)) {
this.finish();
}
}
I chose this because I found it to be a very simple and high-performance alternative.
I have a service, and within this service I have created a class Client that implements Runnable. I call this client in the onCreate of my service using
clientThread = new Thread(new Client());
clientThread.start();
In the client class, I have a long running operation, and have some data that I would like to print to my Activity. In my service, I have a method (sendToUI) that sends data to the Activity, and the Activity uses a handler to receive the data.
Now my question is, how can my Client class use the method(sendToUI), which is in my service, to feed it's data into my Activity?
Thanks in advance for any help.
Update: I did some reading, and found a simple way (in my view) that solved my problem. Here is the process I used.
I added a global variable to my Client class, which I updated constantly in the run() method. I then added a method (getValue) to my Client class, which returned the global variable.
I changed my code to
Client clientthread = new Client();
new Thread(clientthread).start();
in order to start the thread. I then used
int value = clientthread.getValue();
in order to retrieve the current value of the global variable in my Client class. I then called my sendToUi method with the value as its parameter.
You can bind to that service from your activity with a binder that returns the instance of the service. then you can simply invoke whatever method you want with that service instance.
The only problem with this approach is that the binding is done asynchronously, and for a lot of use cases thats going to be a pain! unfourtunately i am not aware of any better approach.
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);
}
}
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
see this for more info.
You need to call the UI Thread, using the method runOnUiThread like following:
runOnUiThread(new Runnable() {
public void run() {
// ... Excecute the code for the Activity here
}
});
Put this code into your class.
I hope it's useful for you.
You should notify your Activity class that some data has to be updated on the UI. You shouldn't update the UI from that class. You may create more problems than solving.
You can either use a Broascast Receiver or, a much simplier solution, use EventBus framework that easily allows you to notify and send data from one thread to another.
Using EventBus takes four simple steps:
Implement any number of event handling methods in the subscriber:
public void onEvent(AnyEventType event) {}
Register subscribers:
eventBus.register(this);
Post events to the bus:
eventBus.post(event);
Unregister subscriber:
eventBus.unregister(this);
I have three classes. "actone", "acttwo" and "actthree". I have a button in "actone". When I click that button, I want to be able to run "acttwo" on a different thread in the background, while my UI takes me to "actthree" and I can do whatever I want there while the code in "acttwo" keeps executing(I'll be doing uploading to a server in "acttwo" that is why I want it to keep running in the background).
if(v.getId() == R.id.button1){
//Start "acttwo" in background on another thread.
Intent i= new Intent(actone.this, actthree.class);
startActivity(i);
}
How do I do that? Do I use a service? If yes, then what's the procedure? How to do that? I'm a newbie at Android. Please help. Thanks!
There are two ways to do this, use a Singleton or use a Service (as you mentioned). Personally I don't like the singleton patterns very much and a service follows the Android patter much better. You will want to use a bound Service which is bound to your Applications context (actone.getActivityContext()). I have written a similar answer to this question however you will want to do something like:
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;
}
}
}
Then from your actone (I'm assuming Activity)
public class actone extends Activity {
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Intent intent = new Intent(this, BoundService.class);
bindService(intent, _serviceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection _serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
BoundService.BackgroundBinder binder = (BoundService.BackgroundBinder)service;
_boundService = binder.getService();
_isBound = true;
//Any other setup you want to call. ex.
//_boundService.methodName();
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
_isBound = false;
}
};
}
Then from ActOne and ActThree (Activities?) you can get the bound service and call methods from actTwo.
You can use a AsyncTask for that. Services are not really useful (much more to code).
I have several activities which use several audio features. For that, I have a MediaPlayer in a singleton java class, so the activities interact with that class and just exist on the media player.
One of the features is to stop automatically the media player after X minutes. So I created a timer in the singleton class and stops perfectly the radio streaming. the problem is that there is no feedback or callback to the running activity. There is a play/stop button wich has to change the image and I do not know how can I capture that onStop event or whatever....or can be called from a single java class the current activity class running, so I could call a function of the activity in order to change the image?
You probably want to use a broadcast receiver for this.
From your singlton class which does the stopping, when your timer stops the music, call this method:
public void broadcastMusicPaused(View v){
Intent broadcast = new Intent();
broadcast.setAction("MUSIC_STOPPED");
sendBroadcast(broadcast);
}
Then, from your controlling activity, set up your receiver like this:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "Music Paused", Toast.LENGTH_SHORT).show();
displayMusicStopped(); //switches images
}
};
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction("MUSIC_STOPPED");
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
First of all, thanks jameo for his answer, sounds pretty good, but i do not know if i will have time to try, i promise i will if i can this week or next time i have a similar issue.
Finally i did the trick this way:
1 - Create a Interface with Method onStopMediaPlayer(); //For example call MediaPlayerStopInterface
public interface MediaPlayerStopInterface {
/**
* Called when the player timer ends
*/
public void onStopMediaPlayer();
}
2 - My activities classes implements the interface switching images.
public class PortadaActivity extends Activity implements MediaPlayerStopInterface{
public void onStopMediaPlayer(){
//Switch images or whatever
}
}
3 - My singletton class has an object of the type of the interface MediaPlayerStopInterface
public class AudioControllerClass { //The Singletton Java Class
private MediaPlayerStopInterface currentActivity;
public void setCurrentActivity(MediaPlayerStopInterface mpsi){
currentActivity=mpsi;
}
}
4 - My activities classes in onResume() do a Singlettonclass.setStoppedPlayerInterface(this), so i always have a reference of the running activitie.
public class PortadaActivity extends Activity implements MediaPlayerStopInterface{
public void onResume() {
AudioControllerClass.getInstance(getApplicationContext()).setCurrentActivity(this); //In every resume the singletton class knows who was the last one in being active
}
}
5 - when timer execute, as i have the activitie class reference, i just call object_StoppedPlayerInterface.stoppedPlayer();
public class AudioControllerClass { //The Singletton Java Class
class TimerRadio extends TimerTask {
public void run() {
if(whatever==true){
currentActivity.onStopMediaPlayer();
}
}
}
}
Finally, i didn't code it, but the callback to onStopMediaplayer in activities must be done with a Handler, if you do not want a "Only UI thread can touch his views" exception :P
It works perfectly :). But i don't know if it is a really bad practice or is not so horrible xD
Anyway thanks Jameo. Yours sound much more elegant :P
Again a question about LocalServices. How do I (re-)bind to an existing Service, after onDestroy()?
The Problem:
I'm binding to a Service and Starting the service from an Activity. I'm Posting runnable Objects to the Binder, for a callback (updating a progressbar) on the UI. When I close this Activity, the OS could end the lifecycle and Destroy the Activity, calling onDestroy(), right? I simulate this, calling finish() in onPause() method. So once I restart the Activity, how to I bind to the SAME Service again? I thought that Services are Singelton, but when I'm trying to re-bind, I get another binder reference. So binder.callbackHandler.post(binder.progressHandler); still has the reference to the old binder/callback/progressHandler, not to my new one.
Even the Constructor of the Service is called again!
Is there any solution to have a progressbar, getting updated by callback objects from the service (working). Closing/onDestroy() the Activity. Come back, and continue the progressbar?
My code is quite large, but recreated the Szenario:
public class MyService extends Service {
private final LocalBinder binder = new LocalBinder();
public class LocalBinder extends Binder implements TestRunServiceBinder {
private Handler callbackHandler;
private ServiceStartActivity.RunOnServiceProgress onProgress;
#Override
public void setActivityCallbackHandler(Handler messageHandler) {
callbackHandler = messageHandler;
}
#Override
public void setServiceProgressHandler(RunOnServiceProgress runnable) {
onProgress = runnable;
}
public void doSomething(){
_doSomething();
};
private void _doSomething(){
while(...){
//do this a couple of times (could take up to 10min)
binder.callbackHandler.post(binder.progressHandler);
wait()
}
}
}
_
public class ServiceStartActivity{
private final Handler messageHandler = new Handler();
private ServiceConnection mTestServiceConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
testRunBinder = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
testRunBinder = (TestRunServiceBinder) service;
testRunBinder.setActivityCallbackHandler(messageHandler);
testRunBinder.setServiceProgressHandler(new RunOnServiceProgress());
}
};
#Override
protected void onStart() {
super.onStart();
// bind to the Service
final Intent serviceIntent = new Intent(ServiceStartActivity.this,
MyService.class);
getApplicationContext().bindService(serviceIntent,
mTestServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
getApplicationContext().unbindService(mTestServiceConnection);
}
public class RunOnServiceProgress implements Runnable {
#Override
public void run() {
//do something on the UI!
}
}
}
I got it now. The solution is to explicit call startService(serviceIntent); before you bind to the Service using getApplicationContext().bindService(serviceIntent,mTestServiceConnection, Context.BIND_AUTO_CREATE);
Reason: When you start a Service with bindService(), it becomes a Bound Service an
runs only as long as another application component is bound to it.
If you start a Service with startService() it can
can run in the background indefinitely,
So if you have e.g. a progessbar on the UI, and you want it to continue updating it, you should start your Service, and bind and undbind it in onResume() / onPause(). But be carfull: Since you started the Service manually, You should also stop it manually. The simplest way to do this is call stopSelf() once the Service did it's work.
This soultion covers a proper binding from an Activity with e.g. an progresss bar to the same Service even after the activity is destroyed or after an orientation change.