【Androidインタビュー質問】2023年最新インタビュートピック:Javaリフレクションクラスローディングとダイナミックプロキシ(1)

1 PathClassLoader と DexClassLoader の違いは何ですか?

この質問は何を調査しようとしているのでしょうか?

Android のクラスローディングメカニズムの原理

検査の知識ポイント

ClassLoader クラスロードメカニズム

候補者の答え方

ClassLoader は、私たちがよくクラスローダーと呼ぶものです。

クラスローダーの概要

Java プログラムは 1 つ以上のクラス ファイルで構成されます。プログラムの実行中、クラス ファイルを使用するには、その前にクラス ファイルを JVM にロードする必要があります。これらのクラス ファイルのロードは、Java クラス ロード メカニズムによって行われます。ClassLoader の役割は、単にクラス ファイルをロードし、プログラムの実行時に使用できるように提供することです。各 Class オブジェクト内には、どの ClassLoader によってロードされたかを識別するための classLoader フィールドがあります。

class Class<T> {
    
    
  ...
  private transient ClassLoader classLoader;
  ...
}

ClassLoader は抽象クラスであり、多くの具体的な実装クラスがあります。最も重要なものは次のとおりです。

  • BootClassLoader

    Android Framework レイヤーのクラス ファイルをロードするために使用されます。

  • PathClassLoader

    Android アプリケーションのクラスローダー用。指定された dex とclasses.dexをjar、zip、apkでロードできます

  • DexClassLoader

    指定された dex と、jar、zip、apk 内のclasses.dex をロードするために使用されます。

  • InMemoryDexClassLoader

    Android 8.0で追加され、dexをメモリにロードするために使用されます

DexClassLoader と PathClassLoader

Android 5.0 より前のバージョンでは、2 つの違いは次のとおりです。

  1. DexClassLoader:jar、apk、dex をロード可能、SD カードからロード可能
  2. PathClassLoader : システム (つまり、/data/app ディレクトリ) にインストールされている apk ファイルのみをロードできます

ただし、Android のバージョンがアップグレードされたため、Android 5.0 以降ではこの限りではなくなりました。

まずAndroid のPathClassLoaderと を見てみましょうDexClassLoader

public class DexClassLoader extends BaseDexClassLoader {
    
    
  
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
    
    
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

public class PathClassLoader extends BaseDexClassLoader {
    
    
   
    public PathClassLoader(String dexPath, ClassLoader parent) {
    
    
        super(dexPath, null, null, parent);
    }

   
    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
    
    
        super(dexPath, null, libraryPath, parent);
    }
}

これらはPathClassLoader両方ともDexClassLoader同じ親クラスを継承します: BaseDexClassLoader。この 2 つの違いは、 dexopt の結果を保存するために最適化されたDexClassLoaderディレクトリを渡す必要があるのに対し、後者は渡さないことです。

デキソプト:

Dalvikでは、仮想マシンが dex ファイルをロードするときに、dex ファイルを検証して最適化します。dex ファイルの最適化結果は、いくつかの最適化されたオペコードを使用する odex (Optimized dex) ファイルになります。dex のドキュメントも同様です。

実際、そうであるかPathClassLoaderどうかに関係なくDexClassLoader、親クラスをオーバーライドする他のメソッドがないことがわかります。optimizedDirectory最適化ディレクトリが Null の場合、つまりAndroid5.0 より前の場合、デフォルトの最適化ディレクトリ/data/dalvik-cache/PathClassLoaderが使用されます

APK をこのディレクトリにインストールすると、システムは odex ファイルをそのディレクトリに自動的に保存します: data@[email protected]@classes.dex
ここに画像の説明を挿入

ロードを使用するときPathClassLoader、ロードされた APK が携帯電話にまだインストールされていない場合は、「Dex キャッシュ ディレクトリは書き込み可能ではありません: /data/dalvik-cache、アプリケーション自体にはこのディレクトリに対する書き込み権限がありません」とレポートされます。したがって、PathClassLoader は、インストールされた APK 内の dex ファイルのみをロードできます。

ARTではロード方法が全く異なり、インストール時にdexファイルに対してdex2oat(AOT事前コンパイル操作)が実行され、OAT(実際にはELFファイル)の実行ファイル(マシンコード)にコンパイルされます。また、読み込み中に oat ファイルが正常に読み込めない場合でも、元の dex から読み込もうとするため、ART では、指定された任意の dex に加え、jar、zip、および apk 内の class.dex を読み込むことができますPathClassLoaderDexClassLoaderただし、元の dex からロードすると、dex2oat が失敗し、ロード速度が速くなり、動作効率が低下します。

Android N以降は、解釈、AOT、JIT混合モードが採用されています。

Android 8.1 以降では、次のようDexClassLoaderになります。

public class DexClassLoader extends BaseDexClassLoader {
    
    
35    /**
36     * Creates a {@code DexClassLoader} that finds interpreted and native
37     * code.  Interpreted classes are found in a set of DEX files contained
38     * in Jar or APK files.
39     *
40     * <p>The path lists are separated using the character specified by the
41     * {@code path.separator} system property, which defaults to {@code :}.
42     *
43     * @param dexPath the list of jar/apk files containing classes and
44     *     resources, delimited by {@code File.pathSeparator}, which
45     *     defaults to {@code ":"} on Android
46     * @param optimizedDirectory this parameter is deprecated and has no effect
47     * @param librarySearchPath the list of directories containing native
48     *     libraries, delimited by {@code File.pathSeparator}; may be
49     *     {@code null}
50     * @param parent the parent class loader
51     */
52    public DexClassLoader(String dexPath, String optimizedDirectory,
53            String librarySearchPath, ClassLoader parent) {
    
    
54        super(dexPath, null, librarySearchPath, parent);
55    }
56}

