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).
Related
My question is exactly as it says on the title. Why is it that Android bound services always load after a whole fragment lifecycle is complete? Bound services are meant to be loaded on the activity containing the fragment, so why is it that the service is only available after the fragment is created? Sometimes I want to use the service to populate things in the fragment and needed to recur to "hacks" to get to use the service.
I load the service connections in the onCreate() method in the activity and start the service in OnStart() as described by the documentation https://developer.android.com/guide/components/bound-services but then I have to create a "loading" method in the fragment to load stuff once the service finished loading.
private void loadServiceConnections()
{
metronomeConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
MetronomeService.LocalBinder localBinder = (MetronomeService.LocalBinder) service;
metronomeService = localBinder.getService();
metronomeService.setLinkManager(notificationsMetronomeLinkManager);
metronomeService.setListener(myFragment);
metronomeFragment.onServiceLoading(); //this is the method that executes inside the fragment once the service is available.
metronomeServiceIsBound = true;
if (loaded && metronomeService.isMetronomePlaying())
metronomeRunning = true;
}
#Override
public void onServiceDisconnected(ComponentName name)
{
metronomeServiceIsBound = false;
}
};
}
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.
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 am new to android and I am now doing an exercise which the application has already stored a audio file(mp4) in /res/raw/ folder and this file can be referenced in android Service class as R.raw.audiofile. In the Service class I have created three methods
onCreate();
onStartCommand();
onDestroy();
in onCreate() I have created a MediaPlayer and in onStartCommand() I have started the MediaPlayer
mp.start() and
returned STRT_STICKY
to play it and in onDestroy() I have done this:
mp.stop();
in the xml layout I have created a Button with this attribute:
android:onClick="onClickStart"
which calls a method in the MainActivity and this action now shoud playback the audio. however, I am now stuck here on how to link this to the music file so that this button should start the audio. can anyone please give me some idea?
so I have one Main activity class and one (My)Service class (extends to Servcie) and one xml file for layout to perform this action.
You can communicate between the Activity and Service using an Intent or by binding to the Service and sending a Message. In this case, binding to the Service and sending a Message in the Button's click listener is probably the cleanest approach. That also gives you the opportunity to appropriately update UI by passing a Messenger/Handler in the replyTo field of the Message. I'll edit with code snippets when I get to a PC.
Edit:
Most of the code you need is in the Android docs here.
The basic idea is that you need to create a Handler class inside your Service. The proper way to do this (to avoid leaking the Handler and the accompanying Lint warning) is as follows:
private static class MyHandler extends Handler {
private WeakReference<MyService> mService;
public MyHandler(MyService service) {
mService = service;
}
#Override
public void handleMessage(Message msg) {
// Your message handling here...
// You can use the members/methods of the Service with
// mService.get().____
}
}
Then you would create a Messenger member and override the onBind method in the Service, like so:
private Messenger mMessenger = new Messenger(new MyHandler(this));
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
Then you need to call bindService with an appropriate Intent and ServiceConnection from your Activity. See the link for a code example. In the onServiceConnected method of the ServiceConnection you can stash a Messenger for the Service with:
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mMessenger = new Messenger(service);
}
You can likewise create a Handler and Messenger in the Activity and supply it as the replyTo field of any Message objects sent to the Service. In that way, you can tell the Activity whether the MediaPlayer started successfully or not and update UI as appropriate.
You need a OnclickListener for your Button like
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
Look here for the MediaPlayer
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.