2013-02-28 11 views
22

mLocationManager के नीचे दिए गए कोड में पहली नज़र में onCreate(...) समाप्त होने के बाद ऑब्जेक्ट को दायरे से बाहर जाना चाहिए, और अपेक्षित व्यवहार यह है कि onLocationChanged ऑब्जेक्ट कचरा एकत्र होने तक कुछ बार कभी नहीं कहा जाता है या कहा जाता है। हालांकि वस्तु getSystemService द्वारा दिया सिंगलटन जो MainActivity (उचित रूप से इसलिए क्योंकि यह एक सिस्टम सेवा :) है)getSystemService() वास्तव में कैसे काम करता है?

ग्रहण मेमोरी विश्लेषक के साथ एक हीप डंप ले रहे हैं और यह अध्ययन करने के बाद ऐसा लगता है के दायरे से बाहर रहता है हो रहा है कि ContextImpl किसी स्थान प्रबंधक उदाहरण का संदर्भ रखता है। मेमोरी डंप में एक स्थान प्रबंधक ऑब्जेक्ट के दो संदर्भ थे, जबकि कोड में स्पष्ट रूप से केवल एक है, जिसका अर्थ है कि एक और संदर्भ कहीं और बनाया गया है।

मेरे प्रश्न हैं:

करता है किसी को वास्तव में क्या हो रहा है जब के कार्यान्वयन बुला का पूरा विवरण है:

public abstract Object getSystemService(String name); 

वस्तु लौटे एक सिंगलटन lazily बनाया है और जहां वास्तव में उल्लेख किया गया है बनाया/रखा?

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

LocationManager सिंगलटन

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; 
} 

के रूप में बनाया गया है, लेकिन मैं मुसीबत समझ क्या होता है जब भी ServiceManager कोड को पढ़ने के बाद ServiceManager.getService(LOCATION_SERVICE); बुला है।

+0

मुझे लगता है कि 'LocationManager' आपके' लोकेशन लिस्टर 'का संदर्भ रखता है जब तक आप' LocationManager.removeUpdates() 'नहीं कहते हैं, और आपके' स्थान लिस्टर 'के पास आपकी' गतिविधि ' – nicopico

+0

का संदर्भ है 'onCreate (...)' विधि के अंदर बनाए गए स्थान लिस्टनर ऑब्जेक्ट पर दायरे को लागू करना चाहिए। जैसे ही विधि समाप्त हो जाती है, यह तब तक गुंजाइश से बाहर हो जाती है जब तक कि कहीं और इसका संदर्भ न हो (इस मामले में LocationManager ऑब्जेक्ट)। इसके अलावा यदि स्थान प्रबंधक ऑब्जेक्ट को कहीं और से संदर्भित नहीं किया गया था तो उनमें से दोनों कुछ बिंदु पर कचरा एकत्रित होता। मैंने स्थान लिस्टनर (अपनी परिकल्पना का परीक्षण करने के लिए) के लिए स्थिर आंतरिक कक्षा बनाने का भी परीक्षण किया और व्यवहार समान है। –

+0

"क्या किसी के पास कार्यान्वयन को कॉल करते समय वास्तव में क्या हो रहा है इसका पूरा विवरण है" - जब आप स्रोत कोड पढ़ते हैं, तो आपने क्या सीखा? – CommonsWare

उत्तर

43

कर देखें कि कहीं मेरी चर्चा समझ में आता है ...

dissection of android service internal

पाठकों मैं यहाँ लेख के कुछ हिस्से को कॉपी करने की कोशिश कर रहा हूँ में से एक ने सुझाव दिया।

क्या आपने कभी सोचा है कि कैसे एक ऐप पावर मैनेजर या गतिविधि प्रबंधक या LOCATION प्रबंधक और कई अन्य जैसे सिस्टम सेवाओं को संभालता है। यह जानने के लिए कि मैंने एंड्रॉइड के स्रोत कोड में खोला और पाया कि यह आंतरिक रूप से कैसे किया जाता है। तो मुझे एप्लिकेशन पक्ष के जावा कोड से शुरू करने दें।

आवेदन पक्ष पर हमें getService फ़ंक्शन को कॉल करना होगा और सेवा को संभालने के लिए सिस्टम सेवा (POWER_SERVICE कहें) की आईडी पास करनी होगी।

यहाँ /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 } 

मान लीजिए हम कैश में सेवा नहीं है में परिभाषित getService के लिए कोड है। इसलिए हमें लाइन 55 return getIServiceManager().getService(name);