この時点では、DexClassLoader の最適化されたディレクトリも null を渡すように修正されているため、両者に違いはありません。

要約する

  • Android 4.4 以下:
    • DexClassLoader:jar、apk、dex をロード可能、SD カードからロード可能
    • PathClassLoader : システム (つまり、/data/app ディレクトリ) にインストールされている apk ファイルのみをロードできます
  • Android 5.0~Android 8.0:
    • DexClassLoader:jar、apk、dex をロード可能、SD カードからロード可能
    • PathClassLoader:jar、apk、dex をロードでき、SD カードからもロードできますが、dex2oat 操作を実行できなくなります。
  • Android 8.1以降:
    • DexClassLoader は PathClassLoader とまったく同じです

2 親委託メカニズムとは何ですか?また、親委託メカニズムが必要な理由は何ですか?

この質問は何を調査しようとしているのでしょうか?

クラスロードメカニズムの原理

検査の知識ポイント

クラスロードメカニズム

候補者の答え方

保護者の委任メカニズム

親委任メカニズムとは、クラス ローダーがクラス ロード要求を受け取ると、まずクラス ローダーがその要求を親クラス ローダーに委任することを意味します。これは各クラスローダにも当てはまり、親クラスローダが自身の探索範囲内で指定されたクラスを見つけられなかった場合にのみ、子クラスローダが自らそのクラスのロードを試みます。

public abstract class ClassLoader{
    
    
    //父类加载器
    ClassLoader parent;

    protected ClassLoader(ClassLoader parentLoader) {
    
    
            this(parentLoader, false);
    }

    ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
    
    
        if (parentLoader == null && !nullAllowed) {
    
    
            throw new NullPointerException("parentLoader == null && !nullAllowed");
        }
        parent = parentLoader;
    }

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    
    
        //先找缓存
        Class<?> clazz = findLoadedClass(className);
        if (clazz == null) {
    
    
             ClassNotFoundException suppressed = null;
            if(parent !=null){
    
    
                try {
    
    
                     //交给父类加载器加载
                    clazz = parent.loadClass(className, false);
                } catch (ClassNotFoundException e) {
    
    
                    suppressed = e;
                }
            }
            if (clazz == null) {
    
    
                try {
    
    
                    //父类加载器加载不到,自己加载
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
    
    
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }

        return clazz;
    }
}

親の委任メカニズムの役割

1. 重複読み込みを防止する

2. セキュリティ。システム クラスが改ざんできないことを保証します。

Java 仮想マシンは、異なるクラスのクラス名が同じであり、クラスをロードするローダーがすべて同じである場合にのみ、これがクラスであると判断します。親委任メカニズムがない場合、同じクラスが複数のクラスローダーによってロードされる可能性があるため、クラスが 2 つの異なるクラスとして認識され、相互に値を割り当てるときに問題が発生します。

親委任メカニズムにより、複数のローダーがクラスをロードするときに、最終的に 1 つのローダーによってクラスがロードされ、最終的なロード結果が同じになることが保証されます。

ユーザーが java というクラスを作成した場合、すべてのクラス ローダーが単独でロードできるようにすると、親委任モデルがなくなり、型システムでの基本的な動作が保証されなくなり、アプリケーションが混乱してしまいます。

3 Android でクラスをロードする方法は何ですか? 違いは何ですか?

この質問は何を調査しようとしているのでしょうか?

クラスロード機構におけるクラスロードのプロセス

検査の知識ポイント

  1. クラスロードプロセス
  2. クラスのロード時間

候補者はどう答えるべきか

Android がクラスをロードする方法は、実際には Java のクラスロードです。仮想マシンは、クラスを記述する情報を Class ファイルからメモリにロードし、データを検証、変換、解析、初期化して、最終的に仮想マシンが直接使用できる Java 型になります。

クラスのロード時間

クラスは次の場合に自動的にロードされます。

  1. new を使用してオブジェクトをインスタンス化し、サブクラスのインスタンスを作成すると、最初にその親クラスがロードされます。
  2. クラスの静的メソッドへのアクセス
  3. クラスの静的プロパティへのアクセス
  4. クラスへのリフレクティブ呼び出しを行う
  5. メインクラスはJavaプログラムで定義されており、メインメソッドの開始時にクラスがロードされます。

上記の 5 つの状況により、クラスのロードがトリガーされ、クラスの初期化が完了します。上記の状況に加えて、アクティブに呼び出しClassLoader#loadClass(name)Class.forName(name)ロードを行うこともできます。実際、Class.forName(name)指定されたクラスのロードも ClassLoader を通じて完了します。

public static Class<?> forName(String className) throws ClassNotFoundException {
    
    
    //得到调用者的类,如main方法所在类
	Class<?> caller = Reflection.getCallerClass();
    //ClassLoader.getClassLoader(caller):获得main方法所在类的类加载器,使用其完成className的加载
	return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

ただし、後者はクラスのロードを完了するだけでなく、クラスを初期化し、クラス内の静的ブロックも実行する点が異なりますClassLoader#loadClass(name)Class.forName(name) 代わりに、ClassLoader#loadClassクラスのロードのみが行われます。

もちろん、次のものも使用できますClass.forName(String name, boolean initialize, ClassLoader loader)2 番目のパラメーターの初期化により、クラスを初期化するかどうかを選択できます。

やっと

このインタビューの質問は今後も更新されますので、注目してください。
このインタビューの質問が必要な友達は、下の QR コードをスキャンして無料で入手できます。
同時に、以下のQRコードをスキャンすることで、ChatGPTロボットのサービスを楽しむためのグループに参加することもできます!

おすすめ

転載: blog.csdn.net/datian1234/article/details/131987451