/* * Tencent is pleased to support the open source community by making Tinker available. * * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package com.tencent.tinker.loader.app; import android.annotation.TargetApi; import android.app.Application; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; import android.os.SystemClock; import com.tencent.tinker.loader.TinkerLoader; import com.tencent.tinker.loader.TinkerRuntimeException; import com.tencent.tinker.loader.TinkerUncaughtHandler; import com.tencent.tinker.loader.shareutil.ShareConstants; import com.tencent.tinker.loader.shareutil.ShareIntentUtil; import com.tencent.tinker.loader.shareutil.ShareTinkerInternals; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * Created by zhangshaowen on 16/3/8. * <p/> * A base class for implementing an Application that delegates to an {@link ApplicationLifeCycle} * instance. This is used in conjunction with secondary dex files so that the logic that would * normally live in the Application class is loaded after the secondary dexes are loaded. */ public abstract class TinkerApplication extends Application { //oh, we can use ShareConstants, because they are loader class and static final! private static final int TINKER_DISABLE = ShareConstants.TINKER_DISABLE; private static final String INTENT_PATCH_EXCEPTION = ShareIntentUtil.INTENT_PATCH_EXCEPTION; private static final String TINKER_LOADER_METHOD = "tryLoad"; /** * tinkerFlags, which types is supported * dex only, library only, all support * default: TINKER_ENABLE_ALL */ private final int tinkerFlags; /** * whether verify md5 when we load dex or lib * they store at data/data/package, and we had verity them at the :patch process. * so we don't have to verity them every time for quicker! * default:false */ private final boolean tinkerLoadVerifyFlag; private final String delegateClassName; private final String loaderClassName; /** * if we have load patch, we should use safe mode */ private boolean useSafeMode; private Intent tinkerResultIntent; private ApplicationLike applicationLike = null; private long applicationStartElapsedTime; private long applicationStartMillisTime; /** * current build. */ protected TinkerApplication(int tinkerFlags) { this(tinkerFlags, "com.tencent.tinker.loader.app.DefaultApplicationLike", TinkerLoader.class.getName(), false); } /** * @param delegateClassName The fully-qualified name of the {@link ApplicationLifeCycle} class * that will act as the delegate for application lifecycle callbacks. */ protected TinkerApplication(int tinkerFlags, String delegateClassName, String loaderClassName, boolean tinkerLoadVerifyFlag) { this.tinkerFlags = tinkerFlags; this.delegateClassName = delegateClassName; this.loaderClassName = loaderClassName; this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag; } protected TinkerApplication(int tinkerFlags, String delegateClassName) { this(tinkerFlags, delegateClassName, TinkerLoader.class.getName(), false); } private ApplicationLike createDelegate() { try { // Use reflection to create the delegate so it doesn't need to go into the primary dex. // And we can also patch it Class<?> delegateClass = Class.forName(delegateClassName, false, getClassLoader()); Constructor<?> constructor = delegateClass.getConstructor(Application.class, int.class, boolean.class, long.class, long.class, Intent.class); return (ApplicationLike) constructor.newInstance(this, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent); } catch (Throwable e) { throw new TinkerRuntimeException("createDelegate failed", e); } } private synchronized void ensureDelegate() { if (applicationLike == null) { applicationLike = createDelegate(); } } /** * Hook for sub-classes to run logic after the {@link Application#attachBaseContext} has been * called but before the delegate is created. Implementors should be very careful what they do * here since {@link android.app.Application#onCreate} will not have yet been called. */ private void onBaseContextAttached(Context base) { applicationStartElapsedTime = SystemClock.elapsedRealtime(); applicationStartMillisTime = System.currentTimeMillis(); loadTinker(); ensureDelegate(); applicationLike.onBaseContextAttached(base); //reset save mode if (useSafeMode) { String processName = ShareTinkerInternals.getProcessName(this); String preferName = ShareConstants.TINKER_OWN_PREFERENCE_CONFIG + processName; SharedPreferences sp = getSharedPreferences(preferName, Context.MODE_PRIVATE); sp.edit().putInt(ShareConstants.TINKER_SAFE_MODE_COUNT, 0).commit(); } } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); Thread.setDefaultUncaughtExceptionHandler(new TinkerUncaughtHandler(this)); onBaseContextAttached(base); } private void loadTinker() { //disable tinker, not need to install if (tinkerFlags == TINKER_DISABLE) { return; } tinkerResultIntent = new Intent(); try { //reflect tinker loader, because loaderClass may be define by user! Class<?> tinkerLoadClass = Class.forName(loaderClassName, false, getClassLoader()); Method loadMethod = tinkerLoadClass.getMethod(TINKER_LOADER_METHOD, TinkerApplication.class); Constructor<?> constructor = tinkerLoadClass.getConstructor(); tinkerResultIntent = (Intent) loadMethod.invoke(constructor.newInstance(), this); } catch (Throwable e) { //has exception, put exception error code ShareIntentUtil.setIntentReturnCode(tinkerResultIntent, ShareConstants.ERROR_LOAD_PATCH_UNKNOWN_EXCEPTION); tinkerResultIntent.putExtra(INTENT_PATCH_EXCEPTION, e); } } @Override public void onCreate() { super.onCreate(); ensureDelegate(); applicationLike.onCreate(); } @Override public void onTerminate() { super.onTerminate(); if (applicationLike != null) { applicationLike.onTerminate(); } } @Override public void onLowMemory() { super.onLowMemory(); if (applicationLike != null) { applicationLike.onLowMemory(); } } @TargetApi(14) @Override public void onTrimMemory(int level) { super.onTrimMemory(level); if (applicationLike != null) { applicationLike.onTrimMemory(level); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (applicationLike != null) { applicationLike.onConfigurationChanged(newConfig); } } @Override public Resources getResources() { Resources resources = super.getResources(); if (applicationLike != null) { return applicationLike.getResources(resources); } return resources; } @Override public ClassLoader getClassLoader() { ClassLoader classLoader = super.getClassLoader(); if (applicationLike != null) { return applicationLike.getClassLoader(classLoader); } return classLoader; } @Override public AssetManager getAssets() { AssetManager assetManager = super.getAssets(); if (applicationLike != null) { return applicationLike.getAssets(assetManager); } return assetManager; } @Override public Object getSystemService(String name) { Object service = super.getSystemService(name); if (applicationLike != null) { return applicationLike.getSystemService(name, service); } return service; } @Override public Context getBaseContext() { Context base = super.getBaseContext(); if (applicationLike != null) { return applicationLike.getBaseContext(base); } return base; } public void setUseSafeMode(boolean useSafeMode) { this.useSafeMode = useSafeMode; } public boolean isTinkerLoadVerifyFlag() { return tinkerLoadVerifyFlag; } public int getTinkerFlags() { return tinkerFlags; } }