DroidPlugin源码分析处理Activity的启动

正常情况下启动一个Activity,首先需要在AndroidManifest文件中声明,其次需要把该应用安装到手机系统中。

而插件apk是没有正在安装到手机系统中的,也就按照正常的启动流程插件Activity是不能启动的。另外插件apk的类需要加载进来是需要指定ClassLoader。前面的文章也大概讲过,当启动一个插件Activity时,先是用预定义的代理Activity替换目标Activity(及插件Activity)去启动,当AMS处理完回调到应用空间时(及回到运行Activity的进程空间时)再用目标Activity替换回来,去调用目标Activity的声明周期。再这个过程中,要实例化目标Activity用到了PluginClassLoader,自定义的ClassLoader来加载实例化目标Activity.

总结:也就是说要启动插件Activity DroidPlugin至少需要解决以下三个问题:

1 在启动Activity进入到AMS前需要用预定义的代理Activity替换目标Activity(及插件Activity)。

2 请完成后进入到运行Activity的进程中时,需要将目标activity替换回来。

3 需要用自定义的PluginClassLoader加载目标Activity。

通过分析Android Framework源码可以了解到(这里没有分析全部的Activity启动流程,只是针对上面三个问题的源码处理流程,这样能帮助理解DroidPlugin的篡改处理流程)

1 启动一个Activity最终都会通过调用ActivityManagerProxy 也就是AMS服务的代理来请求AMS启动。

2 AMS做完相关Activity的启动工作以后

A 会通过ActivityThread的内部类ApplicationThread这个binder对象回调到运行Activity的进程空间。

B 然后通过调用ActivityThread成员变量mH这个继承自Handler类的对象往主线程发送一条LAUNCH_ACTIVITY的消息。

C 在Handler分发消息的流程函数dispatchMessage中可以了解到,Handler有一个成员变量mCallback,如果不为空则调用mCallback.handleMessage(msg),当callback的handleMessage函数返回True时直接返回了, 就不会执行handleMessage(msg);函数。Activity正常启动的情况下会调用mH.handleMessage函数。执行LAUNCH_ACTIVITY消息对应的处理函数handleLaunchActivity函数启动Activity(也就是调用Activity的生命周期)

3 实例化目标Activity过程。

A 在执行LAUNCH_ACTIVITY消息时,会调用ActivityThread的getPackageInfoNoCheck函数获取一个LoadedApk对象。获取过程中会先通过包名从ActivityThread的成员变量mPackages中去查找是否有包名对应的LoadedApk存在,如果不存在则创建一个LoadedApk对象并以当前包名为key保存到mPackages中。同时将创建的LoadedApk保存到ActivityClientRecord的成员变量packageInfo中。在LoadedApk的构造函数中还会初始化一个mClassLoader的成员变量,默认情况下这个mClassLoader是一个PathClassLoader的对象。

B 创建完LoadedApk对象之后就会调用handleLaunchActivity函数,在这个函数里面会调用performLaunchActivity函数中会获取ActivityClientRecord的成员变量packageInfo内部的mClassLoader来加实例化目标Activity对象。

DroidPlugin就是在上面三段代码处理流程中找到Hook点来解决上面三个问题实现插件Activity的启动的。

1 启动前用代理Activity替换目标Activity:

在DroidPlugin中这个ActivityManagerProxy也已经被Hook,(如何Hook 可以看IActivityManagerHook的Install函数,了解AMS的Hook过程)那么只需要在IActivityManagerHook对应的处理分发类IActivityManagerHookHandle中对ActivityManagerProxy的StartActivity函数进行处理Activity的替换就行了。

2 启动后用目标Activity替换回代理Activity。

在DroidPlugin中通过PluginCallbackHook Hook了ActivityThread中的成员变量mH这个Handler的成员变量mCallback,特别对Id为LAUNCH_ACTIVITY的消息做了将目标Activity替换回代理Activity的预处理。(如何Hook 可以看PluginCallbackHook 的Install函数)

