Entschuldigung, ich bin spät dran und die Frage hat bereits eine akzeptierte Antwort, aber ja , Sie können externe Bibliotheken herunterladen und ausführen. So habe ich es gemacht:
Ich habe mich gefragt, ob dies machbar ist, also habe ich die folgende Klasse geschrieben:
package org.shlublu.android.sandbox;
import android.util.Log;
public class MyClass {
public MyClass() {
Log.d(MyClass.class.getName(), "MyClass: constructor called.");
}
public void doSomething() {
Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
}
}
Und ich habe es in eine DEX-Datei gepackt, die ich auf der SD-Karte meines Geräts gespeichert habe /sdcard/shlublu.jar
.
Dann schrieb ich das "dumme Programm" unten, nachdem ich es MyClass
aus meinem Eclipse-Projekt entfernt und bereinigt hatte:
public class Main extends Activity {
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
final String libPath = Environment.getExternalStorageDirectory() + "/shlublu.jar";
final File tmpDir = getDir("dex", 0);
final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass");
final Object myInstance = classToLoad.newInstance();
final Method doSomething = classToLoad.getMethod("doSomething");
doSomething.invoke(myInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Grundsätzlich wird die Klasse folgendermaßen MyClass
geladen:
ein ... kreieren DexClassLoader
Verwenden Sie es, um die Klasse MyClass
aus zu extrahieren"/sdcard/shlublu.jar"
und speichern Sie diese Klasse im "dex"
privaten Verzeichnis der Anwendung (interner Speicher des Telefons).
Anschließend wird eine Instanz von erstellt MyClass
und doSomething()
die erstellte Instanz aufgerufen.
Und es funktioniert ... Ich sehe die MyClass
in meinem LogCat definierten Spuren :
Ich habe sowohl einen Emulator 2.1 als auch mein physisches HTC-Handy (auf dem Android 2.2 ausgeführt wird und das NICHT gerootet ist) ausprobiert.
Dies bedeutet, dass Sie externe DEX-Dateien erstellen können, die von der Anwendung heruntergeladen und ausgeführt werden können. Hier wurde es auf die harte Tour gemacht (hässliche Object
Casts, Method.invoke()
hässliche Calls ...), aber es muss möglich sein, mit Interface
s zu spielen , um etwas sauberer zu machen.
Beeindruckend. Ich bin der erste überrascht. Ich hatte a erwartet SecurityException
.
Einige Fakten zur weiteren Untersuchung:
- Mein DEX shlublu.jar wurde signiert, aber nicht meine App
- Meine App wurde über eine Eclipse / USB-Verbindung ausgeführt. Dies ist also eine nicht signierte APK, die im DEBUG-Modus kompiliert wurde
Shlublus Antwort ist wirklich nett. Einige kleine Dinge, die einem Anfänger helfen würden:
<application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".NotExecutable" android:label="@string/app_name"> </activity> </application>
Stellen Sie im Manifest des Bibliotheksprojekts sicher, dass Sie Folgendes haben: (".NotExecutable" ist kein reserviertes Wort. Ich musste nur etwas hier einfügen.)Um die .dex-Datei zu erstellen, führen Sie einfach das Bibliotheksprojekt als Android-Anwendung (zum Kompilieren) aus und suchen Sie die .apk-Datei aus dem bin-Ordner des Projekts.
Andere Schritte sind die gleichen wie von Shlublu beschrieben.
quelle
dx --dex --keep-classes --output=newjar.jar libproject.jar
. Vielleicht gibt es eine Möglichkeit, diesen Dex-Schritt automatisch zu machen, wenn Eclipse die Bibliothek erstellt.Ich bin nicht sicher, ob Sie dies erreichen können, indem Sie Java-Code dynamisch laden. Möglicherweise können Sie versuchen, eine Skript-Engine in Ihren Code wie Rhino einzubetten, die Java-Skripte ausführen kann, die dynamisch heruntergeladen und aktualisiert werden können.
quelle
sicher ist es möglich. apk, das nicht installiert ist, kann von der Host-Android-Anwendung aufgerufen werden. Im Allgemeinen können Sie den Lebenskreis von Ressourcen und Aktivitäten auflösen und dann jar oder apk dynamisch laden. Einzelheiten finden Sie in meiner Open-Source-Forschung zu Github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md
Außerdem sind DexClassLoader und Reflection erforderlich. Sehen Sie sich nun einen Schlüsselcode an:
/** * Load a apk. Before start a plugin Activity, we should do this first.<br/> * NOTE : will only be called by host apk. * @param dexPath */ public DLPluginPackage loadApk(String dexPath) { // when loadApk is called by host apk, we assume that plugin is invoked by host. mFrom = DLConstants.FROM_EXTERNAL; PackageInfo packageInfo = mContext.getPackageManager(). getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES); if (packageInfo == null) return null; final String packageName = packageInfo.packageName; DLPluginPackage pluginPackage = mPackagesHolder.get(packageName); if (pluginPackage == null) { DexClassLoader dexClassLoader = createDexClassLoader(dexPath); AssetManager assetManager = createAssetManager(dexPath); Resources resources = createResources(assetManager); pluginPackage = new DLPluginPackage(packageName, dexPath, dexClassLoader, assetManager, resources, packageInfo); mPackagesHolder.put(packageName, pluginPackage); } return pluginPackage; }
Ihre Anforderungen sind in dem eingangs erwähnten Open-Source-Projekt nur teilweise von Bedeutung.
quelle
Wenn Sie Ihre .DEX-Dateien im externen Speicher des Telefons aufbewahren, z. B. auf der SD-Karte (nicht empfohlen! Jede App mit denselben Berechtigungen kann Ihre Klasse leicht überschreiben und einen Code-Injection-Angriff ausführen), stellen Sie sicher, dass Sie die angegeben haben App-Berechtigung zum Lesen des externen Speichers. Die Ausnahme, die ausgelöst wird, wenn dies der Fall ist, ist "ClassNotFound", was ziemlich irreführend ist. Fügen Sie Folgendes in Ihr Manifest ein (konsultieren Sie Google für die aktuellste Version).
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" /> ... </manifest>
quelle
Technisch sollte funktionieren, aber was ist mit Google-Regeln? Von: play.google.com/intl/en-GB/about/developer-content-policy-pr int
quelle
Ich denke, die Antwort von @Shlublu ist richtig, aber ich möchte nur einige wichtige Punkte hervorheben.
Um die Benutzeroberfläche aus einem externen JAR zu laden, können wir Fragment verwenden. Erstellen Sie die Instanz des Fragments und binden Sie es in die Aktivität ein. Stellen Sie jedoch sicher, dass das Fragment die Benutzeroberfläche dynamisch wie unten angegeben erstellt.
public class MyFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); LinearLayout layout = new LinearLayout(getActivity()); layout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); Button button = new Button(getActivity()); button.setText("Invoke host method"); layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); return layout; } }
quelle