Android 동적 교체 애플 리 케 이 션 구현
8212 단어 AndroidApplication
핫 업데이트 와 훅 기술 이 모두 엉망 이 되 었 지만 이 방면 의 내용 을 여러분 과 이야기 하고 싶 습 니 다.최근 에 안 드 로 이 드 분야 의 최적화 작업 을 하고 있 습 니 다.안 드 로 이 드 의 ClassLoader 는 dex 파일 을 불 러 오 는 과정 에서 안 드 로 이 드 Manifest 의 Application 류 는 dex 파일 에 있 습 니 다.Application 은 보통 전체적인 초기 화 작업 을 합 니 다.dex 를 불 러 오기 전에 기 존의 Application 을 ProxyApplication 으로 교체 해 야 합 니 다.응용 프로그램 을 시작 할 때 ProxyApplication 을 불 러 온 다음 dex 를 불 러 오 는 등 일부 프로 세 스 를 처리 합 니 다.그 다음 에 기 존의 Application(이하 RealApplication)을 교체 하여 응용 이 정상적으로 작 동 하도록 확보 하고 생명 주기,초기 화 순서 가 변 하지 않 으 며 응용 중의 getContext,getapplicationContext 에 미 치 는 영향 을 차단 해 야 합 니 다.
응용 프로그램 을 교체 하 는 과정 에서 다음 과 같은 몇 가 지 를 주의해 야 한다.
AndroidManifest.xml 파일 에서 Application 을 ProxyApplication 으로 바 꾸 면 자동화 방식 이나 포장 방식 을 사용 할 수 있 으 며 실현 에 대한 구체 적 인 세부 사항 은 논의 하지 않 습 니 다.여 기 는 주로 RealApplication 을 만 드 는 과정 을 서술 합 니 다.프 록 시 애플 리 케 이 션 을 교체 한 후에 시스템 에 있어 프 록 시 애플 리 케 이 션 은 초기 화 된 입구 이 고 모든 리 셋 은 프 록 시 애플 리 케 이 션 에서 발생 합 니 다.우 리 는 주로 attachBaseContext 와 onCreate 의 리 셋 에 관심 을 가진다.
RealApplication 만 들 기
RealApplication 을 만 듭 니 다.저 희 는 반사 적 인 방식 으로 new Instance 로 대상 을 만 든 다음 에 attachBaseContext 를 실행 할 수 있 습 니 다.그러나 서로 다른 시스템 버 전에 대해 내부 에서 실 행 된 세부 사항 이 다 르 거나 다른 관련 논리 적 인 처리 가 있 을 수 있 기 때문에 우 리 는 다른 방식 으로 처리 합 니 다.먼저 시스템 소스 코드 가 어떻게 실현 되 는 지 살 펴 보고 여 기 는 8.0.0 의 시스템 소스 코드 를 선택 하여 분석 하고 다른 버 전 은http://androidxref.com/조회 합 니 다.
안 드 로 이 드 초기 화 는 안 드 로 이 드.app.activity Thread 에서 시작 되 었 다 는 것 을 알 고 있 습 니 다.그래서 Activity Thread 부터 보 세 요.Activity Thread 에 정적 방법 이 존재 합 니 다.current Activity Thread 는 인 스 턴 스 를 되 돌려 줍 니 다.시스템 의 Activity Thread 클래스 를 참고 할 수 있 습 니 다.
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
Activity Thread 내부 에 구성원 변수 AppBindData mBoundApplication 이 존재 합 니 다.AppBindData 는 구성원 변수 LoadedApk info 를 포함 하 는 정적 내부 클래스 입 니 다.android.app.LoadedApk 소스 코드 를 보고 응용 프로그램 을 만 드 는 makeApplication 방법 을 발견 합 니 다.
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
// Rewrite the R 'constants' for all library apks.
SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
.getAssignedPackageIdentifiers();
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
if (id == 0x01 || id == 0x7f) {
continue;
}
rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
}
위의 코드 를 통 해 캐 시 mApplication 이 비어 있 지 않 으 면 바로 돌아 오 는 것 을 발견 할 수 있 습 니 다.mApplication 이 비어 있 을 때 RealApplication 을 만 들 고 관련 리 셋 을 실행 합 니 다.RealApplication 을 만 들 때 클래스 이름 은 mApplication Info.className 에서 가 져 옵 니 다.mActivity Thread.malApplications 에 RealApplication 을 새로 만 듭 니 다.캐 시 mApplication 에 값 을 부여 합 니 다.따라서 MakeApplication 을 호출 하기 전에 mApplication 을 null 로 설정 해 야 합 니 다.그렇지 않 으 면 ProxyApplication 의 인 스 턴 스 를 직접 되 돌려 줍 니 다.
우선 android.app.activity Thread 의 정적 방법 으로 Activity Thread 인 스 턴 스 를 얻 은 다음 Activity Thread 인 스 턴 스 를 통 해 LoadedApk 인 스 턴 스 를 얻 습 니 다.MakeApplication 이 순조롭게 실 행 될 수 있 도록 mApplication 을 null 로 설정 합 니 다.mAllApplications 에서 ProxyApplication 의 인 스 턴 스 를 제거 합 니 다.LoadedApk 에서 mApplicationInfo 와 AppBindData 에서 appInfo 는 모두 ApplicationInfo 형식 으로 className 필드 의 값 을 RealApplication 의 실제 클래스 전체 이름 으로 각각 교체 해 야 합 니 다.
이후 반사 호출 시스템 의 MakeApplication.
이렇게 하면 Proxyapplication.attachBaseContext 에서 MakeApplication 을 호출 하여 RealApplication 을 만 들 고 내부 적 으로 RealApplication 에 대한 attchBaseContext 의 리 셋 이 완료 되 었 습 니 다.Proxyapplication.onCreate 에서 RealApplication 인 스 턴 스 의 onCreate 만 바 꾸 면 RealApplication 생 성,내부 교체 및 정상 적 인 수명 주기 에 대한 리 셋 을 완성 할 수 있 습 니 다.또한 Activity 에서 getApplication Context 를 호출 하여 되 돌려 주 는 값 은 실제 LoadedApk 에서 mApplication 의 값 이자 Activity 등 부분 에서 ProxyApplication 을 차단 하 는 목적 도 보장 합 니 다.
ContentProvider 에서 getContext
응용 프로그램 과 ContentProvider 의 초기 화 순 서 는:응용 프로그램.attachBaseContext->ContentProvider.onCreate->응용 프로그램.onCreate 입 니 다.ContentProvider 에 도 getContext 방법 이 존재 합 니 다.ContentProvider 의 소스 코드 구현 을 보십시오.
그 중에서 mContext 가 할당 되 는 곳 은 두 군데 가 있 는데 하 나 는 구조 방법 이 고 하 나 는 attchInfo 일 때 입 니 다.원본 코드 에서 구조 방법 을 사용 하여 초기 화 하거나 attachInfo 를 호출 한 곳 을 계속 추적 한 결과 android.app.activity Thread 에서 installProvider 방법 을 찾 으 면 호출 관계 가 존재 합 니 다.
이 를 통 해 알 수 있 듯 이 반사 호출 ContentProvider 무 참 구조 방법 으로 인 스 턴 스 를 만 든 다음 에 attachInfo 를 호출 하여 전달 하 는 Context 는 installProvider 방법 중의 매개 변수 이 고 installProvider 의 매개 변 수 는 installContentProviders 내부 에서 초기 화 에서 전 달 된 것 입 니 다.
installContentProviders 에서 installProvider 를 호출 할 때 전달 하 는 Context 도 방법 이 호출 될 때 전달 하 는 매개 변수 임 을 명확 하 게 할 수 있다.계속 위로 추적 해 보 니 Activity Thread.handleBindApplication 이 ContentProvider 를 초기 화 할 때 installContentProviders 를 호출 했 습 니 다.마지막 으로 attach Info 를 통 해 ContentProvider 에 설 치 된 Context 의 실제 유형 은 Application 입 니 다.
앱 이 초기 화 되 었 을 때 시스템 이 MakeApplication 을 호출 하여 ProxyApplication 인 스 턴 스 를 만 들 었 고 attachBaseContext(Context context)를 되 돌 렸 습 니 다.그래서 이 방법 은 앱 초기 화 시 프 록 시 애플 리 케 이 션 을 되 돌려 줍 니 다.프 록 시 애플 리 케 이 션.attachBaseContext 가 발생 한 후 프 록 시 애플 리 케 이 션.onCreate 이전 을 호출 합 니 다.그래서 우 리 는 이 두 가지 방법 으로 수명 주기 내 에 RealApplication 으로 바 꿀 방법 이 없다.
이런 방안 은 접속 원가 가 비교적 낮 지만 새로운 시스템 이 발생 한 후에 호환성 문제 가 발생 할 수 있 으 므 로 매번 새로운 시스템 을 발표 한 후에 관련 된 적절 한 배 치 를 해 야 한다.하지만 이런 훅 이 문 제 를 해결 하 는 방향 은 참고 할 수 있다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.