3 篡改实例化目标Activity(插件Activity)的过程。

前面分析了正常Activity对象是通过LoadedApk的成员变量mClassLoader来是实例化的,而在前面的文章也说过插件Activity要想加载,必须通过自定义的继承自DexClassLoader的PluginClassLoader来实例化,因此DroidPlugin用插件Apk的包名为key,伪造了一个LoadedApk对象为Value,保存到了ActivityThread的成员变量mPackages中同时用PluginClassLoader的对象替换了原本保存在LoadedApk成员变量mClassLoader中的ClassLoader对象。

接下来就通过源码分析DroidPlugin对上面三个流程源码的处理:

一 IActivityManagerHook对目标Activity的替换流程。

通过Hook的过程分析可以知道,IActivityManagerHook是通过IActivityManagerHookHandle对AMS各个函数分发处理类,而startActivity是通过startActivity类的函数beforeInvoke来处理的。

startActivity的beforeInvoke函数如下:

    protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            RunningActivities.beforeStartActivity();
            boolean bRet = true;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                bRet = doReplaceIntentForStartActivityAPILow(args);
            } else {
                bRet = doReplaceIntentForStartActivityAPIHigh(args);
            }
            if (!bRet) {
                setFakedResult(Activity.RESULT_CANCELED);
                return true;
            }
            return super.beforeInvoke(receiver, method, args);
        }
}

A 此函数中首先调用的是RunningActivities.beforeStartActivity();

分析此函数前线了解RunningActivities类的一个内部类和几个成员变量的作用。

内部类RunningActivityRecord及其四个成员变量: 记录运行时Activity的一个数据类,

activity:Activity类,保存目标Activity对象。

targetActivityInfo: ActivityInfo类,保存目标ActivityInfo对象。

stubActivityInfo: ActivityInfo类,保存预定义代理ActivityInfo对象。

index: 记录activity启动时的顺序。

mRunningActivityList : 以activity对象为key, RunningActivityRecord对象为Value。保存所有当前进程所有运行的Activity。

mRunningSingleStandardActivityList,mRunningSingleTopActivityList,mRunningSingleTaskActivityList, mRunningSingleInstanceActivityList,

以activity的启动顺序为key,以RunningActivityRecord为Value分别保存四种不同启动模式的Activity。

RunningActivities.beforeStartActivity()函数如下:

    public static void beforeStartActivity() {
        synchronized (mRunningActivityList) {
            for (RunningActivityRecord record : mRunningActivityList.values()) {
                if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {
                    continue;
                } else if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
                    doFinshIt(mRunningSingleTopActivityList);
                } else if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
                    doFinshIt(mRunningSingleTopActivityList);
                } else if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                    doFinshIt(mRunningSingleTopActivityList);
                }
            }
        }
}

这个函数主要是遍历mRunningActivityList,然后根据RunningActivityRecord的类型,除Standard启动模式外,其他三种启动模式分别调用doFinshIt并以分别保存正在运行的其他三种启动模式的map对象为参数。

继续看doFinshIt函数:

    private static void doFinshIt(Map<Integer, RunningActivityRecord> runningActivityList) {
        if (runningActivityList != null && runningActivityList.size() >= PluginManager.STUB_NO_ACTIVITY_MAX_NUM - 1) {
            List<RunningActivityRecord> activitys = new ArrayList<>(runningActivityList.size());
            activitys.addAll(runningActivityList.values());
            Collections.sort(activitys, sRunningActivityRecordComparator);
            RunningActivityRecord record = activitys.get(0);
            if (record.activity != null && !record.activity.isFinishing()) {
                record.activity.finish();
            }
        }
}

这个函数主要就是,获取保存对应启动模式Activity的map对象不为空,且的size大于等于3的情况下,调用activity的finish函数结束最早启动的activity。

也就是说doFinishIt函数是当预定义的Activity不够用时,在启动新的Activity前把最早启动的activity Finish掉。

