近期遇到一个场景,
静态代码块里,无法拿到context,context.getPackageManager只在运行时适用,而静态代码块里无法做到,那么如何拿到PackageManager?
例如:
static {
if(判断条件) {
//执行静态方法;
}
}
从NfcAdapater里边找到了启发。
package android.nfc;
/**
* Represents the local NFC adapter.
* <p>
* Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC
* adapter for this Android device.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using NFC, read the
* <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p>
* <p>To perform basic file sharing between devices, read
* <a href="{@docRoot}training/beam-files/index.html">Sharing Files with NFC</a>.
* </div>
*/
public final class NfcAdapter {
static final String TAG = "NFC";
/**
* Helper to check if this device has FEATURE_NFC, but without using
* a context.
* Equivalent to
* context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
*/
private static boolean hasNfcFeature() {
IPackageManager pm = ActivityThread.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
return false;
}
try {
return pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0);
} catch (RemoteException e) {
Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
return false;
}
}
}
从代码里边,可以了解到,如果是想在静态代码块判断设备是否支持NFC feature, NfcAdapter和hasNfcFeature都是Public API,三方应用可以直接使用。
那么要判断Feature,还是要拿到PackageManager。
它这里并没有直接传递Context上下文到API,而知通过ActivityThread拿到IPackageManager。
package android.app;
/**
* This manages the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*
* {@hide}
*/
public final class ActivityThread extends ClientTransactionHandler {
/** @hide */
public static final String TAG = "ActivityThread";
private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
static final boolean localLOGV = false;
@UnsupportedAppUsage
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
final IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
}
从框架ActivityThread API来看,它本身是个非公开API,其次它的getPackageManager()方法是在黑名单里边,三方应用无法使用。
那么我们自己能不能直接模仿以上函数体 通过IPackageManager来取得pm?
// IPackageManager.aidl
package android.content.pm;
/**
* See {@link PackageManager} for documentation on most of the APIs
* here.
*
* {@hide}
*/
interface IPackageManager {
void checkPackageStartable(String packageName, int userId);
@UnsupportedAppUsage
boolean isPackageAvailable(String packageName, int userId);
}
//ServiceManager.java
package android.os;
/** @hide */
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static final Object sLock = new Object();
@UnsupportedAppUsage
private static IServiceManager sServiceManager;
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
@UnsupportedAppUsage
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
}
很遗憾,越是往里,越是限制多,IPackageManager和ServiceManager都是hide API,且ServiceManager.getService()也是黑名单,不宜给三方应用使用。
以上API仅仅适用于平台预置APP,例如:Settings。
在这里是满足我们需求的。
三方是无法只能通过SDK public API和上下文获取PackageManager。也就是说题记条件是不成立的,对普通应用开发这者来说,是要考虑设计是否合理了。