Android 플러그인 - 리소스 로드
리소스는 APK 패키지의 부피가 너무 큰 원인 중 하나입니다.플러그인화 기술은 모듈을 분리하여 플러그인의 형식으로 불러옵니다.플러그인 기술에서는 각 플러그인을 별도의 APK로 독립적으로 실행할 수 있습니다.숙주가 플러그인을 시작하는 클래스는 플러그인 클래스의 자원 문제와 관련된 것을 피하기 어렵다.
그러면 플러그인 자원을 어떻게 불러오는가가 해결해야 할 문제가 된다.
의 원리
APK 패키지 프로세스 참조: Android 플러그인 기반 - APK 패키지 프로세스
Android 프로젝트는 apk로 포장될 때, aapt를 사용하여 프로젝트의 자원 이름과 id를 R.java에서 일일이 비추습니다.
R.java
public static final int ic_launcher=0x7f060054;
public static final int ic_launcher_background=0x7f060055;
public static final int ic_launcher_foreground=0x7f060056;
public static final int notification_action_background=0x7f060057;
리소스를 로드할 때마다 Resources 를 검색합니다.다음을 통해 다음을 수행할 수 있습니다.
Drawable drawable = resources.getDrawable(resId);
해당 리소스를 가져옵니다.
따라서 우리의 핵심 사고방식은 플러그인의 Resources와 플러그인의resId를 가져오는 것이다.
실천하다
그러면 플러그인의 Resources를 어떻게 얻을 수 있을까요?
ContextThemeWrapper.java
@Override
public Resources getResources() {
return getResourcesInternal();
}
private Resources getResourcesInternal() {
if (mResources == null) {
if (mOverrideConfiguration == null) {
mResources = super.getResources();
} else {
final Context resContext = createConfigurationContext(mOverrideConfiguration);
mResources = resContext.getResources();
}
}
return mResources;
}
Resources.java는 App 자원의 관리 유형이다.
/**
* Create a new Resources object on top of an existing set of assets in an
* AssetManager.
*
* @param assets Previously created AssetManager.
* @param metrics Current display metrics to consider when
* selecting/computing resource values.
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(null);
mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
주석을 통해 Resources를 차별화하는 것은 AssetManager입니다.
우리는 이 구조 방법이 PackageParser#parseBaseApk 방법에서 호출되는 것을 알아차렸다.Apk의 설치 과정을 본떠서 설치되지 않은 Apk에 Resources를 만들 수 있지 않을까요?
따라서 계속해서 AssetManager를 살펴보겠습니다.java:
/**
* Provides access to an application's raw asset files; see {@link Resources}
* for the way most applications will want to retrieve their resource data.
* This class presents a lower-level API that allows you to open and read raw
* files that have been bundled with the application as a simple stream of
* bytes.
*/
public final class AssetManager implements AutoCloseable {
...
}
주석을 통해 우리는 이 종류가 자원 파일에 접근하는 방식을 제공한다는 것을 알 수 있다.소스 코드를 읽으면 다음 방법을 찾을 수 있습니다.
AssetManager.java
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public final int addAssetPath(String path) {
return addAssetPathInternal(path, false);
}
주석을 통해 알 수 있듯이 이 방법은 ZIP 파일을 포장하는 방법을 제공합니다.이 주석은 Application 에 사용되지 않습니다.APK 파일이 사실 ZIP 파일인 건 알고 있습니다.
여기를 보고 우리의 생각이 떠올랐다.이 방법을 통해 플러그인 APK의 path를 가져와 AssetManager를 포장합니다.그리고 AssetManager로 Resources를 생성하면 이 Resources가 플러그인의 Resources입니다.플러그인 APK는 설치되어 있지 않지만 설치 절차를 따릅니다.
위의 분석을 통해 플러그인 Resources 를 검색할 수 있습니다.
/**
* Resource
* @param context apk
* @param pluginPath apk , apk
* @return
*/
public static Resources getPluginResources(Context context, String pluginPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
// addAssetPath(String path)
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
// Apk AssetManager
addAssetPath.invoke(assetManager, pluginPath);
// apk Resources
Resources superRes = context.getResources();
// apk Resources
Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
return mResources;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
이로써 플러그인에 대한 Resources를 획득했습니다. Drawable과 같은 리소스를 획득하는 방법은 다음과 같습니다.
Drawable drawable = resources.getDrawable(resId);
따라서 플러그인 R.java에 대응하는 플러그인 자원에 대한resId가 부족합니다.
우리는 반사하는 방식으로 R.java의 id를 얻을 수 있습니다:
/**
* apk id
*
* @param context
* @param pluginPath apk
*/
public static int getResId(Context context, String pluginPath, String apkPackageName, String resName) {
try {
// app_dex ,
File optimizedDirectoryFile = context.getDir("dex", Context.MODE_PRIVATE);
// DexClassLoader , :
// 1、 dex apk jar ,
// 2、apk、jar dex ,
// 3、 library , null,
// 4、 ClassLoader
DexClassLoader dexClassLoader = new DexClassLoader(pluginPath, optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader());
// apk , R id
Class> clazz = dexClassLoader.loadClass(apkPackageName + ".R$drawable");
Field field = clazz.getDeclaredField(resName);// resName
return field.getInt(R.id.class);// id
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
Resources 및 resId 검색을 완료하면 플러그인에 대한 리소스를 얻을 수 있습니다.
int resId = getResId(MainActivity.this.getApplication(), PATH, PLUGIN_PACKAGE_NAME, "ic_launcher");
Resources resources = getPluginResources(MainActivity.this.getApplication(), PATH);
Drawable drawable = resources.getDrawable(resId);
mIvTest.setImageDrawable(drawable);
이로써 플러그인화된 불러오는 자원의 기본적인 사고방식과 원리다.
총결산
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.