看完这段代码我有两个疑问:

疑问1: 为什么要在beforeStartActivity函数中遍历mRunningActivityList而不是直接调用doFinishIt函数三次?遍历可能会多次调用同一种启动模式的doFinishIt函数啊。多次调用和调用一次,结果是一样的啊?

疑问2: 为什么doFinishIt函数中最大值是三呢?而不是4?除Standard启动模式之外的三种启动模式预定义了四个啊?还有DialogTheme的Activity也预定义了4个?如果存在如下情况,启动了一个Activity,三个DialogThemeActivity,现在又要启动一个Activity,那不是第一个Activity错杀了?(可能哪里没有理解透彻,希望大虾解答!)

B 回到beforeInvoke继续分析,下面的代码中正对不同阶段的版本调用了不同的函数,最后这两个函数在正常处理完成的逻辑下会返回True,也就是返回super.beforeInvoke,而这个函数默认是返回false的,当beforeInvoke返回false时,就会直接调用被代理的函数去执行。也就是说替换一定发生在这两个函数中,这里就以高版本的处理函数来分析(基本包含低版本的处理逻辑)。

doReplaceIntentForStartActivityAPIHigh函数如下:

        protected boolean doReplaceIntentForStartActivityAPIHigh(Object[] args) throws RemoteException {
            int intentOfArgIndex = findFirstIntentIndexInArgs(args);
            if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
                Intent intent = (Intent) args[intentOfArgIndex];
                if (!PluginPatchManager.getInstance().canStartPluginActivity(intent)) {
                    PluginPatchManager.getInstance().startPluginActivity(intent);
                    return false;
                }
                ActivityInfo activityInfo = resolveActivity(intent);
                if (activityInfo != null && isPackagePlugin(activityInfo.packageName)) {
                    ComponentName component = selectProxyActivity(intent);
                    if (component != null) {
                        Intent newIntent = new Intent();
                            ClassLoader pluginClassLoader = PluginProcessManager.getPluginClassLoader(component.getPackageName());
                            setIntentClassLoader(newIntent, pluginClassLoader);
                        newIntent.setComponent(component);
                        newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                        newIntent.setFlags(intent.getFlags());

                        String callingPackage = (String) args[1];
                        if (TextUtils.equals(mHostContext.getPackageName(), callingPackage)) {
                            newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        }
             }
                        args[intentOfArgIndex] = newIntent;
                        args[1] = mHostContext.getPackageName();
                    }
                }
            }

            return true;
        }

1 从参数中找到Intent参数的位置,取出Intent,然后判断是否能启动插件Activity,如果不能则调用PluginPatchManager的startPluginActivity延迟启动。

能否启动取决于一下两个条件:

条件1:PluginManagerService是否已经启动。

条件2:启动Activity的包名和宿主进程的包名不一样。

2 调用resolveActivity函数从插件服务中找到启动插件插件ActivityInfo。resolveActivity函数不做过多分,看过插件安装的文章中讲过保存的过程,这里只是获取。

3 判断获取到的activityInfo不为空,且是插件中的Activity,然后调用selectProxyActivity(此函数在进程管理文章中已近有分析)找到合适的代理Activity。

4 创建一个新的Intent,设置启动Activity为前面找到的代理Activity,并把目标activity保存到新的Intent中.最后判断调用者的包名是不是宿主进程包名,然后把新的Intent替换掉之前找到的Intent参数的位置。

至此替换过程已经完成。

二:AMS处理完成回到运行Activity进程空间,完成目标Activity替换回来,以及目标Activity实例化过程。

A 前面已经分析过了具体的处理类,直接看PluginCallback对LAUNCH_ACTIVITY的处理函数:handleLaunchActivity:考虑到函数比较长分段阅读:

