English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android APK应用安装原理解析之AndroidManifest使用PackageParser.parserPackage原理分析

Questo articolo spiega l'installazione di applicazioni APK in Android utilizzando il principio di PackageParser.parserPackage. Condividiamo questo con tutti voi per riferimento, come segue:

Quando Android installa un APK, inizialmente lo analizza, qui ci sono molte cose da fare, una delle quali è analizzare il file Manifest.xml e impacchettare tutti gli elenchi di Manifest degli APK in vari oggetti e conservarli in memoria

La classe di analisi di Manifest è molto importante, si chiama frameworks\base\core\java\android\content\pm\PackageParser

PackageManagerService chiama il metodo parserPackage di PackageParser per analizzare l'elenco APK, iniziamo ad analizzare l'implementazione di PackageParser:

PackageParser utilizza lo strumento XMLPullParser per analizzare XML, quindi lo impacchetta rispettivamente attraverso vari classi xxxInfo di android.content.pm:

public Package parsePackage(File sourceFile, String destCodePath,
  DisplayMetrics metrics, int flags) {
//Ultimo messaggio di errore di analisi da eseguire
mParseError = PackageManager.INSTALL_SUCCEEDED;
//Ottiene il percorso del file da analizzare
mArchiveSourcePath = sourceFile.getPath();
//Se non si deve analizzare un tipo di file, si salta e si restituisce il metodo
if (!sourceFile.isFile()) {
  Log.w(TAG, "Omettendo directory: " + mArchiveSourcePath);
  //更新错误信息
  mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
  return null;
}
//Se il file non termina con .apk e il flag non è stato determinato come APK, restituisce anche
if (!isPackageFilename(sourceFile.getName()))
    && (flags&PARSE_MUST_BE_APK) != 0) {
  if ((flags&PARSE_IS_SYSTEM) == 0) {
    //Ci aspettiamo di avere file non-.apk nella directory di sistema,
    // therefore don't warn about them.
    Log.w(TAG, "Skipping non-package file: "+mArchiveSourcePath);
  }
  //更新错误信息
  mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
  return null;
}
if ((flags&PARSE_CHATTY) != 0 && Config.LOGD) Log.d(
  TAG, "Scanning package: "+ mArchiveSourcePath);
XmlResourceParser parser = null;
AssetManager assmgr = null;
boolean assetError = true;
try {
  assmgr = new AssetManager();
  //将一个文件添加到AssetManager中并返回一个唯一标识
  int cookie = assmgr.addAssetPath(mArchiveSourcePath);
  if(cookie != 0) {
    //通过标识去AssetManager中找到标识对应资源中的Manifest清单文件,并返回一个XML的解析器
    parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
    //走到这里证明一切顺利
    assetError = false;
  XmlUtils.skipCurrentTag(parser);
    Log.w(TAG, "Failed adding asset path:\+mArchiveSourcePath);
  }
} catch (Exception e) {
  Log.w(TAG, "Unable to read AndroidManifest.xml of ");
      + mArchiveSourcePath, e);
}
if(assetError) {
  if (assmgr != null) assmgr.close();
  mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
  return null;
}
String[] errorText = new String[1];
Package pkg = null;
Exception errorException = null;
try {
  //XXXX todo: è necessario determinare la configurazione corretta.
  Resources res = new Resources(assmgr, metrics, null);
  //Questo è il metodo che effettivamente analizza il package, è un metodo privato
  pkg = parsePackage(res, parser, flags, errorText);
} catch (Exception e) {
  errorException = e;
  mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
}
if (pkg == null) {
  if (errorException != null) {
    Log.w(TAG, mArchiveSourcePath, errorException);
  XmlUtils.skipCurrentTag(parser);
    Log.w(TAG, mArchiveSourcePath + " (at "
        + parser.getPositionDescription()
        + "): " + errorText[0]);
  }
  parser.close();
  assmgr.close();
  if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
    Log.w(TAG, "<manifest> has more than one <application>");
  }
  return null;
}

parserPackage ha chiamato un altro parserPackage sovraccaricato

