27

以下のコードを一見すると、mLocationManagerオブジェクトは終了後にスコープ外に出る必要がonCreate(...)あり、期待される動作はonLocationChanged、オブジェクトがガベージ コレクションされるまで呼び出されないか、数回呼び出されることです。ただし、によって返されるオブジェクトはgetSystemService、の範囲外にあるシングルトンのようですMainActivity(システムサービスであるため、適切にそうです:))

ヒープ ダンプを取得し、Eclipse メモリ アナライザーで調べたところ、ContextImpl が LocationManager インスタンスへの参照を保持しているようです。メモリ ダンプには LocationManager オブジェクトへの参照が 2 つありましたが、コードには明らかに 1 つしかありません。これは、別の参照が別の場所に作成されていることを意味します。

私の質問は次のとおりです。

次の実装を呼び出すときに正確に何が起こっているかについて、誰かが完全な説明を持っていますか?

public abstract Object getSystemService(String name);

遅延作成されたシングルトンが返されたオブジェクトであり、参照が作成/保持されている正確な場所はどこですか?

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

アップデート1

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.getService(LOCATION_SERVICE);を読んだ後でも呼び出すとどうなるか理解できません。ServiceManager

4

4 に答える 4

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(name) を使用します)。

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

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

したがって、これは基本的にServiceManagerNative.javaに送信され、そこでgetService(name) を探す必要があります。

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 で行われます。また、それらの一部は、サービス インスタンスへの参照を含むローカル キャッシュまたはマップを保持している可能性があることにも注意してください (つまりsCache.get(name)、上記の ServiceManager.java リストで a を確認できます)。ここから余分な参照が発生している可能性があります。

StackOverflow は非常に低レベルになるため、ここでより詳細な回答が得られるとは思いません。Google の従業員が参加している Android OS のメーリング リストのような場所で質問することをお勧めします。

于 2013-02-28T18:58:12.260 に答える
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 は、遅延読み込みパターンを実装します。

于 2015-03-08T11:13:40.620 に答える