1 找出目标Activity

    private boolean handleLaunchActivity(Message msg) {
        try {
            Object obj = msg.obj;
            Intent stubIntent = (Intent) FieldUtils.readField(obj, "intent");
            stubIntent.setExtrasClassLoader(mHostContext.getClassLoader());
            Intent targetIntent = stubIntent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
            // 这里多加一个isNotShortcutProxyActivity的判断,因为ShortcutProxyActivity的很特殊,启动它的时候,也会带上一个EXTRA_TARGET_INTENT的数据,就会导致这里误以为是启动插件Activity,所以这里要先做一个判断。之前ShortcutProxyActivity错误复用了key,但是为了兼容,所以这里就先这么判断吧。
            if (targetIntent != null && !isShortcutProxyActivity(stubIntent)) {
                IPackageManagerHook.fixContextPackageManager(mHostContext);
                ComponentName targetComponentName = targetIntent.resolveActivity(mHostContext.getPackageManager());
                ActivityInfo targetActivityInfo = PluginManager.getInstance().getActivityInfo(targetComponentName, 0);
                if (targetActivityInfo != null) {

                    if (targetComponentName != null && targetComponentName.getClassName().startsWith(".")) {
                        targetIntent.setClassName(targetComponentName.getPackageName(), targetComponentName.getPackageName() + targetComponentName.getClassName());
                    }

                    ResolveInfo resolveInfo = mHostContext.getPackageManager().resolveActivity(stubIntent, 0);
                    ActivityInfo stubActivityInfo = resolveInfo != null ? resolveInfo.activityInfo : null;
                    if (stubActivityInfo != null) {
                        PluginManager.getInstance().reportMyProcessName(stubActivityInfo.processName, targetActivityInfo.processName, targetActivityInfo.packageName);
                    }

1.1 是从msg.obj中获取代理Intent参数,然后从代理Intent中获取启动之前保存的目标intent。

1.2 判断目标Intent是否存在,而且不是一个ShortcutProxyActivity然后调用IPackageManagerHook.fixContextPackageManager(mHostContext);

要创建的Activity(及ContextImpl)有一个成员变量mPackageManager 是一个ApplicationPackageManager类对象,再ApplicationPackageManager类里面有一个成员变量,mPM,保存了PackageManager服务的代理对象。

这一步要做的事情就是保证这个代理对象是之前Hook过的对象。

1.3 接下来的代码就是设置目标intent具体的类,然后找到目标ActivityInfo和代理ActivityInfo。最后提交管理。

2 创建插件Activity加载环境:

                    PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);
                    ClassLoader pluginClassLoader = PluginProcessManager.getPluginClassLoader(targetComponentName.getPackageName());
                    setIntentClassLoader(targetIntent, pluginClassLoader);
                    setIntentClassLoader(stubIntent, pluginClassLoader);

这里就是前面DroidPlugin中需要解决的第二个问题,预存LoadedApk,并替换LoadedApk中的ClassLoader。以便加载插件Activity。

先分析完这个函数在分析PluginProcessManager.preLoadApk函数的实现。

3 最后一段代码比较长,而且都是在做兼容处理,就不贴代码了。

具体做的事情就是把目标ActivityInfo和代理ActivityInfo保存到目标Intent中。在msg.obj中把目标Intent替换到原来的代理intent,再把目标ActivityInfo替换掉代理ActivityInfo。

4 PluginProcessManager.preLoadApk还是分段阅读:

    public static void preLoadApk(Context hostContext, ComponentInfo pluginInfo) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, PackageManager.NameNotFoundException, ClassNotFoundException {

        /*添加插件的LoadedApk对象到ActivityThread.mPackages*/
        boolean found = false;
        synchronized (sPluginLoadedApkCache) {
            Object object = ActivityThreadCompat.currentActivityThread();
            if (object != null) {
                Object mPackagesObj = FieldUtils.readField(object, "mPackages");
                Object containsKeyObj = MethodUtils.invokeMethod(mPackagesObj, "containsKey", pluginInfo.packageName);
                if (containsKeyObj instanceof Boolean && !(Boolean) containsKeyObj) {
                    final Object loadedApk;
                    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
                        loadedApk = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginInfo.applicationInfo, CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO());
                    } else {
                        loadedApk = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginInfo.applicationInfo);
                    }
                    sPluginLoadedApkCache.put(pluginInfo.packageName, loadedApk);

这段代码主要做了如下工作:获取ActivityThread对象的成员变量mPackages,并判断mPackages是否包含当前插件包名为key的LoadedApk对象。如果不包含则调用ActivityThread的getPackageInfoNoCheck函数创建LoadedApk对象。以插件包名为key将创建的LoadedApk对象保存到sPluginLoadedApkCache中。

                    String optimizedDirectory = PluginDirHelper.getPluginDalvikCacheDir(hostContext, pluginInfo.packageName);
                    String libraryPath = PluginDirHelper.getPluginNativeLibraryDir(hostContext, pluginInfo.packageName);
                    String apk = pluginInfo.applicationInfo.publicSourceDir;
                    if (TextUtils.isEmpty(apk)) {
                        pluginInfo.applicationInfo.publicSourceDir = PluginDirHelper.getPluginApkFile(hostContext, pluginInfo.packageName);
                        apk = pluginInfo.applicationInfo.publicSourceDir;
                    }
                    if (apk != null) {
                        ClassLoader classloader = null;
                        try {
                            classloader = new PluginClassLoader(apk, optimizedDirectory, libraryPath, ClassLoader.getSystemClassLoader());
                        } catch (Exception e) {
                        }
                        if(classloader==null){
                            PluginDirHelper.cleanOptimizedDirectory(optimizedDirectory);
                            classloader = new PluginClassLoader(apk, optimizedDirectory, libraryPath, ClassLoader.getSystemClassLoader());
                        }
                        synchronized (loadedApk) {
                            FieldUtils.writeDeclaredField(loadedApk, "mClassLoader", classloader);
                        }
                        sPluginClassLoaderCache.put(pluginInfo.packageName, classloader);
                        Thread.currentThread().setContextClassLoader(classloader);
                        found = true;
                    }
                    ProcessCompat.setArgV0(pluginInfo.processName);
                }
            }
        }

这段代码的主要工作是:

1 获取optimizedDirectory,libraryPath,apk三个插件路径(看过插件安装应该非常熟悉了),创建PluginClassLoader对象classloader。

2 替换到LoadedApk中的成员变量mClassLoader.然后以插件包名为key保存到sPluginClassLoaderCache中。

3 设置当前线程ClassLoader为PluginClassLoader对象classloader。

4 设置当前进程名字为插件包名,这个不一定能实现。至少我的手机上还是显示的预定义的pluginXX.

        if (found) {
            PluginProcessManager.preMakeApplication(hostContext, pluginInfo);
        }
}

