解释器模式(Interpreter Pattern),是一种用的比较少的行为型设计模式,其提供了一种解释语言的语法或表达式的方式。该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。
解释器模式的定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言的句子。
解释器模式的使用场景
1.如果某个简单的语言需要解释执行,而且可以将该语言中的语句表示为一个抽象语法树时,可以考虑使用解释器模式。
2.在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。像这样的从一个具体的符号出发,通过不断应用一些产生式规则从而生成一个字符串的集合,我们将描述这个集合的文法称为形式文法,顾名思义,形式文法与形式语言相对应,用来描述形式语言。
解释器模式的定义: 给定一个语言(如由abcdef六个字符串组成的字符串集合),定义它的文法的一种表示(如上面给出的S::=abA*ef和A::=cd),并定义一个解释器,该解释器使用该表示来解释语言中的句子。
Android源码中的解释器模式
对于Android来说,解释器模式的应用并不多见,在系统源码中也比较少,依然可以在一些地方看到解释器模式原理的应用。
如:AndroidManifest.xml这个应用配置文件,
如果应用是一本书的话,这个配置文件相当于书的目录,其中包含大量应用配置的声明定义,那么在android中如何读取这个配置文件呢?那么就不得不说PackageParser这个类,该类对AndroidManifest.xml中每一个组件标签创建了相应的类用以存储相应的信息。
package android.content.pm;
public class PackageParser {
/**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.静态内部类
*/
public final static class Package implements Parcelable {
... ...
public Package(String packageName) {
this.packageName = packageName;
this.manifestPackageName = packageName;
applicationInfo.packageName = packageName;
applicationInfo.uid = -1;
}
}
public final static class Permission extends Component<IntentInfo> implements Parcelable {
public final PermissionInfo info;
public boolean tree;
public PermissionGroup group;
public Permission(Package _owner) {
super(_owner);
info = new PermissionInfo();
}
public Permission(Package _owner, PermissionInfo _info) {
super(_owner);
info = _info;
}
... ...
}
public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable {
public final PermissionGroupInfo info;
public PermissionGroup(Package _owner) {
super(_owner);
info = new PermissionGroupInfo();
}
public PermissionGroup(Package _owner, PermissionGroupInfo _info) {
super(_owner);
info = _info;
}
... ...
}
public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
public final ActivityInfo info;
private boolean mHasMaxAspectRatio;
private boolean hasMaxAspectRatio() {
return mHasMaxAspectRatio;
}
public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
super(args, _info);
info = _info;
info.applicationInfo = args.owner.applicationInfo;
}
... ...
}
public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable {
public final ProviderInfo info;
public boolean syncable;
public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
super(args, _info);
info = _info;
info.applicationInfo = args.owner.applicationInfo;
syncable = false;
}
public Provider(Provider existingProvider) {
super(existingProvider);
this.info = existingProvider.info;
this.syncable = existingProvider.syncable;
}
... ...
}
public final static class Instrumentation extends Component<IntentInfo> implements
Parcelable {
public final InstrumentationInfo info;
public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
super(args, _info);
info = _info;
}
... ...
}
public final static class ActivityIntentInfo extends IntentInfo {
public Activity activity;
public ActivityIntentInfo(Activity _activity) {
activity = _activity;
}
... ...
}
public final static class ServiceIntentInfo extends IntentInfo {
public Service service;
public ServiceIntentInfo(Service _service) {
service = _service;
}
... ...
}
public static final class ProviderIntentInfo extends IntentInfo {
public Provider provider;
public ProviderIntentInfo(Provider provider) {
this.provider = provider;
}
... ...
}
}
如上述代码所示,PackageParser为Activity、Service、Provider、Permission等构件在其内部以内部类的方式 创建了对应的类,按照解释器模式的定义,这些类其实对应AndroidMainifest.xml文件中的一个标签,也就是一条文法,其在对该配置文件解析时充分运用了解释器模式分离实现、解释执行的特性。
在Android中,解析某一个apk文件会调用到PackageManagerService(PMS)中scanPackageLI方法,该方法有两种实现:
#PackageManagerService
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
... ...
}
#PackageManagerService
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
... ...
}
两者的唯一区别是scanPackageLI的第一个参数,第一种实现为File类型的对象,而第二种实现为PackageParser.Package类型的对象,在具体解析某一个文件时会先调用第一种实现去解析apk文件,再调用第二种实现将解析后的信息保存至PMS中。
#PackageManagerService
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
//解析标志
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
//调用PackageParser对象的parsePackage方法解析apk文件
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Static shared libraries have synthetic package names
if (pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(pkg);
}
//调用该方法的第二种实现,将解析后的信息保存至PMS
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
这里重点分析PackageParser类,该类如上所说,承担着具体的解析重任,上述代码中首先调用了其parsePackage方法,该方法也有两种实现:
#PackageParser
/**
* Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
*/
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
//调用第二种实现进行解析
return parsePackage(packageFile, flags, false /* useCaches */);
}
#PackageParser
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
if (parsed != null) {
return parsed;
}
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
//parsePackage方法中要 对传入File进行判断。
//Android 5.0 之后默认支持一个应用关联多个APK的形式,当某个app是这类情况的话,这些APK就会放在一个文件夹中,那么此时就会调用parseClusterPackage处理。
if (packageFile.isDirectory()) {
parsed = parseClusterPackage(packageFile, flags);
} else {
当传入的file就是一个apk文件的话,调用parseMonolithicPackage来处理。
解析单个apk
首先分析file为一个apk文件的情况,即parseMonolithicPackage:
parsed = parseMonolithicPackage(packageFile, flags);
}
long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
cacheResult(packageFile, flags, parsed);
if (LOG_PARSE_TIMINGS) {
parseTime = cacheTime - parseTime;
cacheTime = SystemClock.uptimeMillis() - cacheTime;
if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
+ "ms, update_cache=" + cacheTime + " ms");
}
}
return parsed;
}
第一种实现就是上面代码中调用到的方法,其主要逻辑就是为第二种实现准备参数后调用第二个方法。而parsePackage方法的第二种实现逻辑则要复杂的多,在其内部主要对整个AndroidManifest.xml配置文件进行具体的解析。
#PackageParser
/**
* Parse the given APK file, treating it as as a single monolithic package.
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in {@link #collectCertificates(Package, int)}.
*
* @deprecated external callers should move to
* {@link #parsePackage(File, int)}. Eventually this method will
* be marked private.
*/
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//AssetManager是资源管理框架
final AssetManager assets = newConfiguredAssetManager();
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) { //PMS初始化时,该变量一般为false,只有在data区加解密等特殊情况下,才为true
////如果是核心应用则以更轻量级的方式进行解析后,判断是否是核心应用,非核心应用不执行解析过程
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
try {
//先创建一个资源管理框架的对象AssetManager,在将其和apkfile等一起传入parseBaseApk方法;
// 在parseBaseApk的时候,会把assets传人
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.setCodePath(apkFile.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
#PackageParser
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath(); // 返回apkFile绝对路径名字字符串
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
Resources res = null;
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
//创建AndroidMainfest.xml的xml解析器
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
// 开始真正解析AndroidMainfest.xml
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSignatures(null);
//porting theme<
// If the pkg is a theme, we need to know what themes it overlays
// and determine if it has an icon pack
if (pkg.mIsThemeApk) {
//Determine existance of Overlays
//<panjuan for overlay 0207
ArrayList<String> overlayTargets = scanPackageOverlays(apkFile);
for(String overlay : overlayTargets) {
pkg.mOverlayTargets.add(overlay);
}//panjuan for overlay>
pkg.hasIconPack = packageHasIconPack(apkFile);
}
//porting theme>
return pkg;
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
}
}
该方法说白了,就是对AndroidMainfest.xml文件进行解析。真正解析这个xml的是其内部调用的parseBaseApk方法。该方法先调用以pkgName为参数的构造方法创建一个PackageParser.Package对象。
#PackageParser 内部类 Package
/**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.
*/
public final static class Package implements Parcelable {
public Package(String packageName) {
this.packageName = packageName;
this.manifestPackageName = packageName;
applicationInfo.packageName = packageName;
applicationInfo.uid = -1;
}
}
#PackageParser
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
... ...
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
#PackageParser
//该方法中有比较重要的解析
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
... ...
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
... ...
}
可以看到,parseBaseApkCommon方法的主要作用其实就是对AndroidManifest.xml配置文件中manifest下的每个子节点进行解析,这里主要看一下:parseBaseApplication方法是如何对application节点进行解析的。
/**
* Parse the {@code application} XML tree at the current parse location in a
* <em>base APK</em> manifest.
* <p>
* When adding new features, carefully consider if they should also be
* supported by split APKs.
*/
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
... ...
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
}
... ...
}
}
在程序中可以看到,parseBaseApplication除了对application节点内的属性进行解析之外,其主要逻辑还是通过遍历解析其子节点,然后继续分别调用不同的解析方法对其进行解析,这里以parseActivity为例。
private Activity parseActivity(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
... ...
}
这里需要注意的是,parseActivity方法不仅承担着对Activity的解析,其同样承担着对Broadcast的解析。与parseApplication方法类似,parseActivity内部逻辑也是遍历其子标签,并调用相应的方法对其进行解析,如上述代码中的parseIntent和parseMetaData。
参考《Android源码设计模式》