不需要在清单文件中注册的Acrivity的启动方式--Hook技术

来源:互联网 发布:鹏业软件视频教程 编辑:程序博客网 时间:2024/05/22 17:17

最近发现自己的技术很欠缺所以看了一些真正大神级别的开发者的直播课比如什么华为的定制开发项目经理啊、什么担任过腾讯的什么经理啊等等的我认为的大神(不知道别人怎么认为啊)


好了废话不多说,开始说说今天的主题 ====》怎么能启动一个不在清单文件中注册的Actriviry


可能大家看到之后会很质疑,从自己Android开发到现在都将Activity在清单文件中才启动负责,否则会报android.content.ActivityNotFoundException异常,这个相信每一个android开发都遇到过,因为每次写一个Activity之后,啪啪啪一顿敲之后,想看看效果,结果。。。。。


好了,可能大家感觉那就在清单文件中注册一下呗,对,就注册一下就好了,但是可能大家没有遇到过插件开发,(我也没遇到过插件开发,听老师说的) 有些时候不知道自己下载的插件里面有哪些Actiivity,就算知道一个两个,有时候也不会全部都知道(比如:你知道插件的Activity  A但是A要启动Activity B) 所以这就会导致程序崩溃。。。。好了  废话太多了,整干货了啊


要实现这个功能需要用到的就是Hook技术,先跟大家说一下这个Activity的启动流程吧,(上源码)startIntent(...)之后你你会看到这里


然后这个mMainThread才是真正启动的类(截图太麻烦直接口述了啊  大家可以去看看源码)

要启动一个Activity是在ActivityThread的类的成员变量mH的handleMessage方法中调用handleLaunchActivity来完成的,(这是系统的),这里我们是没办法搞什么动作的。但是并不是只有这一步可以启动Activity了,启动Activity的流程中涉及到很多的过程,我们可以在别的地方拿到关键的东西偷天换日,把这个方法的参数修改掉,给系统传递我们自己封装好的带有目标Activity的Intent,让系统去执行,这样就可以实现我们的目的了。

还得看源码,大家如果进入源码ActivityThread类之后可以看到一个mH的H(其实是一个Handler),里面有一堆静态常量不用看,看那个handleMessage的一个caseLAUNCH_ACTIVITY  这里就是启动Activity的关键,我们就在这里搞动作,好了就说到这吧上代码了。