最后这段代码主要是为之前创建的LoadedApk对象内部的成员mApplication。而preMakeApplication其实就是调用了LoadedApk的makeApplication函数。就不贴代码了。

三: 创建插件Activity创建之前的其他工作:

阅读DroidPlugin的源码可以发现,其实还通过InstrumentationHook Hook了Instrumentation类,查看InstrumentationHook的onInstall函数可以知道实际就是通过PluginInstrumentation 类对象替换了ActivityThread的成员变量mInstrumentation原来的值。

通过源码分析了解到,在performLaunchActivity函数中会调用mInstrumentation.callActivityOnCreate(activity, r.state);而在callActivityOnCreate函数中才会整整调用activity.performCreate来真正执行activity的onCreate函数。也就是说DroidPlugin Hook mInstrumentation变量就是想在执行activity的onCreate函数之前做了一些工作:

具体来看PluginInstrumentation的callActivityOnCreate函数:

    @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        if (enable) {
            IWindowManagerBinderHook.fixWindowManagerHook(activity);
            IPackageManagerHook.fixContextPackageManager(activity);
            PluginProcessManager.fakeSystemService(mHostContext, activity);
            onActivityCreated(activity);//放到最后分析
            fixBaseContextImplOpsPackage(activity.getBaseContext());
  fixBaseContextImplContentResolverOpsPackage(activity.getBaseContext());
        }
