Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package %PACKAGE%;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;

import com.tencent.tinker.loader.app.ApplicationLike;
import com.tencent.tinker.loader.app.TinkerApplication;

/**
Expand All @@ -13,4 +18,32 @@ public class %APPLICATION% extends TinkerApplication {
super(%TINKER_FLAGS%, "%APPLICATION_LIFE_CYCLE%", "%TINKER_LOADER_CLASS%", %TINKER_LOAD_VERIFY_FLAG%, %TINKER_USE_DLC%, %TINKER_USE_INTERPRET_MODE_ON_SUPPORTED_32BIT_SYSTEM%);
}

@Override
public Resources getResources() {
final Resources resources = super.getResources();
final ApplicationLike applicationLike = getApplicationLike();
return (applicationLike == null) ? resources : applicationLike.getResources(resources);
}

@Override
public AssetManager getAssets() {
final AssetManager assets = super.getAssets();
final ApplicationLike applicationLike = getApplicationLike();
return (applicationLike == null) ? assets : applicationLike.getAssets(assets);
}

@Override
public Context getBaseContext() {
final Context base = super.getBaseContext();
final ApplicationLike applicationLike = getApplicationLike();
return (applicationLike == null) ? base : applicationLike.getBaseContext(base);
}

@Override
public ClassLoader getClassLoader() {
final ClassLoader classLoader = super.getClassLoader();
final ApplicationLike applicationLike = getApplicationLike();
return (applicationLike == null) ? classLoader : applicationLike.getClassLoader(classLoader);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.tencent.tinker.anno.test;

import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class AnnotationProcessorTest {

private static final String TEMPLATE_RESOURCE = "TinkerAnnoApplication.tmpl";

private static final String[] HOT_PATH_METHODS = {
"getResources",
"getAssets",
"getClassLoader"
};

private String readTemplate() throws IOException {
InputStream is = getClass().getClassLoader().getResourceAsStream(TEMPLATE_RESOURCE);
if (is == null) {
is = AnnotationProcessorTest.class.getResourceAsStream("/" + TEMPLATE_RESOURCE);
}
assertNotNull("Template " + TEMPLATE_RESOURCE + " must be on the test classpath", is);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int n;
while ((n = is.read(buf)) > 0) {
out.write(buf, 0, n);
}
return new String(out.toByteArray(), "UTF-8");
} finally {
is.close();
}
}

private String extractMethodBody(String src, String methodName) {
Pattern p = Pattern.compile("\\b" + Pattern.quote(methodName) + "\\s*\\([^)]*\\)\\s*\\{");
Matcher m = p.matcher(src);
if (!m.find()) {
return null;
}
int start = m.end() - 1;
int depth = 0;
for (int i = start; i < src.length(); i++) {
char c = src.charAt(i);
if (c == '{') {
depth++;
} else if (c == '}') {
depth--;
if (depth == 0) {
return src.substring(start, i + 1);
}
}
}
return null;
}

@Test
public void hotPathMethods_useDirectDelegation_notReflection() throws IOException {
String template = readTemplate();
for (String method : HOT_PATH_METHODS) {
String body = extractMethodBody(template, method);
assertNotNull("Template should override " + method + "()", body);
assertFalse(
method + "() must call ApplicationLike directly, not via Method.invoke",
body.contains(".invoke(")
);
assertTrue(
method + "() should delegate to the ApplicationLike instance",
body.contains(method + "(")
);
}
}

@Test
public void template_doesNotRetainReflectionCachesForHotPath() throws IOException {
String template = readTemplate();
for (String method : HOT_PATH_METHODS) {
String cacheField = "mCurrentMethod_" + method;
assertFalse(
"Reflection Method cache field for hot path method " + method
+ " should be removed from the template",
template.contains(cacheField)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.lib.app;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;

public abstract class ApplicationLike implements ApplicationLifeCycle {
private final Application application;
private final int tinkerFlags;
private final boolean tinkerLoadVerifyFlag;
private final long applicationStartElapsedTime;
private final long applicationStartMillisTime;
private Intent tinkerResultIntent;

public ApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
long applicationStartElapsedTime, long applicationStartMillisTime,
Intent tinkerResultIntent) {
this.application = application;
this.tinkerFlags = tinkerFlags;
this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;
this.applicationStartElapsedTime = applicationStartElapsedTime;
this.applicationStartMillisTime = applicationStartMillisTime;
this.tinkerResultIntent = tinkerResultIntent;
}

public final Application getApplication() {
return application;
}

public final int getTinkerFlags() {
return tinkerFlags;
}

public final boolean getTinkerLoadVerifyFlag() {
return tinkerLoadVerifyFlag;
}

public final long getApplicationStartElapsedTime() {
return applicationStartElapsedTime;
}

public final long getApplicationStartMillisTime() {
return applicationStartMillisTime;
}

public final Intent getTinkerResultIntent() {
return tinkerResultIntent;
}

public Resources getResources(Resources resources) {
return resources;
}

public AssetManager getAssets(AssetManager assets) {
return assets;
}

public Context getBaseContext(Context base) {
return base;
}

public ClassLoader getClassLoader(ClassLoader cl) {
return cl;
}

@Override
public void onCreate() {
}

@Override
public void onTerminate() {
}

@Override
public void onLowMemory() {
}

@Override
public void onTrimMemory(int level) {
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
}

@Override
public void onBaseContextAttached(Context base) {
}
}