At first glance in the code below the mLocationManager
object should go out of scope after onCreate(...)
is finished, and the expected behaviour is that onLocationChanged
is never called or called a few times until the object is garbage collected. However the object returned by the getSystemService
seems to be singleton which lives outside the scope of MainActivity
(appropriately so since it is a system service :) )
After taking a heap dump and going through it with the Eclipse Memory Analyzer it seems that ContextImpl keeps a reference to a LocationManager instance. In the memory dump there were two references to a LocationManager object while in the code there is clearly only one, which means that another reference is created somewhere else.
My questions are:
Does someone have a complete description of what is exactly happening when calling the implementation of:
public abstract Object getSystemService(String name);
is the object returned a singleton lazily created and where exactly is the reference created/kept ?
package com.neusoft.bump.client.storage;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("TAG", "STARTED");
LocationManager mLocationManager = (LocationManager) this
.getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
Log.v("TAG", "onLocationChanged");
Log.v("TAG", "Latitude: " + location.getLatitude()
+ "Longitude: " + location.getLongitude());
}
public void onStatusChanged(String provider, int status,
Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location
// updates
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
600, 0, locationListener);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
Update1
The LocationManager
is created as singleton
private LocationManager getLocationManager() {
synchronized (sSync) {
if (sLocationManager == null) {
IBinder b = ServiceManager.getService(LOCATION_SERVICE);
ILocationManager service = ILocationManager.Stub.asInterface(b);
sLocationManager = new LocationManager(service);
}
}
return sLocationManager;
}
but I have trouble understanding what happens when calling ServiceManager.getService(LOCATION_SERVICE);
even after reading the ServiceManager
code.
The Location Manager, as most of the system services/managers is created in an early stage during boot process.
app_process is the native component which starts the DalvikVM, additionally, it tells ZigoteInit (The class which does the actual work) to launch the SystemServer. It's here where the first instance of LocationManager is created and where the reference is kept on the ServerThread within it.
The rest is already know to you, i think.
Method getSystemService
Is implemented in https://android.googlesource.com/platform/frameworks/base/+/android-5.0.2_r1/core/java/android/app/ContextImpl.java
Where SYSTEM_SERVICE_MAP is:
and all services are registered in static block
with call to registerService like this:
or
ServiceFetcher and StaticServiceFetcher implements lazy loading pattern.
See if my discussion makes sense...
dissection of android service internal
As suggested by one of the readers I am trying to copy some portion of the write-up here.
Have you ever wondered how an app gets an handle to the system services like POWER MANAGER or ACTIVITY MANAGER or LOCATION MANAGER and several others like these. To know that I dug into the source code of Android and found out how this is done internally. So let me start from the application side’s java code.
At the application side we have to call the function
getService
and pass the ID of the system service (say POWER_SERVICE) to get an handle to the service.Here is the code for
getService
defined in /frameworks/base/core/java/android/os/ServiceManager.javaSuppose we don’t have the service in the cache. Hence we need to concentrate on line 55
return getIServiceManager().getService(name);
This call actually gets a handle to the service manager and asks it to return a reference of the service whose name we have passed as a parameter.
Now let us see how the
getIServiceManager()
function returns a handle to the ServiceManager.Here is the code of getIserviceManager() from /frameworks/base/core/java/android/os/ServiceManager.java
The ServicemanagerNative.asInterface() looks like the following:
So basically we are getting a handle to the native servicemanager.
This asInterface function is actually buried inside the two macros
DECLARE_META_INTERFACE(ServiceManager)
andIMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
defined in IserviceManager.h and IServiceManager.cpp respectively.Lets delve into the two macros defined in /frameworks/base/include/binder/IInterface.h
The
DECLARE_META_INTERFACE(ServiceManager)
macro is defined asAnd the
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
has been defined as follows:So if we replace expand these two macros in IServiceManager.h & IServiceManager.cpp file with the appropriate replacement parameters they look like the following:
And in IServiceManager.cpp
So if you see the line 12 which shows if the Service Manager is up and running (and it should because the service manager starts in the init process during Android boot up) it returns the reference to it through the queryLocalinterface function and it goes up all the way to the java interface.
from ServiceManagerNative.java. In this function we pass the service that we are looking for.
And the onTransact function for GET_SERVICE_TRANSACTION on the remote stub looks like the following:
It returns the reference to the needed service through the function getService. The getService function from /frameworks/base/libs/binder/IServiceManager.cpp looks like the following:
So it actually checks if the Service is available and then returns a reference to it. Here I would like to add that when we return a reference to an IBinder object, unlike other data types it does not get copied in the client’s address space, but it's actually the same reference of the IBinder object which is shared to the client through a special technique called object mapping in the Binder driver.
To add more details to the discussion, let me go a little deeper into it.
The checkService function looks like the following:
So it actually calls a remote service and pass CHECK_SERVICE_TRANSACTION code (its an enum value of 2) to it.
This remote service is actually implemented in frameworks/base/cmds/servicemanager/service_manager.c and its onTransact looks like the following.
Hence we end up calling the function named do_find_service which gets a reference to the service and returns it back.
The do_find_service from the same file looks as follows:
find_svc looks as follows:
As it becomes clear that it traverses through the svclist and returns the the service we are looking for.
Ok, so here's the source code of getService() in ServiceManager.java:
As we can see, if the requested service is not cached yet, this calls
getIServiceManager().getService(name)
. getIServiceManager() is a method in the same class(we will het to getService(name) in the next step):So this basically sends us to ServiceManagerNative.java where we need to look for getService(name):
Which initiates a transaction to retrieve a service that has a name "LOCATION_SERVICE".
It's getting harder to move from here due to a complex structure of classes and interfaces that deal with the low-level stuff, like system services. But basically it's all done in Context.java, ContextImpl.java, ServiceManager.java and ServiceManagerNative.java. Also note that some of them might keep local caches or maps with references to service instances(i.e. you can see a
sCache.get(name)
in the ServiceManager.java listing above), this is where extra references may be coming from.I don't think you will get a more detailed answer here on StackOverflow, because it becomes very low-level. You might want to ask somewhere like on an Android OS mailing list that has google employees on it.