I'm studying Android services because I heard that this is the android recommendation to perform background tasks.
I want to interact with that service, so this is the reason that I've chosen Bound Services (btw, I don't want to run that service indefinitely).
The question is: Why should I complicate myself using the IBinder interface and call my methods via this callback see official docs
#Override
public void onServiceConnected(ComponentName className, IBinder service){}
when I can simply create my custom service? For example, if I want to play some background music in a single Activity I can create this custom (and simple) service:
public class MyOwnService {
MediaPlayer mp;
MainActivity ma;
public MyOwnService(MainActivity mainActivity) {
mp = MediaPlayer.create(mainActivity, R.raw.badinerie);
ma = mainActivity;
}
public void play(){
new Thread(new Runnable() {
#Override
public void run() {
mp = MediaPlayer.create(ma, R.raw.badinerie);
mp.start();
}
}).start();
}
public void pause(){
mp.pause();
}
public void stop(){
mp.stop();
mp.release();
}
}
and call my service in a simplest way like this
public class MainActivity extends AppCompatActivity {
MyOwnService myOwnService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myOwnService = new MyOwnService(this);
}
public void play(View view) {
myOwnService.play();
}
public void pause(View view) {
myOwnService.pause();
}
public void stop(View view) {
myOwnService.stop();
}
}
You can create threads and other objects as you need. But your solution has one big issue - Android OS is not aware about your "service". Remember, android can terminate your app process at any time. Suppose your activity goes into background, from the perspective of android os your app process is a good candidate for killing - it only has one not active activity.
Think of the service like a notification to os that your app is doing something important in the background.
If on the other hand your background work should only happen as long as your activity is active you do not need a service. Just to avoid confusion do not use name service in classes that are not really android services.
EDIT: also note that you can start a service manually and then later bind to it. If you have started bound service manually then it will continue running until you stop it.
Related
I have a service for playing music that extends Service class. It has local MediaPlayer instance and performs music playback without creating working thread. It looks like UI thread is not blocked, I can freely navigate through my app while listening to music. I am a bit confused because on documentation guide it is said that such operation blocks main thread. Could someone explain what`s going on? Should I create working thread inside my service?
A service runs in the same process as the application in which it is
declared and in the main thread of that application, by default. So,
if your service performs intensive or blocking operations while the
user interacts with an activity from the same application, the service
will slow down activity performance. To avoid impacting application
performance, you should start a new thread inside the service.
The snippet looks like this
public class MusicPlayerService extends Service implements MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
...
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
playNext();
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
mMediaPlayer.reset();
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
#Override
public void onCreate() {
super.onCreate();
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnCompletionListener(this);
startForeground(1, mNotification);
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onDestroy() {
mMediaPlayer.release();
}
public void onPlay() {
mMediaPlayer.start();
}
public void onPause() {
mMediaPlayer.pause();
}
}
https://developer.android.com/guide/components/services.html
As we can see in official document of Android:
Service This is the base class for all services. When you extend this
class, it's important that you create a new thread in which to do all
the service's work, because the service uses your application's main
thread, by default, which could slow the performance of any activity
your application is running.
IntentService This is a subclass of
Service that uses a worker thread to handle all start requests, one at
a time. This is the best option if you don't require that your service
handle multiple requests simultaneously. All you need to do is
implement onHandleIntent(), which receives the intent for each start
request so you can do the background work. The following sections
describe how you can implement your service using either one for these
classes.
If you are doing heavy work in service it's possible to block ui and you should create a thread or use intent service for non-blocking ui.
I want to develop an Android App with three activities and two services.
The first Service, named WebClientService, calls a REST API every 30 seconds, using an Handler, and has to notify the active Activity with the result.
It also has to notify a second Service, named DatabaseService, in order to update a local DB.
The Database Service will be called just once onCreate of the activity (in case of app crash and restart) and just once at onRestart (in this way we have data to show in case there were connectivity issues). The activities will then keep themselves updated thanks to the WebClientService that notifies the "alive" activity every 30 seconds.
Questions are:
What's the best way to notify for an update both the active activity and the background DatabaseService?
My idea is to use sendBroadcast() within WebClientService and a BroadcastReceiver in every activity and within the DatabaseService, is it the right approach?
Should I use the same approach for the communication between AllMeetingRoomActivity and DatabaseService or should I use a Bound Service?
Thanks
UPDATE:
DatabaseService won't be a background service anymore but just a shared instance of the db layer between WebClientService and the activities.
So question now is: is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db?
Would that affect the performance too much?
Context:
Follows what I've implemented so far but using SettableFutures and thus needs to be re-implemented using Services and Broadcasts once I've clear how to make them communicate effectively:
public class MainActivity extends AppCompatActivity {
private TextView meetingsTextView;
private EditText mEdit, editSubject;
private final ConnectorInitializer clientInitializer = new ConnectorInitializer();
private AppConnector genericClient; // can use OutlookClient or a test client to talk with a mock server
#Override
protected void onCreate(Bundle savedInstanceState) {
// initializes client based on the settings in "config.json"
genericClient = clientInitializer.create(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
meetingsTextView = (TextView) findViewById(R.id.NowMeeting);
mEdit = (EditText)findViewById(R.id.editText);
editSubject = (EditText)findViewById(R.id.editSubject);
Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
#Override
public void onSuccess(Boolean result) {
Log.d("APP", "-- Logged in. --");
databaseConnector.synchronouslyGetBackupFromLocalDatabase() // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e("\n ~~~~>> logon \n", t.getMessage());
meetingsTextView.setText(R.string.Login_Failed);
}
});
}
/** At the moment the UI is not updated automatically every 30 seconds
* but manually using a refresh button
*/
public void getBookings(#SuppressWarnings("UnusedParameters") View view){
Log.d("APP", "Retrieve button clicked: "+(DateTime.now())+". Calling async getCalendar.");
meetingsTextView.setText(R.string.retrieving_events);
try{
Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<String>(){
#Override
public void onSuccess(final String resultCalendars) {
Log.d("APP", "Success. Result: "+resultCalendars);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("APP", "Calendars SUCCESSFULLY retrieved.");
String meetingsRetrieved = getString(R.string.calendar)+resultCalendars;
meetingsTextView.setText(meetingsRetrieved);
Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
}
});
databaseConnector.asyncUpdateLocalDbWithResults(); // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e( "APP", "Calendar error. Cause: "+t.getLocalizedMessage() );
String retrieveError = "Retrieve error. \n\n\n"+t.getLocalizedMessage();
meetingsTextView.setText(retrieveError);
Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
}
});
}catch(Exception ex){
Log.e("APP","Something went wrong in your code. Cause:"+ex);
}
}
Best option ever:
Use LocalBroadcastManager. More reference here.
MyService.java:
private LocalBroadcastManager localBroadcastManager;
private final String SERVICE_RESULT = "com.service.result";
private final String SERVICE_MESSAGE = "com.service.message";
#Override
public void onCreate() {
super.onCreate();
// Other stuff
localBroadcastManager = LocalBroadcastManager.getInstance(this);
}
Add below method in service, whenever you want to update data from service to Activity, call method by passing Arguments.
private void sendResult(String message) {
Intent intent = new Intent(SERVICE_RESULT);
if(message != null)
intent.putExtra(SERVICE_MESSAGE, message);
localBroadcastManager.sendBroadcast(intent);
}
HomeActivity.java:
private BroadcastReceiver broadcastReceiver;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_home);
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra(MyService.SERVICE_MESSAGE);
// do something here.
}
};
}
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
new IntentFilter(MyService.SERVICE_RESULT));
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onStop();
}
Hope this will help you.
I think your approach is ok with BroadCastReceiver. However, BroadCastReceiver should be used for a global purpose (like communicating between 2 applications). If you intend to use BroadCastReceiver for your app only, I prefer using LocalBroadcastManager instead. Using LocalBroadcastManager is faster and more security when it can be caught only by your app.
There's another way to communicate between your activitys and your services is using EventBus. It will be much easier than using BroadCastReceiver (especially in passing data between them).
Update: About your update question:
is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db? --> Of course NO. You should let your activities update themselves when they need. When you update your local db, you should know that is there any changes or not. If there is any change, use LocalBroadcastmanager to notify your activity to update.
Would that affect the performance too much? --> Yes, that do. The db connection will take time to execute and it will block your UI in some cases. in that case, you should use a thread with ExecutorService for each execute (insert, update...). One more thing to consider is updating that frequently will drain your phone battery very, very fast.
You can bind the services to the activities and update your UI.
Or you can use libraries like Otto or EventBus to create a publisher/subscriber dependency and notify your activities everytime your services publish an update of information.
Use event bus for this communication. EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration.
There are a lot of them:
http://square.github.io/otto/
https://github.com/greenrobot/EventBus
This is an example of Otto usage:
Bus bus = new Bus();
bus.post(new AnswerAvailableEvent(42));
#Subscribe public void answerAvailable(AnswerAvailableEvent event) {
// TODO: React to the event somehow!
}
bus.register(this); // In order to receive events, a class instance needs to register with the bus.
To post from any thread (main or background), in you case a Service and receive events on the main thread:
public class MainThreadBus extends Bus {
private final Handler mHandler = new Handler(Looper.getMainLooper());
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
mHandler.post(new Runnable() {
#Override
public void run() {
MainThreadBus.super.post(event);
}
});
}
}
My goal is to have a thread running that plays a sound then chooses a random animation and a random image and displays them.
It is currently working, but I was wondering if there is a better way. I have a Hacker's understanding of threading (as in, I only know that this works), so I'd appreciate some feedback. Also, I've been having issues with memory overflow in my app, is there a better way to manage this Activity memory-wise? Thank you so much!
public int[] images = {R.drawable.splat0,R.drawable.splat1,R.drawable.splat2,R.drawable.splat3,
R.drawable.splat4,R.drawable.splat5,R.drawable.splat6,R.drawable.splat7,R.drawable.splat8,
R.drawable.splat9};
public int[] anims= {R.anim.splat0,R.anim.splat1,R.anim.splat2,
R.anim.splat3,R.anim.splat4,R.anim.splat5,R.anim.splat6};
MediaManager mp;
Handler tick_Handler = new Handler();
MyThread tick_thread = new MyThread();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
MainActivity.this.setContentView(R.layout.activity_main);
mp = new MediaManager();
image = (ImageView)this.findViewById(R.id.mainActivitySplat);
tick_Handler.post(tick_thread);
}
#Override
public void onStop(){
tick_Handler.removeCallbacks(tick_thread);
super.onStop();
}
#Override
public void onResume(){
tick_Handler.post(tick_thread);
super.onResume();
}
private class MyThread implements Runnable {
#Override
public void run() {
mp.playSoundClip(MainActivity.this,R.raw.swoosh);
image.setBackgroundResource(images[(int)(Math.random()*splats.length)]);
Animation myAnim=AnimationUtils.loadAnimation(MainActivity.this,splatAnim[(int)(Math.random()*splatAnim.length)]);
splat.startAnimation(myAnim);
tick_Handler.postDelayed(tick_thread, 3500);
}
}
Edit:
I have discovered this is a BAD way of using the Thread. MyThread holds an implicit reference to the Activity, and causes a massive memory leak. By changing the class to private static MyThread I solve the leak, but I have not yet figured out how to get the desired behavior this way. Will update later.
use a flag like
boolean isActibityKilled=true //when in onstop
use it in the runnable to check if activity is running or not if activity is not running , or it is stopped then kill your thread
My application turned black screen right after I initialize service in my activity. No error or warning message was shown. Awhile later the application is already not responding and even after the time limit the service did not update info to the server. I wanted to use the service to send user's location constantly to the server after every minute. Service is initialize in my activity. I also included the service in AndroidManifest.xml as well.
LocationService
The code below is my structure of Service
public class LocationService extends Service{
#Override
public IBinder onBind(Intent arg0){
return null;
}
private String TAG = "LocationService";
#Override
public void onCreate(){
super.onCreate();
boolean isUpdateResult = true;
try{
while(isUpdateResult){
new CountDownTimer (60000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
//Send information to server
}.start();
}
}
finally{
}
}
#Override
public void onDestroy(){
super.onDestroy();
}
}
Below is how I call the service in my main activity.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.display_screen);
startService(new Intent(Web_Slider.this,
LocationService.class));
}
Any idea what causes the black screen? Did I missed out something here?
Any comments will be appreciated! Thank you in advance.
I managed to solve this. Apparently if I run new CountDownTimer (60000, 1000) in the beginning of the loop, the screen will turned into black screen. If not mistaken is due constantly create new CountDownTimer cause it unable to function as it should. The right way to put it is take away the While(isUpdateResult). The function will constantly start a count down timer because there is a .start(); after onFinish();. So there while loop is not needed.
new CountDownTimer (60000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
//Send information to server
}.start();
Based on a suggestion in a previous question I asked on here, I'm trying to push my socket connection for an application that I've written into a service. I spent the better part of the day yesterday researching services and actually mocked up a few (one remote, one local).
My question is in two parts:
1) after having played with both a local service and a remote service, I'm still not sure as to which one would be best for my situation. This is due in large part to the fact that I guess I still don't quite understand what advantages running in another 'process' is going to give me. I'm spawning a new thread for the socket connection no matter what so I won't have any thread contention with the UI. So what does putting the service in another process enable me to do? Will I potentially see better performance that way? My limited understanding is that by putting it in a different process, the service will run independently of whatever activity I have running on my app. I do have a few different activities, but only one of them requires the socket connection which I will rebuild everytime that activity is opened anyway. So would a local service be the way to go for me?
2) I'm going to have my socket "listener" (DataInputStream().readLine() inside a while loop) inside my service for any new data that gets passed down from the server. After the playing I did yesterday, I could not figure out how to pass the data that it reads to the actual "client" (either bound client by remote service, or local client itself) in "realtime".
Would greatly appreciate some suggestions for part 1, and some help with part 2 (code examples? :))
TIA
Edit: added code of my service - going with local service
Service Class:
public class SocketService extends Service {
Socket s;
PrintStream os;
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return myBinder;
}
private final IBinder myBinder = new LocalBinder();
public class LocalBinder extends Binder {
public SocketService getService() {
return SocketService.this;
}
}
#Override
public void onCreate() {
super.onCreate();
s = new Socket();
}
public void IsBoundable(){
Toast.makeText(this,"I bind like butter", Toast.LENGTH_LONG).show();
}
public void onStart(Intent intent, int startId){
super.onStart(intent, startId);
Toast.makeText(this,"Service created ...", Toast.LENGTH_LONG).show();
Runnable connect = new connectSocket();
new Thread(connect).start();
}
class connectSocket implements Runnable {
#Override
public void run() {
SocketAddress socketAddress = new InetSocketAddress("192.168.1.104", 4505);
try {
s.connect(socketAddress);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s = null;
}
}
Activity that calls service:
public class SocketServiceController extends Activity {
private SocketService mBoundService;
private Boolean mIsBound;
public SocketServiceController ssc;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ssc = this;
setContentView(R.layout.main);
Button start = (Button)findViewById(R.id.serviceButton);
Button stop = (Button)findViewById(R.id.cancelButton);
start.setOnClickListener(startListener);
stop.setOnClickListener(stopListener);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mBoundService = ((SocketService.LocalBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
mBoundService = null;
}
};
private void doBindService() {
bindService(new Intent(SocketServiceController.this, SocketService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mBoundService.IsBoundable();
}
private void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
private OnClickListener startListener = new OnClickListener() {
public void onClick(View v){
startService(new Intent(SocketServiceController.this,SocketService.class));
doBindService();
}
};
private OnClickListener stopListener = new OnClickListener() {
public void onClick(View v){
stopService(new Intent(SocketServiceController.this,SocketService.class));
}
};
}
This is due in large part to the fact that I guess I still don't quite understand what advantages running in
another 'process' is going to give me.
Generally, none. You create a remote service if you are expecting other applications to communicate with the service. If it will only be used by your own application, use a local service.
Also, a remote service has nothing to do with creating a separate process within your application.
Will I potentially see better performance that way?
You will see worse performance that way, due to extra memory consumption.
My limited understanding is that by putting it in a different process, the service will run independently of
whatever activity I have running on my app.
Services have a lifecycle independent from activities regardless of whether it is local or remote.
So would a local service be the way to go for me?
Sounds likely.
After the playing I did yesterday, I could not figure out how to pass the data that it reads to the
actual "client" (either bound client by remote service, or local client itself) in "realtime".
Use the local binding pattern, and have the activity call an API on the service to register (and unregister) an event listener. Have the service pass the data to the activity via the listener.