private Package parsePackage(
    Resources res, XmlResourceParser parser, int flags, String[] outError)
    throws XmlPullParserException, IOException {
    AttributeSet attrs = parser;
    //Ogni volta che si chiama questo metodo, svuota queste variabili
    mParseInstrumentationArgs = null;
    mParseActivityArgs = null;
    mParseServiceArgs = null;
    mParseProviderArgs = null;
    //这里调用这个方法获得包名
    String pkgName = parsePackageName(parser, attrs, flags, outError);
    if (pkgName == null) {
      mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
      return null;
    }
    int type;
    final Package pkg = new Package(pkgName);
    boolean foundApp = false;
    //从资源里获得AndroidManifest的数组
    TypedArray sa = res.obtainAttributes(attrs,
        com.android.internal.R.styleable.AndroidManifest)
    //继续挖掘出版本号
    pkg.mVersionCode = sa.getInteger(
        com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
    //获取版本名
    pkg.mVersionName = sa.getNonConfigurationString(
        com.android.internal.R.styleable.AndroidManifest_versionName, 0);
    if (pkg.mVersionName != null) {
      pkg.mVersionName = pkg.mVersionName.intern();
    }
    //获得sharedUserId
    String str = sa.getNonConfigurationString(
        com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
    if (str != null && str.length() > 0) {
      //验证包名是否符合规则
      String nameError = validateName(str, true);
      if (nameError != null && !"android".equals(pkgName)) {
        outError[0] = "<manifest> specifica un nome badSharedUserId non corretto \""
          + str + "\": " + nameError;
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
        return null;
      }
      pkg.mSharedUserId = str.intern();
      pkg.mSharedUserLabel = sa.getResourceId(
          com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
    }
    sa.recycle();
    // Posizione di installazione
    pkg.installLocation = sa.getInteger(
        com.android.internal.R.styleable.AndroidManifest_installLocation,
        PARSE_DEFAULT_INSTALL_LOCATION);
    // Le risorse boolean sono -1, quindi 1 significa che non conosciamo il valore.
    int supportsSmallScreens = 1;
    int supportsNormalScreens = 1;
    int supportsLargeScreens = 1;
    int resizeable = 1;
    int anyDensity = 1;
    int outerDepth = parser.getDepth();
    // Il momento cruciale è arrivato, la vera解析 ha iniziato
    while ((type=parser.next()) != parser.END_DOCUMENT
        && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
      if (type == parser.END_TAG || type == parser.TEXT) {
        if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
      }
      String tagName = parser.getName();
      outError[0] = "<manifest> has more than one <application>";
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
          else {
            }
            Log.w(TAG, "<manifest> has more than one <application>");
            return null;
          XmlUtils.skipCurrentTag(parser);
            continue;
            foundApp = true;
            if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
          }
        }
        else if (tagName.equals("permission-group")) {
        }
          return null;
        }
      if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
        else if (tagName.equals("permission")) {
          return null;
        }
      }
        if (parsePermission(pkg, res, parser, attrs, outError) == null) {
          return null;
        }
      } else if (tagName.equals("permission-tree")) {
        if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
          return null;
        }
      } else if (tagName.equals("uses-permission")) {
        sa = res.obtainAttributes(attrs,
            com.android.internal.R.styleable.AndroidManifestUsesPermission);
        // Note: don't allow this value to be a reference to a resource
        // che potrebbe cambiare.
        String name = sa.getNonResourceString(
            com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
        sa.recycle();
       ...................................................
       ...................................................
       ...................................................Spazio limitato

Ecco come si analizzano diversi elementi con diversi metodi, l'ordine di chiamata è il seguente:

In realtà ci sono molte piccole tecniche in questi piccoli metodi, se sei interessato, puoi assaporarli attentamente.

Chi è interessato a ulteriori contenuti su Android può consultare le sezioni speciali di questo sito: 'Introduzione e avanzamento alla programmazione Android', 'Trucchi di debug e soluzioni ai problemi comuni di Android', 'Sommarizzazione dell'uso dei componenti di base di Android', 'Sommarizzazione delle tecniche di View Android', 'Sommarizzazione delle tecniche di layout Android' e 'Sommarizzazione dell'uso dei controlli Android'.

Spero che il contenuto di questo articolo possa essere utile per la progettazione di applicazioni Android.

Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, è di proprietà del rispettivo autore, il contenuto è stato contribuito autonomamente dagli utenti di Internet e caricato autonomamente, il sito web non detiene i diritti di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, invia un'e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare, fornendo prove pertinenti. Una volta verificata, il sito eliminerà immediatamente il contenuto sospetto di violazione del copyright.