前言:如果程序想要知道有activity启动,如果想要拦截activity,然后跳转到指定的activity怎么办?

我们看下ActivityThread 里面:

   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
} ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
} if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
} ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
} ....
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback); if (customIntent != null) {
activity.mIntent = customIntent;
}

可以看到,执行启动activity的时候,

activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());

那么我们是不是可以在这个时候拦截一下返回的activity呢?

OK,我们继承Instrumentation,并且重写里面的方法。

package com.****r.app;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent; import com.*****.ActivityAbout; /**
* =======================================================================================
* 作 者:caoxinyu
* 创建日期:2019/2/19.
* 类的作用:
* 修订历史:
* =======================================================================================
*/
public class MyInstrumentation extends Instrumentation {
private Instrumentation base; public MyInstrumentation(Instrumentation base) {
this.base = base;
} @Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
//这里需要setExtrasClassLoader 不然的话,getParecleable 对象可能会拿不到
//很多hook Instrumentation的人都不知道。
// 这里try catch 是防止恶意攻击 导致android.os.BadParcelableException: ClassNotFoundException when unmarshalling
intent.setExtrasClassLoader(cl);
intent.getBooleanExtra("a",false);
}catch (Exception e){ }
if (intent.getBooleanExtra("ActivityAbout",false)) {
return super.newActivity(cl, ActivityAbout.class.getName(), intent);
}
return super.newActivity(cl,className, intent);
} }

那么怎么使我们重写的类生效呢?

package com.***;

import android.app.Instrumentation;

import java.lang.reflect.Field;
import java.lang.reflect.Method; public class Hooker {
private static final String TAG = "Hooker"; public static void hookInstrumentation() {
Class<?> activityThread = null;
try {
activityThread = Class.forName("android.app.ActivityThread");
Method sCurrentActivityThread = activityThread.getDeclaredMethod("currentActivityThread");
sCurrentActivityThread.setAccessible(true);
//获取ActivityThread 对象
Object activityThreadObject = sCurrentActivityThread.invoke(activityThread); //获取 Instrumentation 对象
Field mInstrumentation = activityThread.getDeclaredField("mInstrumentation");
mInstrumentation.setAccessible(true);
Instrumentation instrumentation = (Instrumentation) mInstrumentation.get(activityThreadObject);
MyInstrumentation customInstrumentation = new MyInstrumentation(instrumentation);
//将我们的 customInstrumentation 设置进去
mInstrumentation.set(activityThreadObject, customInstrumentation);
} catch (Exception e) {
e.printStackTrace();
}
}
}

上面这些代码是通过反射,把自己的Instrumentation 设置进去。

然后在程序初始化的时候,调用下面的代码即可。

		Hooker.hookInstrumentation();

我们启动一个A activity,如果intent.getBooleanExtra(“ActivityAbout”,false),那么你的A activity 将被拦截成ActivityAbout。

那么还有一个问题,为什么要设置ClassLoader?

 intent.setExtrasClassLoader(cl);

因为如果不设置的话,getParecleable 对象可能会拿不到。在8.0以前的手机,直接崩溃:android.os.BadParcelableException: ClassNotFoundException when unmarshalling。在8.0以上的话,系统会catch 住这个崩溃,但是你的数据全都会被清空。

具体分析如下:

简单try catch,在低版本上没有问题。但是在android8.0以上,会有问题。

在Android8.0以上,如果getBooleanExt 方法里面失败了,系统会catch BadParcelableException,并把intent 里面的数据清空。具体可见下面的截图,



这就导致简单try catch 之后的代码,运行在8.0以上手机,收不到intent里面的数据,因为Intent 里面的跳转数据被清空了。

还是要查清楚为什么会出现ClassNotFoundException when unmarshalling

根据源码,在这里getBooleanExt 会出问题是因为系统在这一步还没有设置解析Parcelable 的classLoader。如下图



所以,有问题的代码需要这样改下。

系统是在调用了 mInstrumentation.newActivity之后设置了classLoader r.intent.setExtrasClassLoader(cl), 所以hook 在newActivity 这一步get Parcelable 数据是有问题的。

不然会有下面这种错误:

java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.aaa./.WelcomeActivity}: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.nearme.mcs.entity.MessageEntity
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2492)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2678)
at android.app.ActivityThread.access$900(ActivityThread.java:187)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1523)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:210)
at android.app.ActivityThread.main(ActivityThread.java:5809)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1113)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:879)
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.nearme.mcs.entity.MessageEntity
at android.os.Parcel.readParcelableCreator(Parcel.java:2305)
at android.os.Parcel.readParcelable(Parcel.java:2255)
at android.os.Parcel.readValue(Parcel.java:2162)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2495)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.BaseBundle.getBoolean(BaseBundle.java:658)
at android.content.Intent.getBooleanExtra(Intent.java:5129)
at com.nearme.game.sdk.y.o_a(SourceFile:46)
at com.nearme.game.sdk.y.newActivity(SourceFile:28)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2469)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2678)
at android.app.ActivityThread.access$900(ActivityThread.java:187)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1523)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:210)
at android.app.ActivityThread.main(ActivityThread.java:5809)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1113)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:879)

