Android Small插件化框架--启动插件Activity源码解析(下)

来源:互联网 发布:ui交互设计软件 编辑:程序博客网 时间:2024/05/16 06:26

转自:http://m.blog.csdn.net/article/details?id=53158432

参考博文

http://www.tuicool.com/articles/EjEJNrJ
http://m.w2bc.com/article/126583

AMS对startActivity请求处理及返回过程

根据上一章的分析了解了调用startActivity(),终于把数据和要开启Activity的请求发送到了AMS了,接下来分析在AMS中的处理过程和重新回到app进程。

1、在AMS中处理的过程

AMS中处理startActivity的过程比较复杂,主要涉及了ActivityManagerService、ActivityStackSupervisor、ActivityStack、PackageManagerService、WindowManagerService等几个类。

在ActivityManagerService中,startActivity先是调用了startActivityAsUser(注意,第一个参数caller就是app在服务端的代理ApplicationThreadProxy,它是一个Binder对象,实现了IApplicationThread。):

    public final int startActivity(IApplicationThread caller, String callingPackage,            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,                resultWho, requestCode, startFlags, profilerInfo, bOptions,                UserHandle.getCallingUserId());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然后在startActivityAsUser中调用了ActivityStackSupervisor的startActivityMayWait。

    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {        ......        // TODO: Switch to user app stacks here.        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,                profilerInfo, null, null, bOptions, false, userId, null, null);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

转到ActivityStackSupervisor中后,又在它和ActivityStack之间来回调用了许多次,主要是进行栈和Task的管理、解析Activity的信息,准备窗口的切换之类的工作,最后回到了ActivityStackSupervisor中,调用realStartActivityLocked函数。

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,            boolean andResume, boolean checkConfig) throws RemoteException {            ......            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),                    new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在realStartActivityLocked函数中,app是ProcessRecord类型,app.thread是IApplicationThread类型,也就是从客户端的代理ApplicationThreadProxy,在这儿调用了它的scheduleLaunchActivity方法,接下来就是回到app的进程空间里的过程。

2、AMS回到app进程

再回顾一下AMS和app之间交互原理图:

ActivityThread的类图结构:

ApplicationThreadProxy中scheduleLaunchActivity方法:

    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,            int procState, Bundle state, PersistableBundle persistentState,            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {        ......        mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,                IBinder.FLAG_ONEWAY);        data.recycle();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在该方法中调用IBinder的transact发出命令,然后由ApplicationThread的代理ApplicationThreadNative的onTransact来处理:

    @Override    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)            throws RemoteException {            ......            case SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION:        {            ......            scheduleLaunchActivity(intent, b, ident, info, curConfig, overrideConfig, compatInfo,                    referrer, voiceInteractor, procState, state, persistentState, ri, pi,                    notResumed, isForward, profilerInfo);            return true;        }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

ApplicationThreadNative的onTransact函数会调用scheduleLaunchActivity,其具体实现在ApplicationThread中:

    private class ApplicationThread extends ApplicationThreadNative {        ......        @Override        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,                int procState, Bundle state, PersistableBundle persistentState,                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {            updateProcessState(procState, false);            ActivityClientRecord r = new ActivityClientRecord();            r.token = token;            r.ident = ident;            r.intent = intent;            r.referrer = referrer;            r.voiceInteractor = voiceInteractor;            r.activityInfo = info;            r.compatInfo = compatInfo;            r.state = state;            r.persistentState = persistentState;            r.pendingResults = pendingResults;            r.pendingIntents = pendingNewIntents;            r.startsNotResumed = notResumed;            r.isForward = isForward;            r.profilerInfo = profilerInfo;            r.overrideConfig = overrideConfig;            updatePendingConfiguration(curConfig);            sendMessage(H.LAUNCH_ACTIVITY, r);        }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

ApplicationThread是ActivityThread里的一个内部类,它的scheduleLaunchActivity的实现就是发一个LAUNCH_ACTIVITY类型的message到ActivityThread中的一个handler上。

    private class H extends Handler {            public void handleMessage(Message msg) {            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));            switch (msg.what) {                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, "LAUNCH_ACTIVITY");                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                } break;        ......        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

H类继承自Handler也是Activity Thread的内部类,重写了handleMessage(Message msg)方法。根据标识LAUNCH_ACTIVITY调用了handleLaunchActivity()方法,handleLaunchActivity里调用了performLaunchActivity:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {        ......          Activity a = performLaunchActivity(r, customIntent);    }
  • 1
  • 2
  • 3
  • 4
  • 5

performLaunchActivity中又用到了Instrumentation类,调它的newActivity函数构造出activity对象。newActivity函数很简单,直接用classLoader加载了Activity类,然后用反射调它的构造函数newInstance出activity实例:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {            Activity activity = null;        try {            java.lang.ClassLoader cl = r.packageInfo.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);            }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
        public Activity newActivity(ClassLoader cl, String className,            Intent intent)            throws InstantiationException, IllegalAccessException,            ClassNotFoundException {        return (Activity)cl.loadClass(className).newInstance();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后再回到performLaunchActivity中,在通过newActivity得到activity的实例后,接下来就该调用它的生命周期函数onCreate了,照旧还是通过Instrumentation来完成,调用它的callActivityOnCreate函数。

                activity.mCalled = false;                if (r.isPersistable()) {                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);                } else {                    mInstrumentation.callActivityOnCreate(activity, r.state);                }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在callActivityOnCreate里会调用Activity的performCreate函数,它里面调用的就是我们熟悉的onCreate了。

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {        restoreHasCurrentPermissionRequest(icicle);        onCreate(icicle, persistentState);        mActivityTransitionState.readState(icicle);        performCreateCommon();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

app到AMS过程时序图:

 
总结:到此为止,startActivity的整个流程就结束了,从整个完整流程可以看出,Instrumentation这个类是startActivity整个过程中的必经之路,无论是从app到AMS,还是从AMS回到app都会经过它,所以它就是Hook对象。并且它是ActivityThread里的一个成员mInstrumentation,所以我们在客户端进程中可以通过反射拿到ActivityThread对象,也可以拿到mInstrumentation。

在早期版本中,作者hook的是newActivity,最新版本将其改为对handler.callback进行hook。为什么?

通过请教作者,他给我的答复是:“提前hook时机,可以减少“善后”工作,handler是跑到App进程最开始的地方,到了newActivity实际上有很多事已经被做掉了,需要再通过反射来重置一些参数,比如Resources,这意味着一件事你实际上做了两次。所以越早处理越好。”

Small中对StartActivity的返回处理解析

根据以上原理分析我们知道ActivityThread中的内部类H继承自handler处理主线程消息对列(MessageQueue)中的Message。因为Android的UI操作是由handler机制进行的,所以在Activity的创建需要通过H类处理。Small框架在对AMS到app的过程进行Hook的就是H类。具体在ApkBundleLauncher实现如下:

     public void setUp(Context context) {        ......        // 反射获取ActivityThread类中mH成员变量        field = activityThreadClass.getDeclaredField("mH");        field.setAccessible(true);        //获得activityThread对象中mH成员变量的属性值        Handler ah = (Handler) field.get(thread);        //反射获取Handler类中mCallback成员变量        field = Handler.class.getDeclaredField("mCallback");        field.setAccessible(true);        //向ah对象中赋值为ActivityThreadHandlerCallback()        //此刻的field是mCallback成员变量,下面就将ActivityThreadHandlerCallback的实例赋给mCallback变量        field.set(ah, new ActivityThreadHandlerCallback());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

由以上可知Small主要改变了H类中mCallback成员变量的值。
为什么要这样做呢?
根据handler源码解析可知,Handler内部是通过执行dispatchMessage方法以实现对Message的处理的,Handler的dispatchMessage的源码如下:

    public void dispatchMessage(Message msg) {        //注意下面这行代码        if (msg.callback != null) {            handleCallback(msg);        } else {             //注意下面这行代码            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }             //注意下面这行代码            handleMessage(msg);        }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我们来分析下这段代码:
1.首先会判断msg.callback存不存在,如果msg.callback存在,这种情况下会执行handleCallback(msg), handleCallback源码如下:

private static void handleCallback(Message message) {        message.callback.run();}
  • 1
  • 2
  • 3

2.如果msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。

3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。

综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。

因此Small对mCallback进行hook实现的就是第二种处理方式,ActivityThreadHandlerCallback中具体代码如下:

    private static class ActivityThreadHandlerCallback implements Handler.Callback {        private static final int LAUNCH_ACTIVITY = 100;        @Override        public boolean handleMessage(Message msg) {            if (msg.what != LAUNCH_ACTIVITY) return false;            Object/*ActivityClientRecord*/ r = msg.obj;            //获取到r中的Intent对象            Intent intent = ReflectAccelerator.getIntent(r);            //使用unwrapIntent方法将插件的类名赋给targetClass            String targetClass = unwrapIntent(intent);            if (targetClass == null) return false;            // 替换上插件类对应的activityInfo            ActivityInfo targetInfo = sLoadedActivities.get(targetClass);            ReflectAccelerator.setActivityInfo(r, targetInfo);            return false;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

代码分析可知,handleMessage方法中主要
是将msg.obj中存根的activityInfo中变成插件类对应的activityInfo,以供后续实例Activity和调用onCreate时使用。

重点:
重写的mCallback.handleMessage(msg)返回的是false,为什么要这样做呢?
这样做的目的是为了实现在handler里dispatchMessage方法中对Message的处理除了执行了第二种方式,还接着执行第三种方式。这样做为了实现对源码的轻度hook来达到将存根Activity替换为插件Activity的过程。

unwrapIntent()方法解析:

    private static String unwrapIntent(Intent intent) {        //得到该intent对象中的所有category        Set<String> categories = intent.getCategories();        if (categories == null) return null;        //通过查找categories其中名为REDIRECT_FLAG的category来找到原来插件activity类名        Iterator<String> it = categories.iterator();        while (it.hasNext()) {            String category = it.next();            if (category.charAt(0) == REDIRECT_FLAG) {                //返回对应插件activity类名                return category.substring(1);            }        }        return null;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

app的生命周期调用最后都是由Instrumentation来执行,所以Small框架中定义的InstrumentationWrapper也对其中的callActivityOnCreate,callActivityOnStop,callActivityOnDestroy等方法进行了重写,具体如下:

callActivityOnCreate()方法解析:

        @Override        /**为插件准备资源*/        public void callActivityOnCreate(Activity activity, android.os.Bundle icicle) {            do {                if (sLoadedActivities == null) break;                ActivityInfo ai = sLoadedActivities.get(activity.getClass().getName());                if (ai == null) break;                //同步插件activity对应的窗口信息                applyActivityInfo(activity, ai);            } while (false);            //调用原Instrumentation的callActivityOnCreate方法            sHostInstrumentation.callActivityOnCreate(activity, icicle);            //如果它被其他的应用程序修改了一些,复位原先的activity instrumentation            if (sBundleInstrumentation != null) {                try {                    Field f = Activity.class.getDeclaredField("mInstrumentation");                    f.setAccessible(true);                    Object instrumentation = f.get(activity);                    if (instrumentation != sBundleInstrumentation) {                        f.set(activity, sBundleInstrumentation);                    }                } catch (NoSuchFieldException e) {                    e.printStackTrace();                } catch (IllegalAccessException e) {                    e.printStackTrace();                }            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

applyActivityInfo()方法解析:

    /**     * 申请的插件Activity信息是与插件的AndroidManifest.xml里面一样     * @param activity     * @param ai     */    private static void applyActivityInfo(Activity activity, ActivityInfo ai) {        // Apply window attributes        Window window = activity.getWindow();        window.setSoftInputMode(ai.softInputMode);        activity.setRequestedOrientation(ai.screenOrientation);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

callActivityOnStop方法解析:

        @Override        public void callActivityOnStop(Activity activity) {            sHostInstrumentation.callActivityOnStop(activity);            if (!Small.isUpgrading()) return;            // If is upgrading, we are going to kill self while application turn into background,            // and while we are back to foreground, all the things(code & layout) will be reload.            // Don't worry about the data missing in current activity, you can do all the backups            // with your activity's `onSaveInstanceState' and `onRestoreInstanceState'.            // Get all the processes of device (1)            ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);            List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();            if (processes == null) return;            // Gather all the processes of current application (2)            // Above 5.1.1, this may be equals to (1), on the safe side, we also            // filter the processes with current package name.            String pkg = activity.getApplicationContext().getPackageName();            final List<RunningAppProcessInfo> currentAppProcesses = new ArrayList<>(processes.size());            for (RunningAppProcessInfo p : processes) {                if (p.pkgList == null) continue;                boolean match = false;                int N = p.pkgList.length;                for (int i = 0; i < N; i++) {                    if (p.pkgList[i].equals(pkg)) {                        match = true;                        break;                    }                }                if (!match) continue;                currentAppProcesses.add(p);            }            if (currentAppProcesses.isEmpty()) return;            // The top process of current application processes.            RunningAppProcessInfo currentProcess = currentAppProcesses.get(0);            if (currentProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) return;            // Seems should delay some time to ensure the activity can be successfully            // restarted after the application restart.            // FIXME: remove following thread if you find the better place to `killProcess'            new Thread() {                @Override                public void run() {                    try {                        sleep(300);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    for (RunningAppProcessInfo p : currentAppProcesses) {                        android.os.Process.killProcess(p.pid);                    }                }            }.start();        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

callActivityOnDestroy()方法解析:

        @Override        public void callActivityOnDestroy(Activity activity) {            do {                if (sLoadedActivities == null) break;                String realClazz = activity.getClass().getName();                ActivityInfo ai = sLoadedActivities.get(realClazz);                if (ai == null) break;                //存根activity与真实activity解绑                inqueueStubActivity(ai, realClazz);            } while (false);            //调用原Instrumentation的callActivityOnDestroy方法            sHostInstrumentation.callActivityOnDestroy(activity);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在调用到callActivityOnDestroy时,需要将存根activity与真实activity解绑,主要通过inqueueStubActivity方法实现:

        private void inqueueStubActivity(ActivityInfo ai, String realActivityClazz) {            if (ai.launchMode == ActivityInfo.LAUNCH_MULTIPLE) return;            if (mStubQueue == null) return;            int countForMode = STUB_ACTIVITIES_COUNT;            int offset = (ai.launchMode - 1) * countForMode;            for (int i = 0; i < countForMode; i++) {                String stubClazz = mStubQueue[i + offset];                if (stubClazz != null && stubClazz.equals(realActivityClazz)) {                    mStubQueue[i + offset] = null;                    break;                }            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

从ActivityInfo获取launchMode,通过启动模式和插件Activity类名找到对存根Activity对应的mStubQueue数组位置并将其赋值为null,这样就实现了解绑。
**
关键问题:**
参考博客
1、由于我们欺骗了 AMS , AMS 应该只知道 StubActivity 的存在,它压根儿就不知道TargetActivity是什么,为什么它能正确完成对TargetActivity生命周期的回调呢?

一切的秘密在 token 里面。 AMS 与 ActivityThread 之间对于Activity的生命周期的交互,并没有直接使用Activity对象进行交互,而是使用一个token来标识,这个token是binder对象,因此可以方便地跨进程传递。Activity里面有一个成员变量 mToken 代表的就是它,token可以唯一地标识一个Activity对象,它在Activity的 attach 方法里面初始化;
在 AMS 处理Activity的任务栈的时候,使用这个token标记Activity,因此在Small里面, AMS 进程里面的token对应的是StubActivity,也就是 AMS 还在傻乎乎地操作StubActivity(关于这一点,你可以dump出任务栈的信息,可以观察到dump出的确实是StubActivity)。但是在我们App进程里面,token对应的却是TargetActivity!因此,在ActivityThread执行回调的时候,能正确地回调到TargetActivity相应的方法。

2、为什么App进程里面,token对应的是TargetActivity呢?
回到代码,ActivityClientRecord是在 mActivities 里面取出来的,确实是根据token取;那么这个token是什么时候添加进去的呢?我们看performLaunchActivity就完成明白了:它通过classloader加载了TargetActivity,然后完成一切操作之后把这个activity添加进了 mActivities !另外,在这个方法里面我们还能看到对Ativity attact 方法的调用,它传递给了新创建的Activity一个token对象,而这个token是在ActivityClientRecord构造函数里面初始化的。
至此我们已经可以确认,通过这种方式启动的Activity有它自己完整而独立的生命周期!

最后总结:至此,我们对Small框架中解决“Activity注册和生命周期问题”,从原理和代码两方面做了一个全面的分析。这个过程中,我弄懂了Android的activity启动流程,领会了Small框架怎么通过轻度hook实现对Android插件化的。写了这两篇博文,希望对你有帮助。


1 0
原创粉丝点击