每天定时抢购飞天茅台
背景
-
上次预留两个问题
- 1. 新进程中的context 如何构建创造 - 2. hidden api的实现原理
问题一 新进程中的context 如何构建创造
-
反射构造客户端Service,构造使用context 原代码
-
public class UserService { @Nullable public static Pair<IBinder, String> create(String[] args) { Log.i(TAG, String.format("starting service %s/%s...", pkg, cls)); IBinder service; try { Context systemContext = ActivityThread.systemMain().getSystemContext(); DdmHandleAppName.setAppName(name != null ? name : pkg + ":user_service", 0); //noinspection InstantiationOfUtilityClass UserHandle userHandle = Refine.unsafeCast( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? UserHandleHidden.of(userId) : new UserHandleHidden(userId)); Context context = Refine.<ContextHidden>unsafeCast(systemContext).createPackageContextAsUser(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY, userHandle); ClassLoader classLoader = context.getClassLoader(); Class<?> serviceClass = classLoader.loadClass(cls); Constructor<?> constructorWithContext = null; try { constructorWithContext = serviceClass.getConstructor(Context.class); } catch (NoSuchMethodException | SecurityException ignored) { } if (constructorWithContext != null) { service = (IBinder) constructorWithContext.newInstance(context); } else { service = (IBinder) serviceClass.newInstance(); } } catch (Throwable tr) { Log.w(TAG, String.format("unable to start service %s/%s...", pkg, cls), tr); return null; } return new Pair<>(service, token); } }
-
-
Context systemContext = ActivityThread.systemMain().getSystemContext();
ActivityThread 作为隐藏API,为什么项目中可以使用到?-
自定义ActivityThread,包名类名和系统相同
-
package android.app; public class ActivityThread { public static ActivityThread systemMain() { throw new RuntimeException(); } public static ActivityThread currentActivityThread() { throw new RuntimeException(); } public Application getApplication() { throw new RuntimeException(); } public ContextImpl getSystemContext() { throw new RuntimeException(); } }
-
-
调用自定义的ActivityThread,这样运行不就抛出异常吗?为什么会顺利执行
-
双亲委派机制
-
private final ClassLoader parent; protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } return c; }
-
- findLoadedClass(name) 从缓存中获取Class对象,如果获取不到,就让parent.loadClass(name)找。
-
android 调用native方法从 class_table(哈希map)的缓存中查找
-
java中可能java层和native层都有缓存,Android只有native有缓存
-
- 递归此过程。直到顶层为BootClassLoader类型时,如果在缓存中无法获取到需要的class对象,就调用BootClassLoader.findClass(name)。
-
- 最后,如果parent获取不到class对象,就调用自己的findClass(name)寻找。
-
DexPathList -> dexElements这个数组就是一个Dex文件集合,而findClass就是遍历这个集合,然后从Dex文件中获取需要的字节码文件,然后使用字节码文件创建对应的Class对象,并将其缓存到class_table中。
-
- 如果findClass()方法仍然无法加载该类,那么ClassLoader会抛出ClassNotFoundException异常。
-
-
根据双亲委派机制,loadClass时返回BootClassCloder中已经加载的系统ActivityThread类,那我们自己实验一下,
-
apk类加载器的父类是否是BootClassCloder?
-
ClassLoader classLoader = MainActivity.class.getClassLoader(); while (classLoader != null) { Log.d("gzp", classLoader.getClass().getCanonicalName()); classLoader = classLoader.getParent(); }
-
-
自己替换一下系统类,demo验证一下,
-
package android.os; import android.util.Log; public class ServiceManager { public static IBinder getService(String name) { Log.d("gzp", "custom getService"); throw new RuntimeException("STUB"); } public static void addService(String name, IBinder service) { Log.d("gzp", "custom addService"); throw new RuntimeException("STUB"); } } public class MainActivity extends HomeActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); IBinder iBinder = ServiceManager.getService("activity"); Log.d("gzp", "custom" + iBinder); } }
果然没有输出custom getService
日志,并且返回的是一个对象
-
-
-
-
是不是所有的类都可以自定义替换呢
- 当然不是,如果替换View类,你会发现编译报错;android jar 包含的类替换有可能编译失败;导入类时,优先导入android jar的类
问题二 hidden api的实现原理
-
hidden api的实现原理
Context context = Refine.<ContextHidden>unsafeCast(systemContext).createPackageContextAsUser();
-
-
gradle 插件,在编译(compileDebugJavaWithJavac/compileRelaseJavaWithJavac)之后 将android.content.ContextHidden重新命名为 android.content.Context
-
class RefineRemapper extends Remapper { @Override public String map(final String typeName) { final ClassData data = context.loadClassData(typeName.replace('/', '.') + "$" + RefineProcessor.REFINE_METADATA_CLASS_NAME); if (data == null) { return typeName; } if (data.getClassAnnotations().contains(RefineProcessor.Descriptor.class.getName())) { final String to = data.getClassAnnotations().stream() .filter(a -> a.startsWith(RefineProcessor.REFINE_NS_PACKAGE)) .findFirst() .orElseThrow(() -> new UnsupportedOperationException("Use deprecated refine class " + data.getClassName())); return to.substring(RefineProcessor.REFINE_NS_PACKAGE.length() + 1).replace('.', '/'); } return typeName; } }
-
-
过程:
-
- 编译时调用自定义ContextHidden.createPackageContextAsUser 保证编译通过,不会因为类找不到或者方法找不到而抛出编译异常;
-
- 编译之后,重新命名(android.content.ContextHidden重新命名为 android.content.Context)
-
- 加载类时,根据双亲委派机制,findClass时,返回BootClassCloder中的系统android.content.Context
-
- 运行调用系统的android.content.Context
-
-