यह कॉल वास्तव में सेवा प्रबंधक को एक संभाल लेता है और उसे उस सेवा का संदर्भ वापस करने के लिए कहता है जिसका नाम हमने पैरामीटर के रूप में पारित किया है।

अब देखते हैं कि getIServiceManager() फ़ंक्शन सेवा प्रबंधक को एक हैंडल कैसे देता है।

:

यहाँ /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 } 

ServicemanagerNative.asInterface() से getIserviceManager() के कोड है ऐसा दिखाई देता है

/** 
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 } 

तो मूल रूप से हम देशी servicemanager को हैंडल प्राप्त कर रहे हैं।

यह asInterface समारोह वास्तव में दो मैक्रो DECLARE_META_INTERFACE(ServiceManager) और IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); क्रमशः IserviceManager.h और IServiceManager.cpp में परिभाषित के अंदर दफन है।

/फ्रेमवर्क/आधार/शामिल/बाइंडर/IInterface में परिभाषित दो मैक्रोज़ में जाने दें।

#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() { } 

तो अगर हम IServiceManager.h & IServiceManager.cpp में इन दो मैक्रो का विस्तार बदल देते हैं: ज

DECLARE_META_INTERFACE(ServiceManager) मैक्रो के रूप में

// ---------------------------------------------------------------------- 
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();           \ 

इस प्रकार परिभाषित किया गया है और IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); परिभाषित किया गया है उचित प्रतिस्थापन मानकों के साथ फ़ाइल को निम्न जैसा दिखता है:

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(); 
…...... 
…..... 
…... 
….. 

और में 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 { } 

तो तुम लाइन 12 है जो दिखाता है, तो सेवा प्रबंधक तैयार होकर चलने लगे देखते हैं (और यह होना चाहिए क्योंकि सेवा प्रबंधक एंड्रॉयड बूट अप के दौरान init प्रक्रिया में शुरू होता है) यह क्वेरी Localinterface समारोह के माध्यम से इसका संदर्भ देता है और यह जावा इंटरफेस के लिए सभी तरह से चला जाता है।

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 } 

ServiceManagerNative.java से। इस समारोह में हम उस सेवा को पास करते हैं जिसे हम ढूंढ रहे हैं।

और दूरदराज के ठूंठ पर GET_SERVICE_TRANSACTION के लिए onTransact समारोह ऐसा दिखाई देता है:

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………………….. 

…………………. 
…………………. 
………………… 

यह समारोह getService के माध्यम से की जरूरत है सेवा के संदर्भ देता है। /frameworks/base/libs/binder/IServiceManager.cpp से getService समारोह ऐसा दिखाई देता है:

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 } 

तो यह वास्तव में अगर सेवा उपलब्ध है और फिर इसे करने के लिए एक संदर्भ रिटर्न की जांच करता है। यहां मैं यह जोड़ना चाहता हूं कि जब हम किसी अन्य प्रकार के डेटा के विपरीत किसी आईबिंडर ऑब्जेक्ट का संदर्भ वापस लेते हैं, तो उसे क्लाइंट के एड्रेस स्पेस में कॉपी नहीं किया जाता है, लेकिन यह वास्तव में आईबींडर ऑब्जेक्ट का एक ही संदर्भ है जिसे क्लाइंट को क्लाइंट के साथ साझा किया जाता है बाइंडर ड्राइवर में ऑब्जेक्ट मैपिंग नामक विशेष तकनीक।

चर्चा में अधिक जानकारी जोड़ने के लिए, मुझे इसमें थोड़ा गहराई से जाने दें।

checkService समारोह ऐसा दिखाई देता है:

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(); 

    } 

तो यह वास्तव में एक दूरस्थ सेवा कॉल करता है और इसे करने के लिए CHECK_SERVICE_TRANSACTION कोड (2 के अपने एक enum मान) गुजरती हैं।

यह रिमोट सेवा वास्तव में ढांचे/आधार/cmds/servicemanager/service_manager.c में लागू होती है और इसकी ऑनट्रैक्ट निम्न की तरह दिखती है।

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; 

इसलिए हम समारोह नामित do_find_service जो सेवा के लिए एक संदर्भ हो जाता है और इसे वापस लौटता है बुला अंत।

एक ही फ़ाइल से do_find_service इस प्रकार दिखता है:

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 इस प्रकार दिखता है:

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; 

} 

यह स्पष्ट हो जाता है कि यह svclist के माध्यम से पार करता है और सेवा हम कर रहे हैं देता है के रूप में खोज रहे हैं

+0

ग्रेट जॉब । स्रोत को लिंक जोड़ना और दो बिंदुओं को समझाएं। क्या सेवा जावा में एक सिंगलटन लौटा दी गई है। 2. क्या कहीं भी संग्रहीत संदर्भ यह एक महान उत्तर देगा –

6

लेकिन मुझे यह समझने में परेशानी है कि ServiceManager.getService (LOCATION_SERVICE) को कॉल करते समय क्या होता है; ServiceManager कोड पढ़ने के बाद भी।

ठीक है, इसलिए यहाँ ServiceManager.java में getService() के स्रोत कोड है:

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; 
} 

हम देख सकते हैं, यदि अनुरोध सेवा अभी तक कैश नहीं है, यह कहता है getIServiceManager().getService(name)। getIServiceManager() एक ही कक्षा में एक विधि है (हम अगले चरण में getService (नाम) के लिए बेचैन होंगे):

private static IServiceManager getIServiceManager() { 
    if (sServiceManager != null) { 
     return sServiceManager; 
    } 

    // Find the service manager 
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject()); 
    return sServiceManager; 
} 

तो यह मूल रूप से ServiceManagerNative.java करने के लिए हमें भेजता है, जहां हम getService (नाम) के लिए देखने की जरूरत है:

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; 
} 

जो एक सेवा एक नाम "LOCATION_SERVICE" है कि पुनः प्राप्त करने के एक सौदे शुरू की।

कक्षा सेवाओं और इंटरफ़ेस की जटिल संरचना के कारण यहां से स्थानांतरित करना कठिन हो रहा है जो सिस्टम सेवाओं जैसी निम्न-स्तरीय सामग्री से निपटते हैं। लेकिन मूल रूप से यह सब Context.java, ContextImpl.java, ServiceManager.java और ServiceManagerNative.java में किया गया है। यह भी ध्यान रखें कि उनमें से कुछ स्थानीय कैश या मानचित्र को सेवा उदाहरणों के संदर्भ में रख सकते हैं (यानी आप उपरोक्त ServiceManager.java सूची में sCache.get(name) देख सकते हैं), यह वह जगह है जहां से अतिरिक्त संदर्भ आ रहे हैं।

मुझे नहीं लगता कि आपको स्टैक ओवरफ्लो पर अधिक विस्तृत उत्तर मिलेगा, क्योंकि यह बहुत कम स्तर बन जाता है। आप किसी एंड्रॉइड ओएस मेलिंग सूची की तरह कहीं पूछना चाहेंगे जिसमें Google कर्मचारी हैं।

+0

मुझे वास्तव में निम्न स्तर के विवरण में रूचि थी इसलिए मुझे एंड्रॉइड ओएस मेलिंग सूची में जाना चाहिए :) –

2

स्थान प्रबंधक, क्योंकि अधिकांश सिस्टम सेवाएं/प्रबंधक बूट प्रक्रिया के दौरान शुरुआती चरण में बनाए जाते हैं।

app_process मूल घटक है जो DalvikVM शुरू करता है, इसके अतिरिक्त, यह सिस्टम सर्वर को लॉन्च करने के लिए ज़िगोटइनेट (वास्तविक कार्य करता है) को बताता है। यह यहां है जहां LocationManager का पहला उदाहरण बनाया गया है और जहां इसके भीतर सर्वर थ्रेड पर संदर्भ रखा गया है।

/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); 
} 

बाकी मुझे पहले से ही पता है, मुझे लगता है।

2

विधि getSystemService

public abstract Object getSystemService(String name); 

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); 
    } 

में लागू किया गया है कहाँ SYSTEM_SERVICE_MAP है:

private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = 
      new HashMap<String, ServiceFetcher>(); 

और सभी सेवाओं स्थिर ब्लॉक में पंजीकृत हैं

+०१२३५१६४१०
static { 

कॉल के साथ इस तरह registerService रहे हैं:

registerService(LOCATION_SERVICE, new ServiceFetcher() { 
       public Object createService(ContextImpl ctx) { 
        IBinder b = ServiceManager.getService(LOCATION_SERVICE); 
        return new LocationManager(ctx, ILocationManager.Stub.asInterface(b)); 
       }}); 

या

registerService(INPUT_SERVICE, new StaticServiceFetcher() { 
       public Object createStaticService() { 
        return InputManager.getInstance(); 
       }}); 

ServiceFetcher और StaticServiceFetcher आलसी लोड हो रहा है पैटर्न लागू करता है।

उम्मीद है कि यह मदद करता है।

संबंधित मुद्दे