Android 동적 로드 Activity 원리 상세 설명
Activity 를 불 러 오 는 것 은 일반 클래스 를 불 러 오 는 것 처럼 되 지 않 을 것 입 니 다.activity 는 시스템 구성 요소 로 서 자신의 수명 주기 가 있 고 시스템 의 많은 리 셋 제어 가 있 기 때문에 DexClassLoader 류 로 더 를 사용자 정의 하여 플러그 인 에 있 는 Activity 를 불 러 오 면 안 됩 니 다.
우선 activity 의 시작 절 차 를 알 아야 합 니 다.물론 간단하게 볼 뿐 너무 상세 하면 연구 하기 어렵 습 니 다.
startActivity 를 통 해 시작 한 후에 AMS 를 통 해 크로스 프로 세 스 를 통 해 applicationThread 의 scheduleLaunchActivity 로 되 돌 립 니 다.이때 Activity Client Record 대상 을 만 듭 니 다.이 대상 은 Activity 와 그의 관련 정 보 를 표시 합 니 다.예 를 들 어 activity Info 필드 는 시작 모드 등 을 포함 하고 loadedApk 도 있 습 니 다.말 그대로 불 러 온 APK 를 말 합 니 다.그 는 패키지 이름 을 Loaded Apk 의 키 쌍 에 적용 하 는 맵 에 놓 여 응용 에 관 한 정 보 를 포함 합 니 다.그리고 Handler 를 통 해 메 인 라인 으로 전환 합 니 다.
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
// 1. ActivityClientRecord packageInfo , null
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE);
}
// ...
Activity activity = null;
try {
// 2. !! ClassLoader LoadedApk , activity
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
// 3. activity , intent activity
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
// 4. !!! 。。。
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
// activity onCreate
mInstrumentation.callActivityOnCreate(activity, r.state);
//
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
// ...
}
return activity;
}
1.getPackageInfo 방법 은 최종 적 으로 LoadedApk 대상 을 되 돌려 줍 니 다.HashMap 의 데이터 구조 에서 가 져 옵 니 다.mPackages 는 패키지 이름과 LoadedApk 의 대응 관 계 를 유지 합 니 다.즉,모든 응용 프로그램 에 키 값 이 대응 합 니 다.null 이면 LoadedApk 대상 을 새로 만 들 고 Map 에 추가 합 니 다.이 대상 의 ClassLoader 필드 는 null 입 니 다!
public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
int flags) {
// true
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
&& ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
: true);
// ...
// includeCode true
// classloader null!!!
return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode);
}
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
synchronized (mPackages) {
WeakReference<loadedapk> ref;
if (includeCode) {
// includeCode true
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) {
if (localLOGV) // ...
// packageInfo null, LoadedApk, mPackages
packageInfo = new LoadedApk(this, aInfo, compatInfo, this, baseLoader, securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo. ) != 0);
if (includeCode) {
mPackages.put(aInfo.packageName, new WeakReference<loadedapk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName, new WeakReference<loadedapk>(packageInfo));
}
}
return packageInfo;
}
}</loadedapk></loadedapk></loadedapk>
2.이 activity 에 대응 하 는 클래스 로 더 를 가 져 옵 니 다.위 에서 말 했 듯 이 mClassLoader 는 null 이기 때문에 applicationLoaders\#getClassLoader(zip,library Path,mBaseClassLoader)방법 을 실행 합 니 다.
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
return mClassLoader;
}
// ...
// ,
// zip Apk ,libraryPath JNI
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, libraryPath, mBaseClassLoader);
initializeJavaContextClassLoader();
StrictMode.setThreadPolicy(oldPolicy);
} else {
if (mBaseClassLoader == null) {
mClassLoader = ClassLoader.getSystemClassLoader();
} else {
mClassLoader = mBaseClassLoader;
}
}
return mClassLoader;
}
}
ApplicationLoaders 는 getClassLoader 방법 을 사용 하여 들 어 오 는 zip 경로 에 따라 사실상 APK 의 경로 에 따라 로 더 를 만 들 고 PathClassLoader 를 되 돌려 줍 니 다.또한 PathClassLoader 는 설 치 된 APK 만 불 러 올 수 있 습 니 다.이 로 더 를 만 들 때 들 어 오 는 경 로 는 현재 APK 를 사용 하 는 경로 입 니 다.당연히 다른 APK 를 불 러 오 려 면 다른 APK 를 전달 하 는 클래스 로 더 를 만 듭 니 다.3.이 종류의 로 더 로 우리 가 시작 할 activity 를 불 러 오고 activity 인 스 턴 스 를 반사 적 으로 만 듭 니 다.
public Activity newActivity(ClassLoader cl, String className,Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
위의 사 고 를 정리 하면 우리 가 activity 를 시작 할 때 시스템 의 기본 PathClassLoader 를 통 해 이 activity 를 불 러 오 는 것 입 니 다.물론 기본 적 인 상황 에서 이 응용 프로그램의 activity 만 불 러 온 다음 에 시스템 에서 이 activity 의 생명 주기 로 호출 합 니 다.4.이곳 의 이상 은 뒤의 예시 에서 나타 날 수 있 습 니 다.그때 원인 을 분석 한 후에 우리 가 액 티 비 티 를 동적 으로 로드 하 는 방향 을 찾 을 수 있 습 니 다.
동적 로 딩 Activity:시스템 클래스 로 더 수정
이 사고방식 에 따라 이런 예 시 를 하고 단 추 를 누 르 면 플러그 인 에 있 는 Activity 를 엽 니 다.
플러그 인 항목
plugin.dl.pluginactivity
|--MainActivity.java
내용 은 간단 합 니 다.바로 하나의 레이아웃 위 에 플러그 인의 Activity 라 고 쓰 여 있 습 니 다!그의 onStart 와 onDestroy 방법 을 함께 썼 다.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// , R.layout.activity_main R.layout.activity_main
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Toast.makeText(this,"onStart", 0).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
Toast.makeText(this,"onDestroy", 0).show();
}
}
숙주 프로젝트
host.dl.hostactivity
|--MainActivity.java
두 개의 단 추 를 포함 하여 첫 번 째 단 추 는 플러그 인 에 있 는 MainActivity.java 로 이동 하고 두 번 째 단 추 는 이 응용 프로그램의 MainActivity.java 로 이동 합 니 다.
private Button btn;
private Button btn1;
DexClassLoader loader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
btn1 = (Button) findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Class activity = null;
String dexPath = "/PluginActivity.apk";
loader = new DexClassLoader(dexPath, MainActivity.this.getApplicationInfo().dataDir, null, getClass().getClassLoader());
try {
activity = loader.loadClass("plugin.dl.pluginactivity.MainActivity");
}catch (ClassNotFoundException e) {
Log.i("MainActivity", "ClassNotFoundException");
}
Intent intent = new Intent(MainActivity.this, activity);
MainActivity.this.startActivity(intent);
}
});
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
MainActivity.this.startActivity(intent);
}
});
우선 이 activity 를 숙주 프로젝트 의 액 인 Android Manifest 에 등록 해 야 합 니 다.단 추 를 누 르 면 플러그 인 에 있 는 activity 를 열 수 있 습 니 다.오류 가 발생 했 습 니 다.
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{host.dl.hostactivity/plugin.dl.pluginactivity.MainActivity}: java.lang.ClassNotFoundException: plugin.dl.pluginactivity.MainActivity
\#사용자 정의 로 더 를 사 용 했 습 니 다.startActivity 때 플러그 인 에 있 는 activity 를 찾 을 수 없 음 을 알려 주 는 이 유 는 무엇 입 니까?앞에서 네 번 째 시 에 이상 하 다 고 했 어 요.사실 이 이상 은 permlaunchActivity 에서 던 진 것 입 니 다.이 이상 인쇄 정 보 를 자세히 보 니 plugin.dl.pluginavity.MainActivity 류 를 찾 을 수 없다 고 했 습 니 다.그러나 우 리 는 방금 DexClassLoader 를 정 의 했 습 니 다.이 종 류 를 성공 적 으로 불 러 왔 습 니까?왜 여기에 또 이런 종 류 를 찾 을 수 없다 고 힌트 를 줍 니까?
실제로 그렇습니다.앞에서 말 했 듯 이 시스템 의 기본 클래스 캐리어 인 PathClassLoader 를 기억 하 십 니까?LoadedApk 대상 의 mClassLoader 변 수 는 null 이기 때문에 applicationLoaders\#getClassLoader 방법 으로 호출 합 니 다.즉,현재 응용 경로 에 따라 기본 PathClassLoader 를 되 돌려 줍 니 다.mPackages.get(aInfo.packageName)을 실행 할 때;Map 에서 가 져 온 LoadedApk 에 mClassLoader 가 지정 되 지 않 았 기 때문에 시스템 의 기본 클래스 로 더 를 사용 합 니 다.따라서 이 mInstrumentation.newActivity(cl,component.getClassName(),r.intent)를 실행 할 때;이 종류의 로 더 는 플러그 인 프로젝트 의 종 류 를 찾 을 수 없 기 때문에 잘못 보 고 했 습 니 다.
시스템 의 기본 적 인 이 종류의 로 더 를 사용 하 는 것 은 플러그 인 프로젝트 경 로 를 포함 하지 않 기 때문에 우리 가 원 하 는 activity 를 정확하게 불 러 올 수 없습니다.
그래서 시스템 의 클래스 로 더 를 교체 하 는 것 을 고려 합 니 다.
private void replaceClassLoader(DexClassLoader loader) {
try {
Class clazz_Ath = Class.forName("android.app.ActivityThread");
Class clazz_LApk = Class.forName("android.app.LoadedApk");
Object currentActivityThread = clazz_Ath.getMethod("currentActivityThread").invoke(null);
Field field1 = clazz_Ath.getDeclaredField("mPackages");
field1.setAccessible(true);
Map mPackages = (Map) field1.get(currentActivitead);
String packageName = MainActivity.this.getPackageName();
WeakReference ref = (WeakReference) mPackages.get(packageName);
Field field2 = clazz_LApk.getDeclaredField("mClassLoader");
field2.setAccessible(true);
field2.set(ref.get(), loader);
} catch (Exception e) {
e.printStackTrace();
}
}
이 코드 의 방향 은 Activity Thread 클래스 의 mPackages 변수 에 저 장 된 현재 패키지 이름 을 키 로 하 는 LoadedApk 값 의 mClassLoader 를 사용자 정의 클래스 로 바 꾸 는 것 입 니 다.다음 에 다른 곳 에 저 장 된 플러그 인 에 저 장 된 Activity 를 불 러 올 때 mPackages 변수 에서 직접 찾 을 수 있 기 때문에 우리 가 수정 한 클래스 로 더 를 사용 합 니 다.따라서 플러그 인 에 있 는 activity 를 열기 전에 replace ClassLoader(loader)를 호출 합 니 다.방법 은 시스템 의 클래스 로 더 를 교체 하면 됩 니 다.
효 과 는 다음 과 같다.
이 때 플러그 인 에 있 는 activity 를 시작 할 수 있 습 니 다.onStart 방법 을 실 행 했 고 닫 을 때 onDestroy 방법 을 실 행 했 기 때 문 입 니 다.그런데 이상 하 게 도 인터페이스 에 있 는 컨트롤 이 변 하지 않 은 것 같 습 니 다.그의 화면 을 시작 하 는 것 과 똑 같 아서 아직 클릭 할 수 없다.이게 무슨 이유 일 까요?
분명히 우 리 는 플러그 인 에 있 는 MainActivity 류 를 불 러 왔 을 뿐 입 니 다.그의 onCreate 방법 을 실 행 했 을 때 setContentView 에서 사용 하 는 레이아웃 매개 변 수 는 R.layot.activity 입 니 다.main,물론 사용 하 는 것 은 현재 응용 자원 입 니 다!
\#\#시스템 의 클래스 로 더 를 교 체 했 습 니 다.왜 이 응용 프로그램의 activity 를 불 러 오 면 정상적으로 작 동 합 니까?
그러나 이 문 제 를 수정 하기 전에 이상 한 현상 이 발견 되 었 습 니까?플러그 인 에 있 는 activity 를 불 러 온 후에 로 컬 activity 를 다시 시작 하 는 것 도 정상적으로 시작 할 수 있 습 니까?왜 그 럴 까요?앞 에 기본 클래스 로 더 가 바 뀌 었 습 니 다.플러그 인 에 있 는 activity 를 열 고 두 번 째 단 추 를 누 르 면 이 프로그램의 activity 를 열기 전에 사용 하 는 activity 를 볼 수 있 습 니 다.확실히 우리 가 바 꾼 클래스 로 더 입 니 다.그럼 여 기 는 왜 이 앱 의 activity 를 정상적으로 시작 할 수 있 습 니까?현 기 는 우리 가 DexClassLoader 를 만 들 때 네 번 째 인자 입 니 다.부모 로 더!부모 로 더 를 현재 클래스 의 로 더 로 설정 하면 부모 위임 모델 이 파괴 되 지 않도록 할 수 있 습 니 다.로 더 를 불 러 올 때 부모 로 더 를 불 러 옵 니 다.로 더 가 성공 하지 못 할 때 자신 이 불 러 옵 니 다.믿 지 않 으 면 new 라 는 로 더 를 실행 할 때 부모 로 더 의 매개 변 수 를 다른 값 으로 설정 할 수 있 습 니 다.예 를 들 어 시스템 클래스 로 더 를 실행 하면 activity 를 실행 할 때 오류 가 발생 할 수 있 습 니 다.
다음은 앞에서 나타 난 플러그 인 activity 로 넘 어가 면 화면 에 잘못된 문 제 를 표시 합 니 다.이 현상 이 나타 난 원인 은 이미 설명 되 었 습 니 다.로 컬 자원 을 사 용 했 기 때문에 set ContentView 에서 플러그 인 에 있 는 자원 레이아웃 을 사용 해 야 합 니 다.따라서 플러그 인 Activity 에서 다음 과 같이 수정 합 니 다.
public class MainActivity2 extends Activity {
private static View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// , R.layout.activity_main R.layout.activity_main
// setContentView(R.layout.activity_main);
if (view != null)
setContentView(view);
}
@Override
protected void onStart() {
super.onStart();
Toast.makeText(this,"onStart", 0).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
Toast.makeText(this,"onDestroy", 0).show();
}
private static void setLayout(View v){
view = v;
}
}
그리고 숙주 Activity 에서 플러그 인 자원 을 가 져 와 레이아웃 을 View 로 채 운 다음 플러그 인 에 있 는 activity 를 ContentView 의 내용 으로 설정 합 니 다.
Class<!--?--> layout = loader.loadClass("plugin.dl.pluginactivity.R$layout");
Field field = layout.getField("activity_main");
Integer obj = (Integer) field.get(null);
// APK Resources
//View view = LayoutInflater.from(MainActivity.this).inflate(resources.getLayout(obj),null);
// , getResources ,
View view = LayoutInflater.from(MainActivity.this).inflate(obj, null);
Method method = activity.getDeclaredMethod("setLayout", View.class);
method.setAccessible(true);
method.invoke(activity, view);
완전한 코드
public class MainActivity extends Activity {
private Resources resources;
protected AssetManager assetManager;
private Button btn;
private Button btn1;
DexClassLoader loader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
btn1 = (Button) findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String dexPath = "/PluginActivity.apk";
loader = new DexClassLoader(dexPath, MainActivity.this.getApplicationInfo().dataDir, null, getClass().getClassLoader());
Class<!--?--> activity = null;
Class<!--?--> layout = null;
try {
activity = loader.loadClass("plugin.dl.pluginactivity.MainActivity");
layout = loader.loadClass("plugin.dl.pluginactivity.R$layout");
}catch (ClassNotFoundException e) {
Log.i("MainActivity", "ClassNotFoundException");
}
replaceClassLoader(loader);
loadRes(dexPath);
try {
Field field = layout.getField("activity_main");
Integer obj = (Integer) field.get(null);
// APK Resources
View view = LayoutInflater.from(MainActivity.this).inflate(resources.getLayout(obj),null);
// , getResources ,
// View view = LayoutInflater.from(MainActivity.this).inflate(obj, null);
Method method = activity.getDeclaredMethod("setLayout", View.class);
method.setAccessible(true);
method.invoke(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent(MainActivity.this, activity);
MainActivity.this.startActivity(intent);
}
});
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
MainActivity.this.startActivity(intent);
}
});
}
public void loadRes(String path){
try {
assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, path);
} catch (Exception e) {
}
resources = new Resources(assetManager, super.getResources().getDisplayMetrics(), super.getResources().getConfiguration());
//
}
private void replaceClassLoader(DexClassLoader loader){
try {
Class clazz_Ath = Class.forName("android.app.ActivityThread");
Class clazz_LApk = Class.forName("android.app.LoadedApk");
Object currentActivityThread = clazz_Ath.getMethod("currentActivityThread").invoke(null);
Field field1 = clazz_Ath.getDeclaredField("mPackages");
field1.setAccessible(true);
Map mPackages = (Map)field1.get(currentActivityThread);
String packageName = MainActivity.this.getPackageName();
WeakReference ref = (WeakReference) mPackages.get(packageName);
Field field2 = clazz_LApk.getDeclaredField("mClassLoader");
field2.setAccessible(true);
field2.set(ref.get(), loader);
} catch (Exception e){
System.out.println("-------------------------------------" + "click");
e.printStackTrace();
}
}
@Override
public Resources getResources() {
return resources == null ? super.getResources() : resources;
}
@Override
public AssetManager getAssets() {
return assetManager == null ? super.getAssets() : assetManager;
}
}
동적 불 러 오기 Activity:프 록 시 사용
플러그 인 에 있 는 activity 를 시작 하 는 방법 도 있 습 니 다.플러그 인 에 있 는 activity 를 일반적인 클래스 로 생각 하고 구성 요소 activity 로 생각 하지 않 습 니 다.그래서 시작 할 때 프 록 시 Activity 를 시작 하 는 것 이 야 말로 진정한 Activity 입 니 다.그의 생명 주 기 는 시스템 에서 관리 되 고 우 리 는 플러그 인 Activity 의 함 수 를 호출 하면 됩 니 다.또한 플러그 인 Activity 에 프 록 시 Activity 의 인용 을 저장 하고 이 인용 을 컨 텍스트 환경 Context 로 이해 합 니 다.
여기 서 플러그 인 Activity 의 생명주기 함 수 는 모두 에이전트 Activity 에서 조정 합 니 다.Proxy Activity 는 사실은 우리 가 시작 하 는 Activity 입 니 다.플러그 인 에 있 는 Activity 를 시작 하 는 것 이 아니 라 플러그 인 에 있 는'시작 해 야 합 니 다'의 Activity 는 일반적인 클래스 로 생각 하고 일부 함 수 를 포함 하 는 일반 클래스 로 이해 합 니 다.다만 이 클래스 안의 함수 이름 이 좀 이상 하 게 지 어 졌 을 뿐이다.접근 자원 과 UI 업데이트 와 관련 된 경우 현재 컨 텍스트 환경,즉 저 장 된 proxy Activity 참조 로 가 져 옵 니 다.
다음 이 데 모 를 예 로 들 면...
숙주 프로젝트
com.dl.host
|--MainActivity.java
|--ProxyActivity.java
MainActivity 는 단 추 를 포함 하고 단 추 를 누 르 면 플러그 인 Activity 로 이동 합 니 다.
public class MainActivity extends Activity{
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.startActivity(new Intent(MainActivity.this, ProxyActivity.class));
}
});
}
}
Proxy Activity 는 우리 가 시작 할 플러그 인 Activity 의 꼭두각시,에이전트 입 니 다.시스템 유지보수 액 티 비 티 입 니 다.
public class ProxyActivity extends Activity{
private DexClassLoader loader;
private Activity activity;
private Class<!--?--> clazz = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loader = new DexClassLoader("/Plugin.apk", getApplicationInfo().dataDir, null, getClass().getClassLoader());
try {
clazz = loader.loadClass("com.dl.plugin.MainActivity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// activity
try {
Method setProxy = clazz.getDeclaredMethod("setProxy", Activity.class);
setProxy.setAccessible(true);
activity = (Activity)clazz.newInstance();
setProxy.invoke(activity, this);
Method onCreate = clazz.getDeclaredMethod("onCreate", Bundle.class);
onCreate.setAccessible(true);
onCreate.invoke(activity, savedInstanceState);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onStart() {
super.onStart();
// activity onStart
Method onStart = null;
try {
onStart = clazz.getDeclaredMethod("onStart");
onStart.setAccessible(true);
onStart.invoke(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onStart();
// activity onDestroy
Method onDestroy = null;
try {
onDestroy = clazz.getDeclaredMethod("onDestroy");
onDestroy.setAccessible(true);
onDestroy.invoke(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
이 를 통 해 알 수 있 듯 이 프 록 시 액 티 비 티 는 실제 액 티 비 티 입 니 다.플러그 인 에 있 는 액 티 비 티 가 아 닌 이 액 티 비 티 를 시작 합 니 다.플러그 인 항목
com.dl.plugin
|--MainActivity.java
프 록 시 Activity 의 인용 을 저 장 했 습 니 다.주의해 야 할 것 은 플러그 인 에 있 는 자원 에 접근 하려 면 추가 작업 이 필요 하고 자원 을 불 러 와 야 하기 때문에 플러그 인 항목 에 있 는 자원 을 사용 하지 않 습 니 다.그래서 저 는 코드 에 추 가 된 TextView 를 사용 하지만 원 리 는 앞에서 말 한 내용 과 같 습 니 다.
public class MainActivity extends Activity {
private Activity proxyActivity;
public void setProxy(Activity proxyActivity) {
this.proxyActivity = proxyActivity;
}
// activity
@Override
protected void onCreate(Bundle savedInstanceState) {
TextView tv = new TextView(proxyActivity);
tv.setText(" Activity");
proxyActivity.setContentView(tv,new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
@Override
protected void onStart() {
Toast.makeText(proxyActivity, " onStart", 0).show();
}
@Override
protected void onDestroy() {
Toast.makeText(proxyActivity, " onDestroy", 0).show();
}
}
이러한 방법 은 앞에서 시스템 로 더 를 수정 하 는 방법 에 비해 스스로 생명 주 기 를 유지 해 야 하고 번 거 로 우 며 앞의 방식 은 시스템 이 스스로 유지 하고 시작 하 는 것 이 바로 플러그 인 에 있 는 실제 Activity 입 니 다.이전 방식 은 숙주 의 AndroidManifest 에서 플러그 인 Activity 를 설명 해 야 합 니 다.이렇게 하면 activity 가 너무 많 을 때 많은 것 을 설명 하고 번 거 롭 지만 시스템 검 사 를 피 할 수도 있 습 니 다.뒤에 있 는 이 방식 은 프 록 시 Activity 클래스 하나만 있 으 면 됩 니 다.그의 onCreate 에서 전 달 된 값 에 따라 플러그 인 에 있 는 Activity 를 불 러 오 면 됩 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.