//上面代码是实际调用activity onCreate函数前的处理。
        if (mTarget != null) {
            mTarget.callActivityOnCreate(activity, icicle);
        } else {
            super.callActivityOnCreate(activity, icicle);
        }
}

1 IWindowManagerBinderHook.fixWindowManagerHook(activity);

在Activity对象内部有一个PhoneWindow类的成员变量 mWindow, 在PhoneWindow类中有一个内部类WindowManagerHolder,他里面有一个成员变量sWindowManager 保存了WindowManager服务的代理。

这一步要做的事情就是保证PhoneWindow 内部类WindowManager的静态成员变量SwindowManager这个WindowManager代理对象也是我们Hook过的WindowManager。

2 IPackageManagerHook.fixContextPackageManager(activity);这个前面已经分析过了。

3 PluginProcessManager.fakeSystemService(mHostContext, activity);

分析前现需要理解两个变量 SYSTEM_SERVICE_MAP 以及 mServiceCache 是做什么的?

ContextImpl 在类加载的时候会有静态代码块,初始化封装一些系统服务。

静态代码块中,会以要封装服务的名字为key ,以ServiceFetcher 对象为value 保存在SYSTEM_SERVICE_MAP中,其中在创建ServiceFetcher对象时实现了创建封装服务的方法。

当用户需要获取某一个系统服务时,通过ContextImpl getSystemService函数以及服务名字,这个函数就会从SYSTEM_SERVICE_MAP中获取ServiceFetcher对象,

然后调用ServiceFetcher对象的getService来获取。getService中,会先从ContextImp的成员变量mServiceCache,ServiceFetcher对象中有一个mContextCacheIndex保存了

一个位置,如果ServiceFetcher中的服务已近缓存了,那就必定就在mServiceCache的mContextCacheIndex位置,

如果不存在那么就直接调用ServiceFetcher的createService,创建服务对象并缓存到mServiceCache的mContextCacheIndex位置。

有了上面的理解,接下来fakeSystemService这个函数就好理解了:

这个函数调用了另外一个函数fakeSystemServiceInner

在fakeSystemServiceInner中主要做的事情就是:

事情一:保证新创建的Activity(及ContextImpl)中mServiceCache缓存的服务对象都是我们Hook过的。

事情二:为了兼容性跳过一些不能篡改的服务sSkipService。

事情三:在确保ContextImplement成员变量mServiceCache中缓存的服务是已经Hook过的以后,把这个ContextImplement 保存到mFakedContext。

这个函数的目的,因为系统服务可能缓存在多个地方,所以需要保证多个地方的服务都是Hook过的,从而保证插件正常运行。

4 fixBaseContextImplOpsPackage(activity.getBaseContext());

修改ContextImpl中的成员变量mOpPackageName 为宿主进程的包名。

5 fixBaseContextImplContentResolverOpsPackage

修改ContentResolver的成员变量,mPackageName

6 onActivityCreated

这个函数主要做的事情如下:

事情一:从目标Intent中获取目标ActivityInfo和代理ActivityInfo,调用RunningActivities.onActivtyCreate记录运行的Activity信息。

事情二:设置目标Activity屏幕请求方向。

事情三:调用PluginManager.getInstance().onActivityCreated添加到进程已经Activity管理中。

到这里DroidPlugin对插件Activity的处理全部分析完了。

时间: 2024-10-17 16:15:36

DroidPlugin源码分析处理Activity的启动的相关文章

DroidPlugin源码分析处理Activity的启动211ew4dfr

