android - programacion - ¿Cómo funciona getSystemService() exactamente?
manual de android en pdf (4)
A primera vista, en el código que se encuentra debajo, el objeto mLocationManager
debería quedar fuera del alcance una vez finalizado onCreate(...)
, y el comportamiento esperado es que nunca se llama o llama a onLocationChanged
varias veces hasta que el objeto se recolecta como basura. Sin embargo, el objeto devuelto por el getSystemService
parece ser un singleton que vive fuera del alcance de MainActivity
(apropiadamente ya que es un servicio del sistema :))
Después de tomar un volcado de pila y analizarlo con Eclipse Memory Analyzer, parece que ContextImpl mantiene una referencia a una instancia de LocationManager. En el volcado de memoria había dos referencias a un objeto LocationManager, mientras que en el código hay claramente solo una, lo que significa que se crea otra referencia en otro lugar.
Mis preguntas son:
Alguien tiene una descripción completa de lo que está sucediendo exactamente cuando llama a la implementación de:
public abstract Object getSystemService(String name);
¿el objeto devuelto es un singleton creado perezosamente y dónde se crea / mantiene exactamente la referencia?
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
El LocationManager
se crea como 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;
}
pero tengo problemas para entender lo que sucede al llamar a ServiceManager.getService(LOCATION_SERVICE);
Incluso después de leer el código de ServiceManager
.
pero tengo problemas para entender lo que sucede al llamar a ServiceManager.getService (LOCATION_SERVICE); Incluso después de leer el código de ServiceManager.
Bien, aquí está el código fuente de getService () en ServiceManager.java :
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
Como podemos ver, si el servicio solicitado aún no está en caché, esto llama a getIServiceManager().getService(name)
. getIServiceManager () es un método en la misma clase (lo haremos para obtenerServicio (nombre) en el siguiente paso):
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
Entonces, esto básicamente nos envía a ServiceManagerNative.java donde necesitamos buscar getService (nombre):
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
Lo que inicia una transacción para recuperar un servicio que tiene un nombre "LOCATION_SERVICE".
Es cada vez más difícil moverse desde aquí debido a una compleja estructura de clases e interfaces que se ocupan de las cosas de bajo nivel, como los servicios del sistema. Pero básicamente todo se hace en Context.java, ContextImpl.java, ServiceManager.java y ServiceManagerNative.java. También tenga en cuenta que algunos de ellos pueden guardar cachés locales o mapas con referencias a instancias de servicio (es decir, puede ver un sCache.get(name)
en el listado de ServiceManager.java que aparece más arriba); aquí es donde pueden provenir referencias adicionales.
No creo que obtenga una respuesta más detallada aquí en , porque se convierte en un nivel muy bajo. Es posible que desee preguntar en algún lugar como en una lista de correo del sistema operativo Android que tenga empleados de Google.
A ver si mi discusión tiene sentido ...
diseccion de servicio android interno
Según lo sugerido por uno de los lectores, estoy tratando de copiar aquí una parte de la reseña.
¿Alguna vez se ha preguntado cómo una aplicación puede controlar los servicios del sistema como POWER MANAGER o ACTIVITY MANAGER o LOCATION MANAGER y muchos otros como estos? Para saber que busqué en el código fuente de Android y descubrí cómo se hace esto internamente. Así que permítanme comenzar desde el código Java del lado de la aplicación.
En el lado de la aplicación, debemos llamar a la función getService
y pasar el ID del servicio del sistema (por ejemplo, POWER_SERVICE) para obtener un identificador del servicio.
Aquí está el código para getService
definido en /frameworks/base/core/java/android/os/ServiceManager.java
/**
44 * Returns a reference to a service with the given name.
45 *
46 * @param name the name of the service to get
47 * @return a reference to the service, or <code>null</code> if the service doesn''t exist
48 */
49 public static IBinder getService(String name) {
50 try {
51 IBinder service = sCache.get(name);
52 if (service != null) {
53 return service;
54 } else {
55 return getIServiceManager().getService(name);
56 }
57 } catch (RemoteException e) {
58 Log.e(TAG, "error in getService", e);
59 }
60 return null;
61 }
Supongamos que no tenemos el servicio en el caché. Por lo tanto, necesitamos concentrarnos en la línea 55 return getIServiceManager().getService(name);
Esta llamada realmente recibe un identificador para el administrador de servicios y le pide que devuelva una referencia del servicio cuyo nombre hemos pasado como parámetro.
Ahora veamos cómo la función getIServiceManager()
devuelve un identificador al ServiceManager.
Aquí está el código de getIserviceManager () de /frameworks/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager() {
34 if (sServiceManager != null) {
35 return sServiceManager;
36 }
37
38 // Find the service manager
39 sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
40 return sServiceManager;
41 }
El ServicemanagerNative.asInterface () se parece a lo siguiente:
/**
28 * Cast a Binder object into a service manager interface, generating
29 * a proxy if needed.
30 */
31 static public IServiceManager asInterface(IBinder obj)
32 {
33 if (obj == null) {
34 return null;
35 }
36 IServiceManager in =
37 (IServiceManager)obj.queryLocalInterface(descriptor);
38 if (in != null) {
39 return in;
40 }
41
42 return new ServiceManagerProxy(obj);
43 }
Básicamente, estamos manejando el administrador de servicios nativo.
Esta función asInterface está realmente oculta dentro de las dos macros DECLARE_META_INTERFACE(ServiceManager)
y IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
definido en IserviceManager.h e IServiceManager.cpp respectivamente.
Vamos a profundizar en las dos macros definidas en /frameworks/base/include/binder/IInterface.h
La DECLARE_META_INTERFACE(ServiceManager)
se define como
// ----------------------------------------------------------------------
73
74#define DECLARE_META_INTERFACE(INTERFACE) /
75 static const android::String16 descriptor; /
76 static android::sp<I##INTERFACE> asInterface( /
77 const android::sp<android::IBinder>& obj); /
78 virtual const android::String16& getInterfaceDescriptor() const; /
79 I##INTERFACE(); /
80 virtual ~I##INTERFACE(); /
Y el IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
Se ha definido como sigue:
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) /
84 const android::String16 I##INTERFACE::descriptor(NAME); /
85 const android::String16& /
86 I##INTERFACE::getInterfaceDescriptor() const { /
87 return I##INTERFACE::descriptor; /
88 } /
89 android::sp<I##INTERFACE> I##INTERFACE::asInterface( /
90 const android::sp<android::IBinder>& obj) /
91 { /
92 android::sp<I##INTERFACE> intr; /
93 if (obj != NULL) { /
94 intr = static_cast<I##INTERFACE*>( /
95 obj->queryLocalInterface( /
96 I##INTERFACE::descriptor).get()); /
97 if (intr == NULL) { /
98 intr = new Bp##INTERFACE(obj); /
99 } /
100 } /
101 return intr; /
102 } /
103 I##INTERFACE::I##INTERFACE() { } /
104 I##INTERFACE::~I##INTERFACE() { }
Entonces, si reemplazamos expandimos estas dos macros en el archivo IServiceManager.h & IServiceManager.cpp con los parámetros de reemplazo apropiados, se parecen a los siguientes:
class IServiceManager : public IInterface
{
public:
static const android::String16 descriptor;
static android::sp<IServiceManager> asInterface( const android::sp<android::IBinder>& obj);
virtual const android::String16& getInterfaceDescriptor() const;
IServicemanager();
virtual ~IServiceManager();
…......
….....
…...
…..
Y en IServiceManager.cpp
const android::String16 IServiceManager::descriptor("android.os.IServiceManager”);
const android::String16&
IServiceManager::getInterfaceDescriptor() const {
return IServiceManager::descriptor;
}
android::sp<IServiceManager> IServiceManager::asInterface(
const android::sp<android::IBinder>& obj)
{
android::sp< IServiceManager> intr;
if (obj != NULL) {
intr = static_cast<IServiceManager*>(
obj->queryLocalInterface(
IServiceManager::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
IServiceManager::IServiceManager() { }
IServiceManager::~IIServiceManager { }
Entonces, si ve la línea 12 que muestra si el administrador de servicios está activo y en funcionamiento (y debería hacerlo porque el administrador de servicios se inicia en el proceso de inicio durante el inicio de Android), devuelve la referencia a través de la función de interfaz de consulta local y sube todo El camino a la interfaz java.
public IBinder getService(String name) throws RemoteException {
116 Parcel data = Parcel.obtain();
117 Parcel reply = Parcel.obtain();
118 data.writeInterfaceToken(IServiceManager.descriptor);
119 data.writeString(name);
120 mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
121 IBinder binder = reply.readStrongBinder();
122 reply.recycle();
123 data.recycle();
124 return binder;
125 }
de ServiceManagerNative.java. En esta función pasamos el servicio que estamos buscando.
Y la función onTransact para GET_SERVICE_TRANSACTION en el stub remoto se parece a lo siguiente:
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
51 {
52 try {
53 switch (code) {
54 case IServiceManager.GET_SERVICE_TRANSACTION: {
55 data.enforceInterface(IServiceManager.descriptor);
56 String name = data.readString();
57 IBinder service = getService(name);
58 reply.writeStrongBinder(service);
59 return true;
60 }
61
62 case IServiceManager.CHECK_SERVICE_TRANSACTION: {
63 data.enforceInterface(IServiceManager.descriptor);
64 String name = data.readString();
65 IBinder service = checkService(name);
66 reply.writeStrongBinder(service);
67 return true;
68 }
69
//Rest has been discarded for brevity…………………..
………………….
………………….
…………………
Devuelve la referencia al servicio necesario a través de la función getService. La función getService de /frameworks/base/libs/binder/IServiceManager.cpp se parece a lo siguiente:
virtual sp<IBinder> getService(const String16& name) const
134 {
135 unsigned n;
136 for (n = 0; n < 5; n++){
137 sp<IBinder> svc = checkService(name);
138 if (svc != NULL) return svc;
139 LOGI("Waiting for service %s.../n", String8(name).string());
140 sleep(1);
141 }
142 return NULL;
143 }
Entonces, realmente verifica si el Servicio está disponible y luego le devuelve una referencia. Aquí me gustaría agregar que cuando devolvemos una referencia a un objeto IBinder, a diferencia de otros tipos de datos, no se copia en el espacio de direcciones del cliente, pero en realidad es la misma referencia del objeto IBinder que se comparte al cliente a través de un Técnica especial llamada mapeo de objetos en el driver Binder.
Para agregar más detalles a la discusión, permítanme profundizar un poco más en ella.
La función checkService se parece a la siguiente:
virtual sp<IBinder> checkService( const String16& name) const
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
}
Así que en realidad llama a un servicio remoto y le pasa el código CHECK_SERVICE_TRANSACTION (es un valor de enumeración de 2).
Este servicio remoto se implementa realmente en frameworks / base / cmds / servicemanager / service_manager.c y su onTransact se parece a lo siguiente.
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
ptr = do_find_service(bs, s, len);
if (!ptr)
break;
bio_put_ref(reply, ptr);
return 0;
Por lo tanto, terminamos llamando a la función llamada do_find_service que obtiene una referencia al servicio y la devuelve.
El do_find_service del mismo archivo tiene el siguiente aspecto:
void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)
{
struct svcinfo *si;
si = find_svc(s, len);
// ALOGI("check_service(''%s'') ptr = %p/n", str8(s), si ? si->ptr : 0);
if (si && si->ptr) {
return si->ptr;
} else {
return 0;
}
find_svc se ve de la siguiente manera:
struct svcinfo *find_svc(uint16_t *s16, unsigned len)
{
struct svcinfo *si;
for (si = svclist; si; si = si->next) {
if ((len == si->len) &&
!memcmp(s16, si->name, len * sizeof(uint16_t))) {
return si;
}
}
return 0;
}
Como queda claro, atraviesa el svclist y devuelve el servicio que estamos buscando.
El Administrador de ubicaciones, como la mayoría de los administradores / servicios del sistema, se crea en una etapa temprana durante el proceso de arranque.
app_process es el componente nativo que inicia el DalvikVM, además, le dice a ZigoteInit (la clase que hace el trabajo real) que inicie el SystemServer. Es aquí donde se crea la primera instancia de LocationManager y donde se guarda la referencia en el ServerThread dentro de él.
/frameworks/base/services/java/com/android/server/SystemServer.java
DevicePolicyManagerService devicePolicy = null;
StatusBarManagerService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
WallpaperManagerService wallpaper = null;
-> LocationManagerService location = null;
CountryDetectorService countryDetector = null;
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;
try {
Slog.i(TAG, "Location Manager");
location = new LocationManagerService(context);
ServiceManager.addService(Context.LOCATION_SERVICE, location);
} catch (Throwable e) {
reportWtf("starting Location Manager", e);
}
El resto ya lo sabemos, creo.
Método getSystemService
public abstract Object getSystemService(String name);
Se implementa en https://android.googlesource.com/platform/frameworks/base/+/android-5.0.2_r1/core/java/android/app/ContextImpl.java
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
Donde SYSTEM_SERVICE_MAP es:
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
new HashMap<String, ServiceFetcher>();
Y todos los servicios están registrados en bloque estático.
static {
con llamada a registerService como este:
registerService(LOCATION_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
o
registerService(INPUT_SERVICE, new StaticServiceFetcher() {
public Object createStaticService() {
return InputManager.getInstance();
}});
ServiceFetcher y StaticServiceFetcher implementan un patrón de carga diferida.