package hookutils;import android.content.Context;import android.content.Intent;import android.os.Handler;import android.os.Message;import android.util.Log;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 宋鑫 */public class HookUtil {    private static final String TAG = "hook";    private Class<?> proxyActivity;    private Context context;    private Object activityThreadValue;    public HookUtil(Context context, Class<?> proxyActivity) {        this.context = context;        this.proxyActivity = proxyActivity;    }    /**     * 这个方法就是拦截了Intent的意图,将拦截下的Intent更换成注册过的Activity,并将真正意图的Activity存放在Intetn里面通过系统检测     * @throws Exception     */    public void hookAms() throws Exception {        Class<?> forName = Class.forName("android.app.ActivityManagerNative");        Field defaultField = forName.getDeclaredField("gDefault");        defaultField.setAccessible(true);        Object defaultValue = defaultField.get(null);        Class<?> forName2 = Class.forName("android.util.Singleton");        Field instanceField = forName2.getDeclaredField("mInstance");        instanceField.setAccessible(true);        Object iActivityManagerObject = instanceField.get(defaultValue);        Class<?> iActivity = Class.forName("android.app.IActivityManager");        InvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),                new Class<?>[]{iActivity}, handler);        instanceField.set(defaultValue, proxy);    }    class AmsInvocationHandler implements InvocationHandler {        private Object iActivity;        public AmsInvocationHandler(Object iActivity) {            this.iActivity = iActivity;        }        @Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            Log.i(TAG, "invoke, " + method.getName());            if ("startActivity".contains(method.getName())) {                Intent intent = null;                int index = 0;                for (int i = 0; i < args.length; i++) {                    if (args[i] instanceof Intent) {                        index = i;                        break;                    }                }                intent = new Intent(context, proxyActivity);                Intent realIntent = (Intent) args[index];                intent.putExtra("oldIntent", realIntent);                args[index] = intent;            }            return method.invoke(iActivity, args);        }    }    /**     * 更换系统的Hanlder     * @throws Exception     */    public void hookSystemHandler() throws Exception {        Class<?> forName = Class.forName("android.app.ActivityThread");//通过反射拿到运行时ActivityThread类        Field field = forName.getDeclaredField("sCurrentActivityThread");//然后在获取属性名为sCurrentActivityThread的ActivityThread        field.setAccessible(true);//因为是私有的所以需要设置可以访问        activityThreadValue = field.get(forName);//拿到ActivityThread实例,因为我们声明不僚ActivityThread属性 所以只能用Object        Field mH = forName.getDeclaredField("mH");//拿到运行时类ActivityThread类的mH属性        mH.setAccessible(true);//因为是私有的所以需要设置可以访问        Object handler = mH.get(activityThreadValue);//拿到ActivityThread的Handler实例        Class<?> handlerClass = handler.getClass().getSuperclass();//获取运行时handler父类        Field callbackField = handlerClass.getDeclaredField("mCallback");//在父类中有一个属性名为mCallback的Callback类    这就是Handler的回掉方法        callbackField.setAccessible(true);//因为是私有的所以需要设置可以访问        HookCallback callback = new HookCallback((Handler) handler);//new出自己的Handler        callbackField.set(handler, callback);//把自己的Hanlder换进去   这样就可以为所欲为了        Log.i(TAG, "hook system handler completed.");    }    /**     * 制造自己的Handler 因为Hnalder主要就是需要handleMessage方法     * 所以我们重写handleMessage就好了     * 在重写过程中 把上面Intent放进去的值取出来就是我们需要跳转的Activity     */    class HookCallback implements Handler.Callback {        Handler handler;        public HookCallback(Handler handler) {            this.handler = handler;        }        @Override        public boolean handleMessage(Message msg) {            Log.i(TAG, "message callback.");            if (msg.what == 100) {                Log.i("hook", "handle message 100.");                handleLaunchActivity(msg);            }            handler.handleMessage(msg);            return true;        }        private void handleLaunchActivity(Message msg) {            Object obj = msg.obj;            try {                Field intentField = obj.getClass().getDeclaredField("intent");//通过msg获取到跳转意图                intentField.setAccessible(true);//上面说了好几遍了                Intent proxyIntent = (Intent) intentField.get(obj);//这里就是注册过的Activity                Intent realIntent = proxyIntent.getParcelableExtra("oldIntent");//在注册过的Activity里面我们存放了没注册的Activity                if (realIntent != null) {                    proxyIntent.setComponent(realIntent.getComponent());//通过setComponet这样就完成了 偷梁换柱的行为                }            } catch (Exception e) {                e.printStackTrace();            }        }    }}

有个这个工具类之后 只需要在Application中调用下两个方法就好了

HookUtil hookUtil = new HookUtil(this, proxyActivity.class); try {     hookUtil.hookAms();    hookUtil.hookSystemHandler(); } catch (Exception e) {     e.printStackTrace();}

再看看清单文件

<activity android:name="httputils.MyMainActivity">    <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />    </intent-filter></activity><activity android:name=".proxyActivity" />

看启动一个未注册的Activity

@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    text = (TextView)findViewById(R.id.hello);    text.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View v) {            startActivity(new Intent(MainActivity.this,TestActivity.class));        }    });}

看看效果吧


代码在这里===》》HookDemo

大家看代码吧! 我已经都加了注释   我感觉注释已经说的很清楚了   可能有错误的地方 希望大家能够指正,我也会及时更改,为大家提供更好的资源


感觉我解释的还行的可以给个赞微笑

5 0
原创粉丝点击