`
shuai1234
  • 浏览: 972690 次
  • 性别: Icon_minigender_1
  • 来自: 山西
社区版块
存档分类
最新评论

8个类搞定插件化——Activity实现方案

 
阅读更多

写在前面

本文原创,转载请以链接形式注明地址:http://kymjs.com/code/2016/05/15/01
前两篇文章写完后,有人跟我说怎么觉得你文章风格突然变了,最近讲了这么多内容变啰嗦了,没有你高效率精简的风格了。宝宝心里苦啊,不是我不想,实在是插件化这东西,如果你不知道理论知识的话,根本没办法去理解啊。接下来这几篇我尽可能的以实践为主,让大家都能看得懂。 

在 序文 【Android 插件化的过去 现在 未来】中简单的跟大家讲过现在开源社区中所有插件化的基本实现原理。
从本文开始就带大家用最简单的办法实现一个插件化库。 
Android插件化

Activity 加载过程

首先讲讲最主要的功能,Activity 的动态加载。查看源码我们知道

Android插件化2

  • 每个Activity的启动过程都是通过startActivityForResult() 最终都会调用Instrument.execStartActivity()
  • 再由ActivityManagerNative.startActivity() 通过 IPC AMS所在进程,ActivityManagerService.startActivity()
  • 最后 ActivityStackSupervisor.startActivityLocked(),权限以及安全检查mService.checkPermission。我们的Activity如果不注册就会在这个检查时返回一个没有注册的错误,最后回到应用进程的时候抛出这个没注册的异常。
  • 安全校验完成以后,会调用ApplicationThread.scheduleLaunchActivity()
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState,
results, newIntents, andResume, mService.isNextTransitionForward(),
profilerInfo);  
 
//ApplicationThread.scheduleLaunchActivity中发送消息的部分(只有这部分是有用的)
privatevoidsendMessage(intwhat,Objectobj,intarg1,intarg2,booleanasync){
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

顺带一说:上面app.thread.scheduleLaunchActivity()的第7个参数,task字段包含了一个ActivityStack,就是我们即将创建的Activity所在的ActivityStack,而如果是通过直接调用Context类的startActivity()方法;这种方式启动的Activity没有 Activity栈,因此不能以 standard 方式启动,必须加上FLAG_ACTIVITY_NEW_TASK这个 Flag 。而通常我们都是调用被Activity类重载过的startActivity()方法,这个是有 Stack 的。

这一步让ApplicationThread做好跳转 activity 的准备(一些数据的封装),紧接着通过handle发送消息通知app.thread要进行Activity启动调度了,然后 app.thread接收到消息的时候才开始进行调度。

  • 这个message的接收是在ActivityThread中的handleMessage(Message msg)处理的。
case LAUNCH_ACTIVITY: {
	Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
	final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
	
	r.packageInfo = getPackageInfoNoCheck(
	        r.activityInfo.applicationInfo, r.compatInfo);
	handleLaunchActivity(r, null);
	Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  • 这句中handleLaunchActivity()又调用了performLaunchActivity(r, customIntent); 而最终又调用了这句:
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
        cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);

兜了一圈又回到Instrumentation了。结果终于找到了可以hook的点了,就是这个mInstrumentation.newActivity()

这一部分详细讲解可以查看:Android应用程序启动过程源代码分析
Activity生命周期管理

替换Activity加载过程

知道了上面Activity启动过程,我们要做的就是通过替换掉Instrumentation类,达到定制插件运行环境的目的。

// 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);

// 拿到原始的 mInstrumentation字段
Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);

//如果没有注入过,就执行替换
if (!(mInstrumentation instanceof PluginInstrumentation)) {
    PluginInstrumentation pluginInstrumentation = new PluginInstrumentation(mInstrumentation);
    mInstrumentationField.set(currentActivityThread, pluginInstrumentation);
}

这样子就替换掉了系统的Instrumentation

而在Instrumentation中,有一个方法叫newActivity() 
这个方法就是实际创建Activity的方法,它的返回值就是我们应用中实际使用的 activity。
我们就可以在这里,判断到如果即将加载的 className 是一个插件中的Activity,那么就通过ClassLoader.load(className).newInstance(); 创建插件 Activity 并返回来替换掉原本系统要创建的Activity 了。

@Override
publicActivitynewActivity(ClassLoadercl,StringclassName,Intentintent)throwsInstantiationException,IllegalAccessException,ClassNotFoundException{
    if (intent != null) {
        isPlugin = intent.getBooleanExtra(HJPlugin.FLAG_ACTIVITY_FROM_PLUGIN, false);
    }
    if (isPlugin && intent != null) {
        className = intent.getStringExtra(HJPlugin.FLAG_ACTIVITY_CLASS_NAME);
    } else {
        isPlugin = HJPlugin.getInstance().getPluginAtySet().contains(className);
    }
    return super.newActivity(cl, className, intent);
}

插件的跳转支持

如果仅仅是启动一个未安装的Activity,上面所做的事情已经足够了。但是如果我们需要从插件中启动另一个插件Activity,就需要多做一些事了。
Activity启动时,会调用Instrumentation. execStartActivity()方法,我们所要做的就是重写这个方法,并且重新定义一个intent,来替换掉原本代码中的intent,这个替换的目的就是为了防止上文提到的ActivityStackSupervisor.startActivityLocked()安全校验,我们要把 intent 原本的setClass()方法传入的 class 给替换成一个合法的已经注册过的Activity(可以是任何一个,只要是注册过就行),接着将原本要启动的插件 Activity 类名作为一个字符串保存在Bundle里面,这样到我们的Instrumentation.newActivity()执行时判断如果是一个插件Activity,就不去创建 intent 传递的 Activity.class,而是创建Intent.Bundle里面保留的插件 Activity。

/**
 * 覆盖掉原始Instrumentation类的对应方法,用于插件内部跳转Activity时适配
 *
 * @Override
 */
publicActivityResultexecStartActivity(Contextwho,IBindercontextThread,IBindertoken,Activitytarget,Intentintent,intrequestCode,Bundleoptions){
    replaceIntentTargetIfNeed(who, intent);
    try {
        // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
        Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                "execStartActivity", Context.class, IBinder.class, IBinder.class,
                Activity.class, Intent.class, int.class, Bundle.class);
        execStartActivity.setAccessible(true);
        return (ActivityResult) execStartActivity.invoke(mBase, who,
                contextThread, token, target, intent, requestCode, options);
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("do not support!!!" + e.getMessage());
    }
}

结尾

至此,通过替换掉系统的 Instrumentation,我们已经可以将 Activity 动态加载到应用中了。但是如果完整实现出来,还会有个问题,就是类可以完美执行,但是资源还不能加载进来,下章就讲资源的加载以及 so文件和 Service 的加载了。【8个类搞定插件化——Service实现方案

 

分享到:
评论

相关推荐

    安卓Android源码——Activity实现透明的最简洁Demo.zip

    安卓Android源码——Activity实现透明的最简洁Demo.zip

    Android四大组件——Activity——Activity的生命周期.doc

    Android 四大组件——Activity——Activity 的生命周期 Android 四大组件中的 Activity 是一个非常重要的组件,它是用户与应用程序交互的入口。Activity 的生命周期是指从创建到销毁的整个过程,在这个过程中,...

    安卓Android源码——Activity实现透明的最简洁Demo.rar

    透明Activity的实现是一个常见的需求,它可以为用户提供更丰富的视觉体验,例如过渡效果或者自定义启动屏幕。本Demo旨在展示如何在Android中创建一个最简洁的透明Activity。下面将详细解释实现透明Activity的关键...

    安卓Andriod源码——Activity实现透明的最简洁Demo.zip

    本Demo主要展示了如何通过简单的代码实现Android Activity的透明化。 首先,我们了解透明Activity的基本原理。在Android中,Activity的背景颜色默认为白色,但通过修改主题(Theme)或者直接在布局文件中设置背景色...

    Android四大组件——Activity(一)Activity之间通信及其生命周期变化

    例如,当需要从一个Activity跳转到另一个Activity时,我们可以创建一个Intent,设置目标Activity的类名,然后调用`startActivity()`方法。如果需要传递数据,可以在Intent中添加额外的数据,如键值对,通过`putExtra...

    Android学习3——Activity生命周期

    在“Android学习3——Activity生命周期”这个主题中,我们将深入探讨Activity如何启动、运行、暂停、停止以及销毁,并了解每个状态之间的转换。 Activity生命周期主要包括以下几个关键状态: 1. **初始状态...

    安卓Android源码——Activity设置相同的action进行判断源码.zip

    在这个“安卓Android源码——Activity设置相同的action进行判断源码.zip”压缩包中,包含的资源可能帮助我们深入理解这一过程。 首先,让我们了解Intent的作用。Intent是Android中的一种消息对象,用于在应用程序的...

    安卓Android源码——activity切换特效.zip

    总的来说,"安卓Android源码——activity切换特效.zip"这个压缩包很可能包含了一系列的源代码示例,演示了如何利用Android的动画系统实现各种Activity切换特效。通过研究这些代码,开发者可以学习如何在自己的应用中...

    安卓Andriod源码——activity切换特效.zip

    本压缩包“安卓Andriod源码——activity切换特效.zip”提供了一些关于如何实现Activity间动画的示例代码。 首先,我们要理解Android中的Activity动画分为两种主要类型:进入动画(Enter Animation)和退出动画...

    android开发实现插件化开发,使用hook启动未注册的activity实现demo

    本示例"android开发实现插件化开发,使用hook启动未注册的activity实现demo",提供了一种轻量级的解决方案,仅需三个关键工具类就能达成目标。下面我们将详细探讨这个过程。 首先,我们来理解“hook”技术。在编程...

    Activity最小化终极解决方案

    下面将详细介绍一种被称为“Activity最小化终极解决方案”的方法,该方法涉及到了Android的权限设置、Activity生命周期以及Logcat的使用。 首先,我们需要了解Android对Home键的处理机制。默认情况下,系统不允许...

    动手学Android之七——Activity生命周期

    "动手学Android之七——Activity生命周期"这个主题深入探讨了Activity如何在应用程序中创建、运行、暂停、停止以及销毁的过程,这对于理解和优化应用性能至关重要。在这个例子程序中,开发者将有机会实践这些生命...

    Android源码——Activity设置相同的action进行判断源码_new_02.zip

    这个压缩包"Android源码——Activity设置相同的action进行判断源码_new_02.zip"很可能包含了关于如何在源码级别处理具有相同action的多个Intent的分析。 在Android的Intent解析过程中,当有多个Activity可以响应同...

    动手学Android之二——初识Activity示例代码

    当我们谈论"动手学Android之二——初识Activity示例代码"时,这通常指的是一个简单的教程,旨在帮助初学者理解如何创建并运行第一个Android应用,从而开始他们的Android开发之旅。 在Android中,Activity是负责显示...

    Android源码——Activity设置相同的action进行判断源码_new_02.7z

    当我们需要根据不同的情况启动同一个Activity时,通常会通过Intent的Action属性来区分不同的启动意图。本压缩包文件提供了关于如何在Android源码中处理相同Action的Activity判断的详细示例。 在Android开发中,...

    安卓Android源码——(Activity跳转与操作).zip

    这个压缩包文件“安卓Android源码——(Activity跳转与操作).zip”显然专注于讲解如何在Android系统中进行Activity的跳转和操作。以下是关于这个主题的详细知识点: 一、Activity生命周期 1. Activity有七个关键状态...

    Activity插件化解决方案.docx

    Activity插件化解决方案.docx

    activity更新另一个activityUI方法——广播

    当我们需要在一个Activity中更新另一个Activity的用户界面时,直接调用另一个Activity的方法是不可行的,因为这违反了Android的组件通信规则。在这种情况下,我们可以利用Android的BroadcastReceiver机制来实现这种...

    eclipse activity 工作流插件

    Eclipse Activity工作流插件是用于增强Eclipse IDE的一个强大工具,主要针对使用Activiti BPMN(业务流程建模和规范)框架的开发者。通过这个插件,开发人员可以更直观地设计、编辑和管理业务流程,提升工作效率。...

Global site tag (gtag.js) - Google Analytics