I'm having some problems figuring out how to properly organize a particular bit of android code.
This is the architecture of the code: Inside of an activity's onCreate, addService does some work via bindService, and getServices can be run only once the onServiceConnected methods have successfully completed:
public class MyClass{
List<IBinder> binders = new ArrayList<IBinder>;
int stillSettingUp = 0;
public void addService(Class<?> cls) {
//Adds a binder via bindService
ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
//Callback for service being successfully started
binders.add(service);
stillSettingUp--;
}
};
//Increment count of the number of services being set up
stillSettingUp++;
Intent intent = new Intent(context, cls);
context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
public List<IBinder> getServices(){
while (stillSettingUp != 0) {
Log.w("", "Waiting for services to successfully connect... " + stillSettingUp);
Thread.sleep(1000);
}
return binders;
}
}
Here is the hitch: the second method requires the onServiceConnected functions to complete. The onServiceConnected functions can't execute until the entire onCreate function completes (since they are events that are tacked onto the end of the main loop, and can't be executed until the current event finished), and so the system deadlocks.
Is there a way to force the other events on the UI thread to process, or a better way to orchestrate the code? I'm trying to avoid running an AsyncTask every time I call these two pieces of code together, as this requires exposing threading requirements to the calling code. This is difficult, however, since you can't force the service connection callbacks to execute in their own thread. Any suggestions are welcome.
It looks like what you need is to execute your 3rd function on the UI thread as soon as both your 1st and 2nd functions have completed. So why not to use AsyncTask and put your 1st and 2nd routines in doInBackground() while putting your 3rd routine in onPostExecute()
Here are my takeaways from my question:
1) If you ever have to depend on data from an Android callback, you should not block, since Android callbacks aren't posted to a separate thread, as in other programming paradigms. You should instead gracefully move past the point where you needed the data, possibly reattempting the data access in e.g., a polling thread.
2) You can also pass in a runnable to be executed after the service is connected. This could get very messy, however.
3) DON'T USE TOO MANY SERVICES. It's typically much easier to just use one or two services than it is to use a bunch of services that talk with one another. I rewrote this set of code, and it's 20x more maintainable now that I'm not dealing with bound services constantly.
Related
I know how to save states and restore them, but I just get confused when I have to do work with the Web services and to update UI. For times I was using the AsyncTask but then I came to point where I loose my activity/fragment context for example when I rotate the device. So in this way, I am thinking how other apps are handling such situations.
If I use the IntentService and call my web service from there, then I came to think that for each web service I have to make IntentService differently, and update the UI of each activity and fragment I have to make the BroadcastReceiver for each activity and fragments.
So what is a good practice for calling web service from the activity and the fragments?
How can I Update UI when the service return arrives (or call next service based on first services results)?
If you want your data to be instantly available through configuration changes (which you do), then you probably want to use Loaders.
It gives the developer a mechanism of loading data asynchronously for an activity or fragment. Since loaders are specifically designed to solve the issue of async loading, one does not have to spend too much time designing async tasks to handle all different scenarios efficiently.
Good article about Loaders https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832
Try using retrofit. It's a great networking libraries for Android apps and it's easy to use.
The entire network call + JSON/XML parsing is completely handled by it (with help from Gson for JSON parsing). Documentation is great and the community is huge.
check out this sample.
I noticed a comment you made:
...and my webservices are soap and I cant change them
The way I'm currently calling my web service, which is also SOAP, is via an Intent. I do this by passing in the data that I'm submitting to the Web service with putExtra then receiving it on my WebService, as you probably do right now. I then get the result from that web call and process it inside an AsyncTask, the async task will then utilize EventBus to post to Results as needed which are received on my MainThread via ThreadMode.Main.
So with that said, I highly recommend the use of a library called EventBus from Greenrobot.
You greatly simplify communication between Activities and Fragments, You can get started immediately using a default EventBus instance available from anywhere in your code. For example, you can do the following.
EventBus.getDefault().post(new ModelForOtherActivityToSee(data));
In the model, you can include anything you want, and react accordingly when received.
The best part is that when received, EventBus handles how the data will be executed by either running ASYNC, MAIN, BACKGROUND
ASYNC - Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long-running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
Background - Subscribers will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread.
MAIN -Subscribers will be called in Android’s main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly (synchronously like described for ThreadMode.POSTING). Event handlers using this mode must return quickly to avoid blocking the main thread.
An example of receiving an event broadcasted from EventBus:
//ThreadMode can be ASYNC, MAIN, BACKGROUND
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ModelForOtherActivityToSee eventModel) {
/* Do something with eventModel received, this runs on UI thread */
};
Full example on how to use EventBus:
1 - Open your build.gradle for the app and set your dependency for EventBus:
dependencies { compile 'org.greenrobot:eventbus:3.0.0'}
2 - Create your first model to use in publishing an EventBus, I will use a very simplistic example of a model:
package com.myapp.models.eventbusmodels;
public final class EventBusMyModel {
private final String dataRaw
public EventBusMyModel(final String rawData) {
this.dataRaw = rawData;
}
public String getRawData() {
return this.dataRaw;
}
}
3 - Now all that's left is pushing out a broadcast by using from anywhere.
EventBus.post(new EventBusModel("My Data here"));
4 - To enable Activities/Fragments to receive events from EventBus you must attach and detach, this is what I mean. From inside an Activity on the onResume() and onStop() overrides:
public class SomeActivity {
#Override
protected void onResume() {
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
}
#Override
protected void onStop() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onStop();
}
}
5 - The final thing to do is receive that broadcast, you can receive it in Any Fragment, Activity, or in all your fragments/activities. Here's an example from inside the SomeActivity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void eventThisNameDoesNotMatter(final EventBusMyModel resultModel) {
String receivedData = resultModel.getRawData();
//Do whatever with receivedData. Since we are on ThreadMode.MAIN, this is on the UI thread.
}
I am working on an app where I need to bind to a database via IBinder. I planned on putting any framework initialization in my Application subclass since that kind of stuff isn't presentation related and I know that the Application will be instantiated once per app.
public class MyApplication extends MultiDexApplication {
public void onCreate() {
super.onCreate();
// Bind to the db server
Intent intent = new Intent(getBaseContext(), DB.BIND_ADDRESS);
super.bindService(intent,
new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
DB.start(service);
}
#Override
public void onServiceDisconnected(ComponentName name) {
DB.shutdown();
}
},
Context.BIND_AUTO_CREATE
);
}
}
The problem that I run into is that the call back to my ServiceConnection.onServiceConnected() isn't performed until my activity has started and that's a problem since I need the database available for the activity to display the data. I currently see this behavior.
myApplication.onCreate()
myActivity.onCreate()
myActivity.onStart()
serviceConnection.onServiceConnected()
This is a problem since the database isn't yet connected when the onStart() is invoked on my activity.
I would rather not have to move this infrastructure initialization in the activities. So I have two questions.
Is there a way to block the application.onCreate to wait until
the onServiceConnected() has been invoked? This being single
threaded I don't see how.
Is there a way to tell android not to invoke any onStart() on any
activities prior to my application having completed its
initialization such that I have the following startup sequence
This
myApplication.onCreate()
serviceConnection.onServiceConnected()
myActivity.onCreate()
myActivity.onStart()
or
myApplication.onCreate()
myActivity.onCreate()
serviceConnection.onServiceConnected()
myActivity.onStart()
You'll need to rewrite your code to work with the Android framework. There is no way to wait in onCreate- all the code happens on a single thread, it won't even attempt to start your service until onCreate is finished. I suggest you use onResume or onServiceCreated itself to load the data.
There is no way to do like that.The best solution is to show some type of loading dialog until it connected and fetch from the database
I have a bindservice in onCreate that takes a long time, I tried to solve this problem by moving it to a worker thread, but it still causes the UI to wait. What can I do
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
new Thread(){
public void run(){
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
Intent serviceIntent = new Intent(getApplicationContext(), Recognizer.class);
bindService(serviceIntent, mConnection,Context.BIND_AUTO_CREATE);
}
}.start();
}
From your description, it seems that calling bindService within UI thread is not the root cause of ANR. You should instead, call bindService without spawning user thread. One main reason is that, this causes memory leak.
When there is configuration change, as long the the thread still alive, it doesn't give chance to Activity for performing garbage collection. This is because your user thread is still holding reference to Activity.
The ANR root cause should come from Recognizer or mConnection. If you can post the implementation details of them, this can help us to help you.
Ask yourself the following questions
Are you performing any I/O intensive task in mConnection's onServiceConnected?
Are you performing any I/O intensive task in Recognizer's onCreate?
...
As others have said, the problem is not in your Activity, but in your Service. Your onBind() call is taking too long and onBind() is called on the main (UI) Thread. You are doing too much work in the onBind() call. What you need to do is to move your initialization of your Service out of the onBind() call and into a separate background thread. To prevent the client from using the Service before it has been initialized, you'll need to keep a flag for each bound client which indicates if the background thread has completed the initalization. If the client tries to use the Service before it is ready (ie: completely initialized) then you can return some error code or throw a suitable exception.
Remember that lifecycle calls in all Android components (Activity, Service, BroadcastReceiver, ContentProvider) all run on the main (UI) thread and must run quickly.
The problem is not in your Activity. You said that your bind service takes long time. So you must create thread inside the onBind method of your service.
public class ClientService extends Service
{
#Override
public IBinder onBind(final Intent intent)
{
new Thread(){
public void run(){
// Your code here
}
}.start();
}
}
I am confused with respect to design of my app. I need to continuously poll a server to get new data from it. I am confused whether Async Task running at fixed interval or Service running is background is better option. The thread will run only when the app is running
You have already some answers to your question, but I think it worths a summary ...
What you need
When you want to run a peice of code that takes some time to complete you should always run it in a separate thread from the UI thread.
You can achieve that in 2 ways:
Using Thread:
This is the simplest one, if you don't need a lot of communication from the new thread to the UI thread. If you need the communication, you will probably have to use a Handler to do it.
Using AsyncTask:
Also runs in a separate thread and already implements some communications channels with the UI thread. So this one is preferable if you need this communication back to the UI.
What you don't need
Service
This serves mainly to keep some code running even after you exit the main application, and it will run in the UI thread unless you spawn a new thread using the options described above. You said that your thread are suposed to terminate when you exit application, so this is not what you need.
IntentService
This can be activated by an external event (i.e. BroadcastReceiver) that can start a piece of code defined by you, even if your application is not running. Once again, based on your requirements, this is not what you are looking for.
Regards.
an Android Service is not in a background thread.
Therefore you should have a Service running that will start an ASyncTask each time you want to poll.
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
Service should not be compared to AsyncTask. I guess you most likely meant IntentService here - and this is slightly different thing than Service, despite the common name.
As for periodical fetching, I'd stick with recurrent alarm (using AlarmManager) and (most likely) use IntentService to do the fetching.
Here you got with AsyncTask fundamentals and some tutorials
And here you got with IntentService fundamentals and tutorials
Note, that IntentService jobs are queued by design, while AsyncTasks can run fully paralel. However be aware of regression related to AsyncTask handling in newer APIs. Not a big deal as workaround is just a few more code lines, however it's worth knowing that.
EDIT
There's misunderstanding floating among many concerning AsyncTask lifecycle being bond to Activity's life cycle. This is WRONG. AsyncTask is independent from an Activity. Finishing Activity does not do anything to any AsyncTasks, unless you are cleaning them up from onDestroy() by your code. Yet, if an activity's process is being killed while it is in the background, then AsyncTask will also be killed as well, as part of the entire process being killed
If you want to "continuously poll", an asyncTask won't do. The task stops when your app gets stopped by Android. A Service by itself won't do either, as Blundell already pointed out. A Service runs in the main thread, and you don't want to do polling in the main thread. There's two ways of doing it: you create a Service that spawns its own thread to do the stuff you want it to do, or you let it schedule polls that are executed in an AsyncTask or in a separate thread. I try not to have polling in my app, but if you have to, creating a special thread in your service that does the polling seems best to me.
Depending on what your app does and what the polling is about, you can give the separate thread a lower priority, so it doesn't get in the way of other processing.
The thread will run only when the app is running
Then AsyncTask will be the simplest solution. Send data periodically to app thread using publishProgress() from background thread. Set desired interval using Thread.sleep() in doInBackground(). Also, make sure you start a new task in onResume() method of Activity, and end this task in onPause() method of Activity.
Example:
public class MyActivity extends Activity {
private AsyncTask<Void,String,Void> mAsyncTask;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onResume() {
super.onResume();
mAsyncTask = new MyTask();
mAsyncTask.execute();
}
#Override
protected void onPause() {
super.onPause();
if(mAsyncTask != null){
mAsyncTask.cancel(true);
}
}
private void onServerResponse(String response){
Toast.makeText(this, "Got response !", Toast.LENGTH_SHORT).show();
}
private final class MyTask extends AsyncTask<Void,String,Void>{
#Override
protected Void doInBackground(Void... voids) {
while (!isCancelled()){
String response = "";
//server query code here
publishProgress(response);
Log.i("TEST", "Response received");
//sleep for 5 sec, exit if interrupted ,likely due to cancel(true) called
try{
Thread.sleep(5000);
}catch (InterruptedException e){
return null;
}
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
if(values.length > 0){
onServerResponse(values[0]);
}
}
}
}
I'm asking for help so my life, and more importantly my user's lives will not be ruined by me not knowing how to use Services and Threads correctly.
I'm not asking for a long explanation, but more of a confirmation. It's fine if I'm dead wrong. I'm here to learn.
If I understand correctly:
1. a service runs in the background (no UI).
2. a service theoretically will run forever until it kills itself (I'm taking a big guess here)
3. a service will continue to run even when the main Activity is not visible (how about even destroyed?)
So here's my coding question.
I've got my service setup and a thread. Everything works great, but it only works once. I need it to loop and keep checking back. Once it's done run() how do I go about telling it to run() again?
public class NotifyService extends Service{
private long mDoTask;
NoteThread notethread;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
mDoTask = System.currentTimeMillis();
notethread = new NoteThread();
notethread.start();
}
public class NoteThread extends Thread {
NotificationManager nManager;
Notification myNote;
#Override
public synchronized void start() {
super.start();
//init some stuff
}
#Override
public void run() {
//If it's been x time since the last task, do it again
//For testing set to every 15 seconds...
if(mDoTask + 15000 < System.currentTimeMillis()){
//Take care of business
mDoTask = System.currentTimeMillis();
}
}
}
}
From the Android docs:
A Service is an application component
representing either an application's
desire to perform a longer-running
operation while not interacting with
the user or to supply functionality
for other applications to use. Each
service class must have a
corresponding declaration in
its package's AndroidManifest.xml.
Services can be started with
Context.startService() and
Context.bindService().
Note that services, like other
application objects, run in the main
thread of their hosting process. This
means that, if your service is going
to do any CPU intensive (such as MP3
playback) or blocking (such as
networking) operations, it should
spawn its own thread in which to do
that work. More information on this
can be found in Processes and Threads.
The IntentService class is available
as a standard implementation of
Service that has its own thread where
it schedules its work to be done.
You can find a detailed discussion
about how to create services in the
Services document.
In other words, a service does NOT run in the background unless you put it in a thread. If you put a service that never ends in your application without manually threading the service, then it WILL block.
Android provides an API to do background tasks for you without having to poke around with Java threads; it's called AsyncTask and it's one of the few GOOD design decisions that the Android team has ever made.
EDIT I forgot to address your question about multithreading. You don't want to make a thread execute its run() method more than once. Either instantiate a new thread or put a while loop around the contents of the run logic that you would like to have repeated.
To understand threads better, read "Java Concurrency In Practice" by Brian Goetz.
To understand services better, I think you should write them to be single threaded and let the Java EE container you deploy them to handle threading issues. A pooled servlet is a better solution than having your code shoulder the burden of managing threads for users.