Android 애플리케이션에서 런타임에 라이브러리를 동적으로로드 할 수 있습니까?
런타임에 Java 라이브러리를 다운로드하고 사용하도록 Android 애플리케이션을 만드는 방법이 있습니까?
다음은 그 예입니다.
애플리케이션이 입력 값에 따라 몇 가지 계산을해야한다고 가정 해보십시오. 애플리케이션은 이러한 입력 값을 요청한 다음 필요한 Classe
s 또는 Method
s를 사용할 수 있는지 확인 합니다.
그렇지 않은 경우 서버에 연결하고 필요한 라이브러리를 다운로드 한 다음 런타임에로드하여 리플렉션 기술을 사용하여 필요한 메서드를 호출합니다. 구현은 라이브러리를 다운로드하는 사용자와 같은 다양한 기준에 따라 변경 될 수 있습니다.
죄송합니다. 늦었고 질문에 이미 답변이 허용되었지만 예 . 외부 라이브러리를 다운로드하여 실행할 수 있습니다. 내가 한 방법은 다음과 같습니다.
이것이 가능한지 궁금해서 다음 클래스를 작성했습니다.
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.");
}
}
그리고 장치의 SD 카드에 /sdcard/shlublu.jar
.
그런 다음 MyClass
Eclipse 프로젝트에서 제거 하고 정리 한 후 아래 "어리석은 프로그램"을 작성 했습니다.
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();
}
}
}
기본적으로 다음과 같이 클래스 MyClass
를 로드합니다 .
창조하다
DexClassLoader
클래스 추출하는 데 사용할
MyClass
에서를"/sdcard/shlublu.jar"
이 클래스를 애플리케이션의
"dex"
개인 디렉터리 (전화의 내부 저장소)에 저장합니다.
그런 다음의 인스턴스를 생성하고 생성 된 인스턴스를 MyClass
호출 doSomething()
합니다.
그리고 그것은 작동합니다 ... MyClass
LogCat에 정의 된 추적이 표시됩니다 .
에뮬레이터 2.1과 물리적 HTC 휴대폰 (Android 2.2를 실행 중이며 루팅되지 않음) 모두에서 시도했습니다.
즉, 애플리케이션이 다운로드하고 실행할 외부 DEX 파일을 만들 수 있습니다. 여기에서는 어려운 방식으로 만들어 졌지만 (추악한 Object
캐스트, Method.invoke()
추악한 콜 ...), Interface
s로 플레이하여 더 깨끗한 것을 만드는 것이 가능해야합니다 .
와. 처음으로 놀랐습니다. 나는 SecurityException
.
추가 조사에 도움이되는 몇 가지 사실 :
- 내 DEX shlublu.jar가 서명되었지만 내 앱이 아닙니다.
- 내 앱은 Eclipse / USB 연결에서 실행되었습니다. 따라서 이것은 DEBUG 모드로 컴파일 된 서명되지 않은 APK입니다.
Shlublu의 anwser는 정말 좋습니다. 초보자에게 도움이 될 몇 가지 작은 것 :
- 라이브러리 파일 "MyClass"의 경우 myClass 파일을 src 폴더에있는 유일한 파일로 포함하는 별도의 Android 애플리케이션 프로젝트를 만듭니다 (project.properties, manifest, res 등과 같은 다른 항목도 있어야 함).
in library project manifest make sure you have:
<application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".NotExecutable" android:label="@string/app_name"> </activity> </application>
(".NotExecutable" is not a reserved word. It is just that I had to put something here)For making the .dex file, just run the library project as android application (for the compiling) and locate .apk file from the bin folder of the project.
- Copy the .apk file to your phone and rename it as shlublu.jar file (an APK is actually a specialization of a jar, though)
Other steps are the same as described by Shlublu.
- Big thanks to Shlublu for cooperation.
I am not sure if you can achieve this by dynamically loading java code. May be you can try embedding a script engine your code like rhino which can execute java scripts which can be dynamically downloaded and updated.
sure, it is possible. apk which is not installed can be invoked by host android application.generally,resolve resource and activity's lifecircle,then,can load jar or apk dynamically. detail,please refer to my open source research on github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md
also,DexClassLoader and reflection is needed, now look at some key code:
/**
* 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;
}
your demands is only partly of function in the open source project mentioned at the begining.
If you're keeping your .DEX files in external memory on the phone, such as the SD card (not recommended! Any app with the same permissions can easily overwrite your class and perform a code injection attack) make sure you've given the app permission to read external memory. The exception that gets thrown if this is the case is 'ClassNotFound' which is quite misleading, put something like the following in your manifest (consult Google for most up to date version).
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
I think @Shlublu answer is correct but i just want to highlight some key points.
- We can load any classes from external jar and apk file.
- In Any way, we can load Activity from external jar but we can not start it because of the context concept.
To load the UI from external jar we can use fragment. Create the instance of the fragment and embedded it in the Activity. But make sure fragment creates the UI dynamically as given below.
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; } }
Technically should work but what about Google rules? From: play.google.com/intl/en-GB/about/developer-content-policy-print
An app distributed via Google Play may not modify, replace or update itself using any method other than Google Play’s update mechanism. Likewise, an app may not download executable code (e.g. dex, JAR, .so files) from a source other than Google Play. This restriction does not apply to code that runs in a virtual machine and has limited access to Android APIs (such as JavaScript in a WebView or browser).
'Nice programing' 카테고리의 다른 글
유사한 결과를 찾고 유사성을 기준으로 정렬하는 방법은 무엇입니까? (0) | 2020.11.11 |
---|---|
CDI는 Spring의 좋은 대체품입니까? (0) | 2020.11.11 |
.NET 4.0 작업 패턴을 사용하여 HTTPClient .ReadAsAsync로 JSON을 배열 또는 목록으로 역 직렬화 (0) | 2020.11.11 |
Groovy 클로저에서 "계속"시뮬레이션을위한 최상의 패턴 (0) | 2020.11.11 |
이메일 주소를 사용자 ID로 사용할 때의 장단점은 무엇입니까? (0) | 2020.11.11 |