正常情况下启动一个Activity,首先需要在AndroidManifest文件中声明,其次需要把该应用安装到手机系统中. 而插件apk是没有正在安装到手机系统中的,也就按照正常的启动流程插件Activity是不能启动的.另外插件apk的类需要加载进来是需要指定ClassLoader.前面的文章也大概讲过,当启动一个插件Activity时,先是用预定义的代理Activity替换目标Activity(及插件Activity)去启动,当AMS处理完回调到应用空间时(及回到运行Activity的进程空

插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broadcastReceiver的启动过程后,今天将分析下360 DroidPlugin是如何预注册占坑的?本篇文章主要分析Activity预注册占坑,Activity占了坑后又是什么时候开始瞒天过海欺骗AMS的?先看下Agenda: AndroidMainfest.xml中概览 Activity中关键方

DroidPlugin源码分析插件运行环境初始化

从DroidPlugin的官方文档中我们知道. 2 在AndroidManifest.xml中使用插件的com.morgoo.droidplugin.PluginApplication: 或者在自定义的Application的onCreate()函数中,调用PluginHelper.getInstance().applicationOnCreate(getBaseContext()); 在Application的attachBaseContext()函数中,调用 PluginHelper.get

插件开发之360 DroidPlugin源码分析(五)Service预注册占坑

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broadcastReceiver的启动过程后,今天将分析下360 DroidPlugin是如何预注册占坑的?本篇文章主要分析Service预注册占坑,Service占了坑后又是什么时候开始瞒天过海欺骗AMS的?先看下Agenda: AndroidMainfest.xml中概览 Service中关键方法被h

DroidPlugin源码分析插件进程管理以及预注册Activity,Service,ContentProvide的选择

在360对DroidPlugin的特点介绍中有云: 插件的四大组件完全不需要在Host程序中注册,支持Service.Activity.BroadcastReceiver.ContentProvider四大组件. 实现了进程管理,插件的空进程会被及时回收,占用内存低. 之所以支持Service,Activity,ContentProvider三大组件,是因为DroidPlugin在AndroidManifest文件中预先注册了8个运行插件的进程,每个进程预注册Service一个, Content

源码分析_Shinken-2.4.0001.启动脚本/etc/init.d/shinken源码分析?

简单介绍:说明: Shinken是一个网络监控平台,可以通过一系列直观的方式监控网络内的各种健康状况.Shinken脱胎于Nagios,其实Shinken这个项目本身就是一帮Nagios项目的人无法忍受Nagios,自己跳出来重新用纯Python重构了一下,甚至完全兼容Nagios的配置文件. 相关地址: 官网地址: http://www.shinken-monitoring.org/ 官网文档: http://shinken.readthedocs.io/en/latest/ 论坛地址: ht

Android触摸屏事件派发机制详解与源码分析三(Activity篇)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)>,阅读本篇之前建议先阅读. 1 背景 还记得前面两篇从Android的基础最小元素控件(View)到ViewGroup控件的触摸屏事件分发机制分析吗?你可能看完会有疑惑,View的事件是ViewGro

Solr初始化源码分析-Solr初始化与启动

用solr做项目已经有一年有余,但都是使用层面,只是利用solr现有机制,修改参数,然后监控调优,从没有对solr进行源码级别的研究.但是,最近手头的一个项目,让我感觉必须把solrn内部原理和扩展机制弄熟,才能把这个项目做好.今天分享的就是:Solr是如何启动并且初始化的.大家知道,部署solr时,分两部分:一.solr的配置文件.二.solr相关的程序.插件.依赖lucene相关的jar包.日志方面的jar.因此,在研究solr也可以顺着这个思路:加载配置文件.初始化各个core.初始化各个

DroidPlugin源码分析服务与静态广播的处理214r3rff4

上一篇文章分析过DroidPlugin对Activity的处理过程,不得不为对DroidPlugin的工程师们钦佩不已,那么是不是Service可以像Activity的处理过程一样来处理呢?前面讲过每一个代理进程只是预定义了一个Service,如果某一个插件中有多个Service,那岂不是某一个时刻只能有一个Service运行呢?由此可以判定可能Service的处理和Activity不一样. 一方面:平时使用Activity主要是用于展示界面和用户交互,Activity的生命周期可能受用户控制,