18

インターフェイスを実装した未定義の数のjarクラスを動的にロードする必要があるAndroidアプリケーションがあります。

実際、ディレクトリを見て、このディレクトリにあるすべての jar ファイルをリストします。jar ファイルのマニフェストを開き、関連するクラスを見つけてリストします。その後、dexClassLoader をインスタンス化して、すべての jar ファイルをロードし、マニフェストで見つけたクラスがインターフェイスを実装しているかどうかを確認しました。このように、最初にそれらを知らなくても、インターフェースを実装したすべてのクラスを持つことができます

再開するには、インターフェイスを実装するクラス jar のリストがありますが、リストは Android アプリケーションと私には不明です。jar クラスのリストは、アプリケーションを起動するたびに変更できます。

しかし、DexClassLoader を作成しようとすると失敗します。私は常にヌルポインタを持っています

DexClassLoader classLoader = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());

テストを行うために、エミュレータを使用しました。DDMS で jar ファイルをディレクトリ /data/data/com.example.Myappli/JarFilesDirectory/*.jar にコピーしました

私のjarファイルの内容がdexファイルであることに注意してください

私はこれについて多くのことを読みました。いくつかの権限の問題 私はすべてを試しましたが、解決策が見つかりませんでした

誰か助けてください!!!

ここではjarファイルのマニフェストの内容

マニフェスト バージョン: 1.0

モジュール クラス: com.example.asktester.AskPeripheral

ここに私のコード:

パブリッククラスModuleLoader {

private static List<URL> urls = new ArrayList<URL>(); 

private static List<String> getModuleClasses(String folder)
{ 
    List<String> classes = new ArrayList<String>(); 

    //we are listing the jar files
    File[] files = new File(folder).listFiles(new ModuleFilter()); 

    for(File f : files)
    { 
        JarFile jarFile = null; 

        try 
        { 
            //we open the jar file
            jarFile = new JarFile(f); 

            //we recover the manifest 
            Manifest manifest = jarFile.getManifest(); 

            //we recover the class
            String moduleClassName = manifest.getMainAttributes().getValue("Module-Class"); 

            classes.add(moduleClassName); 

            urls.add(f.toURI().toURL()); 
        }
        catch (IOException e) 
        { 
            e.printStackTrace(); 
        } 
        finally
        { 
            if(jarFile != null)
            { 
                try
                { 
                    jarFile.close(); 
                }
                catch (IOException e) 
                { 
                    e.printStackTrace(); 
                } 
            } 
        } 
    } 

    return classes; 
} 

private static class ModuleFilter implements FileFilter { 
    @Override 
    public boolean accept(File file) { 
        return file.isFile() && file.getName().toLowerCase().endsWith(".jar"); 
    } 
}

private static ClassLoader classLoader; 

public static List<IPeripheral> loadModules(String folder, Context CurrentContext) throws IOException, ClassNotFoundException
{ 
    List<IPeripheral> modules = new ArrayList<IPeripheral>(); 

    List<String> classes = getModuleClasses(folder);


    final File dexInternalStoragePath = new File(CurrentContext.getDir("dex", Context.MODE_PRIVATE),"ask.dex");

     File dexOutputDir = CurrentContext.getDir("dex", Context.MODE_PRIVATE);

     final File dexClasses = new File(CurrentContext.getDir("dex", Context.MODE_PRIVATE),"ASK.jar");
     DexFile dexFile = DexFile.loadDex(dexClasses.getAbsolutePath(), dexOutputDir.getAbsolutePath(), 0);




    DexClassLoader classLoader = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
    //Class<?> myClass = classLoader.loadClass("com.example.asktester.AskPeripheral");



            if(IPeripheral.class.isAssignableFrom(myClass )){ 
                Class<IPeripheral> castedClass = (Class<IPeripheral>)myClass ; 

                IPeripheral module = castedClass.newInstance(); 

                modules.add(module); 
        }  
    }
        catch (ClassNotFoundException e1) 
        { 
            e1.printStackTrace(); 
        } 
        catch (InstantiationException e) 
        { 
            e.printStackTrace(); 
        }
        catch (IllegalAccessException e) 
        { 
            e.printStackTrace(); 
        } 
    } 

    return modules; 
}
4

2 に答える 2

18

私の問題の解決策を見つけました。

Android アプリケーションでインターフェイスを実装するクラスである jar を動的にロードするには、いくつかのジョブを jar で実行する必要があります。

  • jar の独自のマニフェストを作成し、この情報を入力します

     Manifest-Version: 1.0
     Module-Class: com.example.myjar.MyPeripheral
    
  • Eclipse を使用して jar をエクスポートし、独自のマニフェストを使用するパラメーターを入力します。

  • jar に関連付けられた classes.dex を作成します (このファイルは Dalvik VM に必要です。単に jar を dalvik VM で読み取ることはできません)。

    dx --dex --output=C:\classes.dex C:\MyJar.jar
    

注意してください、dexファイルの名前はclasses.dexでなければなりません

  • jar ファイルにファイル classes.dex を追加します。

    aapt add C:\MyJar.jar C:\classes.dex
    
  • また、dalvik キャッシュ ディレクトリに書き込む権限も必要です。

       adb shell chmod 777 /data/dalvik-cache
    

    毎回実行して、エミュレータを再起動します

  • このjarファイルをSDカードなどのエミュレーターに配置します

  • PathClassLoader を使用して jar ファイルをロードする

    dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader("/Sdcard/MyJar.jar", ModuleLoader.class.getClassLoader());
    

注意: Eclipse の LogCat は貴重な情報を提供します。そのメッセージを見ることを忘れないでください

以下、コード:

私のインターフェース

package com.example.StandartPeripheral; 

public interface IPeripheral {

    public boolean Initialize();
    public boolean configure();
    public boolean execute();
    public String GetName();
}

インターフェイスを実装する MyPeripheral

public class MyPeripheral implements IPeripheral {

    //public static void main(String[] args) {}

    private final String PeripheralName = "MyPeripheral";

    public boolean Initialize()
    {

        System.out.println("Initialize "); 
        return true;
    };

    public boolean configure()
    {
        System.out.println("Configure !"); 
        return true;
    };

    public boolean execute()
    {
        System.out.println("Execute !"); 
        return true;
    };

    public String GetName()
    {
        return PeripheralName;
    }

}

jar ファイルを動的にロードする方法

package com.example.ModuleLoader;


import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import android.annotation.SuppressLint;
import android.content.Context;

import com.example.StandartPeripheral.IPeripheral;


public class ModuleLoader { 

    private static List<URL> urls = new ArrayList<URL>(); 


    // to retrieve the unknown list of jar files contained in the directory folder
        // in my case it was in the SDCard folder
        // link to create a SDCard directory on the Eclipse emulator
        // http://blog.lecacheur.com/2010/01/14/android-avoir-acces-a-une-carte-memoire-dans-lemulateur/
        // retrieve the classes of all this jar files and their URL (location)

    private static List<String> getModuleClasses(String folder)
    { 
        List<String> classes = new ArrayList<String>(); 

        //we are listing the jar files
        File[] files = new File(folder).listFiles(new ModuleFilter()); 

        for(File f : files)
        { 
            JarFile jarFile = null; 

            try 
            { 
                //we open the jar file
                jarFile = new JarFile(f); 

                //we recover the manifest 
                Manifest manifest = jarFile.getManifest(); 

                //we recover the class name of our peripherals thanks to ours manifest
                String moduleClassName = manifest.getMainAttributes().getValue("Module-Class"); 

                classes.add(moduleClassName); 

                urls.add(f.toURI().toURL()); 
            }
            catch (IOException e) 
            { 
                e.printStackTrace(); 
            } 
            finally
            { 
                if(jarFile != null)
                { 
                    try
                    { 
                        jarFile.close(); 
                    }
                    catch (IOException e) 
                    { 
                        e.printStackTrace(); 
                    } 
                } 
            } 
        } 

        return classes; 
    } 

    private static class ModuleFilter implements FileFilter { 
        @Override 
        public boolean accept(File file) { 
            return file.isFile() && file.getName().toLowerCase().endsWith(".jar"); 
        } 
    }

    //This function loads the jar file into the dalvik system
        // retrieves the associated classes using its name
        // and try to know if the loaded classes are implementing our interface


    public static List<IPeripheral> loadModules(String folder, Context CurrentContext)  { 
        List<IPeripheral> modules = new ArrayList<IPeripheral>(); 

        List<String> classes = getModuleClasses(folder);

            int index  = 0;
        for(String c : classes)
        { 
            try
            { 
                dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader(urls.get(index).toString(), ModuleLoader.class.getClassLoader());
                Class<?> moduleClass = Class.forName(c, true, myClassLoader); 
                //check and cast to an interface, then use it
                if(IPeripheral.class.isAssignableFrom(moduleClass))             
                { 
                    @SuppressWarnings("unused")
                    Class<IPeripheral> castedClass = (Class<IPeripheral>)moduleClass; 

                    IPeripheral module =  (IPeripheral)moduleClass.newInstance(); 

                    modules.add(module); 
                }  
                index++;
        }
            catch (ClassNotFoundException e1) 
            { 
                e1.printStackTrace(); 
            } 
            catch (InstantiationException e) 
            { 
                e.printStackTrace(); 
            }
            catch (IllegalAccessException e) 
            { 
                e.printStackTrace(); 
            } 
        } 

        return modules; 
    }

}
于 2012-07-19T08:43:10.103 に答える
8

また、Dalvik パス クラス ローダーではなく ClassLoader を使用することをお勧めします。

  ClassLoader cl = new DexClassLoader(url, ApplicationConstants.ref_currentActivity.getFilesDir().getAbsolutePath(), null, ModuleList.class.getClassLoader());

url は、「から」ロードするファイルの場所です。ApplicationConstants.ref_currentActivity は単なるアクティビティ クラスです。私の実装は、動的なモジュラー ローディングのためにかなり複雑です。そのため、この方法で追跡する必要がありました。ただし、そのクラスが既にアクティビティである場合、他の人はおそらく「this」を使用できます。

dalvik よりもクラスローダーを使用する主な理由は、ファイルをキャッシュに書き込む必要がないためです。したがって、パーミッション chmod 777 /data/dalvik-cache は不要です。もちろん、あなたもそうしません。ルート化された電話のルートからこのコマンドをプログラムで渡す必要があります。

アプリがルート化を必要としているという理由だけで、ユーザーに携帯電話のルート化を強制しないことが常に最善です。特に、あなたのアプリがより専門的な「会社での使用を意図したもの」である場合は、通常、ルート化された電話の使用に対する作業ポリシーも適用されます。

モジュールの読み込みについて質問がある場合は、お気軽にお問い合わせください。私の現在のコードのベースはすべて、Virginie Voirin と私自身の修正のおかげです。頑張ってください!

于 2012-10-30T13:21:24.907 に答える