加油,自己学会了使用Source Insight 看源码。开始慢慢的去学习技术的原理。加油!

androd hook acitivity 启动流程,替换启动的activity(Android Instrumentation)的更多相关文章

  1. iOS应用程序工程文件以及启动流程

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51351188 本文出自:[openXu的博客] iOS程序启动流程 完整启动流程 UIApp ...

  2. Activity的启动流程分析

    Activity是Android应用程序的四大组件之中的一个,负责管理Android应用程序的用户界面,一般一个应用程序中包括非常多个Activity,他们可能执行在一个进程中.也可能执行在不同的进程 ...

  3. Flask应用启动流程

    目录 flask应用启动流程 WSGI 启动流程 flask应用启动流程 WSGI 所有的 python web 框架都要遵循 WSGI 协议 在这里还是要简单回顾一下 WSGI 的核心概念. WSG ...

  4. 【转】linux-系统启动流程详解

    第二十章.启动流程.模块管理与 Loader 最近升级日期:2009/09/14 1. Linux 的启动流程分析 1.1 启动流程一览 1.2 BIOS, boot loader 与 kernel ...

  5. lk启动流程详细分析

    转载请注明来源:cuixiaolei的技术博客 这篇文章是lk启动流程分析(以高通为例),将会详细介绍下面的内容: 1).正常开机引导流程 2).recovery引导流程 3).fastboot引导流 ...

  6. centOS 6启动流程

      centOS6启动流程 centOS6启动流程 linux内核组成 centos6启动大致流程 1.post加电自检 2.Boot Sequence 3.MBR引导 4.Grub启动 制作init ...

  7. centOS7服务管理与启动流程

    centOS7服务管理与启动流程 centOS7启动流程 systemd简介 unit对象 unit类型 特性 service unit文件格式 service unit file文件通常由三部分组成 ...

  8. 高通msm8994启动流程简介

    处理器信息 8994包含如下子系统: 子系统 处理器 含义 APSS 4*Cortex-A53 应用子系统 APSS 4*Cortex-A57 应用子系统 LPASS QDSP6 v5.5A(Hexa ...

  9. Tiny4412 Linux 内核启动流程

    Linux内核的启动分为压缩内核和非压缩内核两种,这里我们以压缩内核为例.压缩内核运行时,将运行一段解压缩程序,得到真正的内核镜像,然后跳转到内核镜像运行.此时,Linux进入非压缩内核入口,在非压缩 ...

随机推荐

  1. WebSphere Studio Application Developer 5.0 优化设置

    公司有一个项目需要用到WebSphere Studio Application Developer 5.0 的开发环境,这个环境比较老,而且只能用JDK1.4. 项目开发的时候 总是报错: JVM t ...

  2. Springmvc 进行数据类型转换

    SpringMVC进行一些常用的数据类型转换,这里以Date 数据类型的转换为例. SpringMVC表单中输入日期,一般都是以字符串的形式输入,如何将字符形式的日期转换为Date 类型的呢?这里只需 ...

  3. Django 查询集的过滤内置条件

    条件选取querySet的时候,filter表示=,exclude表示!=.querySet.distinct() 去重复__exact 精确等于 like 'aaa' __iexact 精确等于 忽 ...

  4. 【模板】Splay总结

    rentenglong大佬写的splay的梳理使我受益颇丰,特此做出一定的总结. 数据结构 定义了一个struct结构体. 为了在splay操作下储存根节点,我们宏定义了root 为 tree[0]. ...

  5. luogu P4231 三步必杀

    嘟嘟嘟 这道题就是区间加一个等差数列,然后最后求每一个数的值. O(n)做法:二阶差分. 其实就是差分两遍.举个例子 0 0 0 0 0 0 0,变成了 0 2 4 6 8 0 0.第一遍差分:0 2 ...

  6. [19/03/28-星期四] IO技术_基本概念&字符编码与解码

    一.概念 输入(Input)  指的是:可以让程序从外部系统获得数据(核心含义是“读”,读取外部数据) 常见的应用: Ø 读取硬盘上的文件内容到程序.例如:播放器打开一个视频文件.word打开一个do ...

  7. C++中rapidxml用法及例子(源码)

    rapidxml是一个快速的xml库,比tinyxml快了50-100倍.本文给出创建.读取.写入xml的源码.      由于新浪博客不支持文本文件上传,在使用下面代码需要先下载 rapidxml, ...

  8. torch.backend.cudnn.benchmark

    大部分情况下,设置这个 flag 可以让内置的 cuDNN 的 auto-tuner 自动寻找最适合当前配置的高效算法,来达到优化运行效率的问题. 一般来讲,应该遵循以下准则: 如果网络的输入数据维度 ...

  9. 清除IE8/IE9/IE10/IE11浏览器缓存文件 100%有效

    不管你是哪个版本的IE浏览器,按照下面指示操作,都能清除掉你使用浑身解数也清不掉的缓存文件! 第一步,打开IE浏览器——工具——Internet选项 有的IE浏览器的Internet选项藏在右上角一个 ...

  10. Office365学习笔记—列表查询,删除条目,更新条目。

    1,基于Query语句的列表查询. function retrieveListItems(itemId) { var siteUrl=_spPageContextInfo.webServerRelat ...