I read the docs on Android Developer about IBinder/Binder.
It says
The Binder system also supports recursion across processes. For
example if process A performs a transaction to process B, and process
B while handling that transaction calls transact() on an IBinder that
is implemented in A, then the thread in A that is currently waiting
for the original transaction to finish will take care of calling
Binder.onTransact() on the object being called by B. This ensures that
the recursion semantics when calling remote binder object are the same
as when calling local objects.
I have two questions about this
then the thread in A that is currently waiting for the original
transaction to finish will take care of calling Binder.onTransact() on
the object being called by B
First, how can a blocked thread be notified to do other stuff other than original procedure?
Second, After the thread finishes the onTransact(), will it block again to wait for original transaction.
First, How can a blocked thread be notified to do other stuff other than original procedure?
Binder is meant to abstract out the process of IPC, hence this question essentially simplifies to "how can a called function call a function before returning". Since that is clearly possible, and sensible, it should work with Binder too.
Implementation wise, it would be done by interpreting the data received from the binder transaction operation - if that is an encoding of "call this method for me" rather than "your return value is" then that is what will happen.
Second, After the thread finishes the onTransact(), will it block again to wait for original transaction.
Yes, because the method called out of the code that handles binder transactions will eventually return there (unless there's an exception, process death, signal or similar)
Related
Is it possible that activity lifecycle callbacks get interrupted before executting all its code? can a callback interrupt another?
https://developer.android.com/guide/components/activities/activity-lifecycle
In this documentation it recommends not implementing heavy work in onPause for example as it has a "very brief execution", who controls that? how? does the system calls the next lifecycle callback even if onPause did not finish execution yet?
Docs suggest that onResume()/onPause() execute rapidly for better user experience, because these methods are called frequently. Android activity callbacks are always executed in a sequence. They may be interrupted only by an uncaught exception which will terminate the whole app.
Those calls are what will influence UI mostly (along with onCreate, onCreateView) and if they take a long time to complete their code the user will most certainly know about it.
Say you have this in your onResume() method
OnResume(){ super.onResume; updateView(); }
Something standard, this is what normally happens before the fragment or activity gets “resumed”.
If your updateView(); required 2seconds to complete, weather it’s loading data or creating objects, that’s 2 seconds that the UI is “frozen” and the user cannot use the app, or could mistake as “is broken”
If you have the theoretical same scenario for onPause() then that too is 2 seconds of “frozen” time in the app
It is preferred to have the “updateView()” (essentially your onResume and onPause methods) complete as quickly as possible so that the user does not notice any “lag” in the application.
I have a simple piece of code which starts an intent but when I debug it passes through various classes and often makes me wonder why these classes get called every time and what tasks are they performing before coming back to normal execution.
the control of execution then shifts to View class and calls performClick()
after then it goes to Handler class and calls dispatchMessage(Message msg)
and at last, it calls loop() of Looper class a number of times before coming back to normal flow.
so can someone help me here understand what is happening under the hood and why this loop() gets called multiple times?
The loop() is part of the Looper class. Deep in your android app when your process initializes the application, the first thing the JVM looks for is an entry point, which in all Java applications is the main method. The android main method exists in a class called ActivityThread (check the AOSP for the activity thread).
The beauty of this class is that it does a few things,
The Looper in the ActivityThread's main method calls the prepareMainLooper() method. This. initializes the current thread as the application's main looper. i.e this is where your main thread receives its mainthread designation, that differs it from all other threads at runtime.
Also in the main method, a handler is responsible for managing a MessageQueue where messages from your app are received and handled. It is important to note a [Messages] can be executed by the MessageQueue as a Runnable or other executable object.
What makes android different than most desktop/console like java applications is the Looper class. This class has a Looper.loop() method that is called within the ActivityThread's main method, and it runs an infinite loop thanks to a for(;;){} (the double semicolon indicates infinite loop). This loop will continue to run indefinitely as unless the quit() is called externally. The loop method calls message.next() each time it has completed with a message, to retrieve a new message.
In short, without this infinite looping method, it would be difficult for android to have a basic way to process incoming messages asynchronously, which is at the core of Android's event driven nature. The loop is eagerly seeking new messages to process or pass to the handler.
Checkout the AOSP for a deeper dive if you're interested!
I have a MainActivity that uses fragments.
The onCreate of MainActivity completes its onCreate with the use of
welcomeFragment = new MyWelcomeFragment();
fr.beginTransaction().replace(R.id.mainContent, welcomeFragment).commit()
As a part of MyWelcomeFragment's on onResume, a thread is started to get updates from my webserver. If the user selects an action before the thread is completed and goes to MyNewsFragment, what happens to the thread that has yet to complete running in MyWelcomeFragment's thread stack?
Thread was created with: (myThread and handler are instance variables)
myThread = new Thread(new Runnable() {
#Override
public void run() {
sendDataToServer("");
handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
onTaskDone();
}
});
}
});
myThread.start();
Dalvik keeps all Thread references in the runtime so your thread will keep running unless it is terminated or completes (some reference). So depending on where you start your thread, you may be creating more than one. There is no clean way to cancel a Thread and in this case you may need to first cancel the http request inside sendDataToServer and use a shared flag to stop the thread.
In a bigger picture, I would suggest
move the networking method to Activity and handle it there since it has longer lifespan than
Fragment
use Android Volley to handle networking. With it you can manage inadvertent multiple requests to send data to your server. Since each request can be attached with tag, you can cancel any with a particular tag in the queue (in your case the one corresponding to sendDataToServer process) before starting a new one.
and finally use Publisher-Subsriber pattern which has already been made available by libraries like Otto or EventBus. This allows communication between Fragments or Activities while avoiding life cycle related problems. In a gist: a publisher emits events to subscribers registered to it and unlike listeners both publisher and subscriber are totally decoupled. In your case, when sendDataToServer completes, you will not know if the fragment containing onTaskDone is still around. If this method manipulates UI while the fragment has destroyed its view then you will definitely get an error. So onTaskDone should be wrapped inside a subscriber method whose parent fragment is registered to the http event publisher and deregistered as soon as its view is destroyed.
It'll keep running until run() method completes, which is probably for how long it takes for sendDataToServer("") takes to complete, as the handler should be fairly quick in comparison to network IO - or the thread is force interrupted.
Are you still interested in the result if the user switches fragments?
Are you keeping a reference to the welcome fragment? (Via either fragment manager or activity) - if so you could still access the result.
If the user goes back to welcome fragment, the previous thread reference will be lost.
Thread will keep on running till MyWelcomeFragment is alive and If you don't kill it in onPause().
If an Asynchronous thread is a thread that operates separately to the main thread and doesn't interfere with the main thread...
Does a new Activity not occupy the main thread after it has been started through startActivity(intent)?
The majority of what I have read on this says these are both asynchronous, however there are a fair few conflicting answers and the people that say this don't really give convincing arguments.
So if anyone who has this clear in their head and could explain why they are synchronous/asynchronous, I would be a very grateful man!
Cheers
EDIT: So the answer I have derived from these two good folk and some stuff online...
Bringing Activities into the mix of synchronous/asynchronous can cause a load of horse to come about. But it is still referring to the principles of...
Synchronous methods/commands must be completed before the call stack can continue.
Asynchronous methods/commands (commonly done in a thread/AsyncTask) continue in parallel allowing the main thread of execution to continue. (It can return in its own time)
The startActivity(intent) and startActivityForResult(intent) methods are Asynchronous as they are non-blocking and allow the thread of execution to continue whilst performing their corresponding task as well.
startActivity(intent) and startActivityForResult(intent) are asynchronous in the sense that these methods return immediately without starting an Activity. Actually, they schedule an Activity to start only after the lifecycle events of the current Activity is finished.
The takeaway is, if you have something, that takes some time to finish, in the onPause() method of the first activity , the new Activity will be slow to start.
When you startActivityForResult you still perform an asynchronous call. Your caller activity gets suspended and the new is started in another process (if it runs under a different user).
But when the called activity terminates setting a result, your activity is resumed and you get onActivityResult called as a callback containing the result.
I think I tracked down a memory leak and want to confirm what I think may true about how Android's Binder is implemented. In this case I have a Service and an Activity, each in their own process. I created an AIDL that allows me to pass a Callback object from the Activity to the Service through an ipc method and then have the callback called when the Service is done with the requested task.
For a long time I was wondering: if I pass a new Callback object to the Service and I don't keep a pointer to the Callback object in my Activity why doesn't the garbage collector just go ahead and collect the Callback in my Activity process? Since that doesn't seem to happen, how does the JVM know when to garbage collect the Callback in my Activity.
I think the answer is that the Binder system keeps a pointer to my Callback in the Activity process until the corresponding Callback object in the Service process has its finalize() method called, which then sends a message to the Activity to release the pointer. Is this correct? If not how does it work?
I believe it is and it leads to interesting situation where if the Callback in the Activity is pointing to something very memory intensive it won't be collected until the Callback in the Service is collected. If the Service isn't low on memory it might not collect the Callback for a long time and the Callbacks might just build up in the Activity until there is an OutOfMemoryError in the Activity.
Yury is pretty much correct.
My Service starts a thread that holds the callback and when the thread is done with its work it calls the callback and the thread ends. When the callback is called it may do a tiny bit of work in my Activity and then return at which point I don't have pointers in my Activity process to the callback.
However the callback object in the Activity will continue to be pointed to by Android's binder system until the corresponding callback object in the Service is garbage collected.
If the callback object in the Activity process dominates some other objects that consume a lot of memory then I am wasting memory in my Activity process for no good reason and could even get an OutOfMemoryError. The solution is to create a simple method in my callback class called destory() to null out all the callback's fields and to call that method when I am done with the callback.
If the callback class is a non-static inner class you may want to consider changing it to a static inner class and passing in the parent class in the constructor, this way you can null that out as well in the destory() method.
This brings up an interesting thought, if the parent class of a non-static inner callback class is an Activity and a configuration change happens (such as a screen rotation) after the callback is sent through the binder but before it is called back then the callback will be pointing to an old Activity object when it executes!
Update: I discovered this code inside Binder.java, of course it is disabled but it would have been nice if they mentioned this kind of stuff in the Javadocs.
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
If I understand correctly how Binder works the problem in your case is the following. For each incoming incoming call your Service create a separate thread. When you pass an object to this thread your Binder system creates local copy of your object for the thread. Thus, until your Service method has returned result the thread with the copy of the object continues to work.
To check this just try to see the threads of your Service process (in DDMS).