前言:如果程序想要知道有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. JavaScript的DOM_获取和操作层次节点

    一.层次节点的概述 节点的层次结构可以划分为:父节点与子节点.兄弟节点这两种.当我们获取其中一个元素节点的时候,就可以使用层次节点属性来获取它相关层次的节点. 二.childNodes 属性 chil ...

  2. vue.js加入购物车小球动画

    生成一个动画小球的div,并且生成五个小球,五个是为了生成一定数量的小球来作为操作使用,按照小球动画的速度,一般来说五个也可以保证有足够的小球数量来运行动画 动画的内容分别是外层和内层,外层控制动画小 ...

  3. Linux关于scp命令

    声明:本文主要转自https://www.2cto.com/os/201503/379474.html scp主要应用场景如下: (1)必要时,每个季度或者每月将数据由这台服务器传输到另外一台,不过前 ...

  4. CUDA与OpenGL互操作实例

    本文要解决的问题是如何实现CUDA和OpenGL的互操作,使得GPU能够将通用计算的运算结果交给OpenGL进行绘制. 本文的应用程序主要包括两个方面: 1.      使用CUDA核函数生成图像数据 ...

  5. layUI不同页面传参数

    //页面一的方法 function modifyHotSearch(id){ layer.open({ type:2, title:"修改热门搜索", area: ['600px' ...

  6. The Linux Kernel

  7. Spring MVC 框架

    一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于SpringMVC的配置 <!--conf ...

  8. 5.同步关键字(synchronized)

    同步关键字(synchronized): 多线程给我们提供方便的时候,也给整个编程增加了难度,尤其是对临界资源的控制,尤为重要. 一个在操作系统课上,老掉牙的事例,就把这种情况解释的明明白白. 一对夫 ...

  9. PX4地面站QGroundControl在ubuntu下的安装

    1.引言 相信很多玩开源无人机的朋友手上都有一架无人机,而不是仅仅停留在理论的学习和程序的学习.放飞自己组装的无人机才是乐趣所在,那么这本文就介绍玩无人机必不可少的地面站软件qgroundcontro ...

  10. oracle在线迁移同步数据,数据库报错

    报需要升级的错误,具体处理步骤如下: 一.错误信息 SQL> alter database open ;alter database open resetlogs*ERROR at line 1 ...