滴滴插件化解决方案VirtualAPK源码解析之Activity

以下文章摘录自滴滴插件化框架VirtualAPK原理解析(一)之插件Activity管理

具体的使用手册请参考官方文档和demo: VirtualAPK

723726-e43349d26881ee96.png

(Activity启动流程图)
(startActivityLocked之后处理的都是Activity任务栈相关内容)

ActivityManagerService的功能

在解释VirtualAPK是如何对Activity进行管理之前,有必要说一下ActivityManagerService,Android的四大组件都需要与它打交道,它主要为四大组件做了这些事情:

1.启动activity:startActivity最终会调用到AMS的startActivty方法(由上图可知),实现了Activity的启动。同时Activity的生命周期回调也是在AMS中完成的

[->ActivityManagerService.java]

    @Override
    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());
    }
2.启动service:startService,bindService最终会调用到AMS的startSservice和bindService方法:

[->ActivityManagerService.java]

   @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }


public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");

        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }
3.广播:动态广播的注册和接收在AMS中完成(静态广播在PMS中完成)

[->ActivityManagerService.java]

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller("registerReceiver");
        int callingUid;
        int callingPid;
        synchronized(this) {
            ProcessRecord callerApp = null;
            if (caller != null) {
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != Process.SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            userId = this.handleIncomingUser(callingPid, callingUid, userId,
                    true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            List allSticky = null;

            // Look for any matching sticky broadcasts...
            Iterator actions = filter.actionsIterator();
            if (actions != null) {
                while (actions.hasNext()) {
                    String action = (String)actions.next();
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.USER_ALL);
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.getUserId(callingUid));
                }
            } else {
                allSticky = getStickiesLocked(null, filter, allSticky,
                        UserHandle.USER_ALL);
                allSticky = getStickiesLocked(null, filter, allSticky,
                        UserHandle.getUserId(callingUid));
            }

            // The first sticky in the list is returned directly back to
            // the client.
            Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;

            if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter
                    + ": " + sticky);

            if (receiver == null) {
                return sticky;
            }

            ReceiverList rl
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
...........
4.ContentProvider:getContentResolver最终从AMS的getContentProvider获取到ContentProvider

[->ActivityManagerService.java]

    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        enforceNotIsolatedCaller("getContentProvider");
        if (caller == null) {
            String msg = "null IApplicationThread when getting content provider "
                    + name;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
        // with cross-user grant.
        return getContentProviderImpl(caller, name, null, stable, userId);
    }

AMS以Binder的方式提供给应用程序使用的系统服务,所以我们要处理插件中的四大组件,必须要Hook掉AMS进行相应的处理。

Activity的启动

Context的startActivity

[->Context.java]

 /**
     * Same as {@link #startActivity(Intent, Bundle)} with no options
     * specified.
     *
     * @param intent The description of the activity to start.
     *
     * @throws ActivityNotFoundException  
     *`
     * @see #startActivity(Intent, Bundle)
     * @see PackageManager#resolveActivity
     */
    public abstract void startActivity(@RequiresPermission Intent intent);

由于Activity和Service并没有直接继承Context,而是继承ContextWrapper,所以我们直接看看ContextWrapper的startActivity方法:
[->ContextWrapper.java]

    @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

而此处的mBase其实就是ContextImpl,下面看看ContextImpl的startActivity:
[->ContextImpl.java]

   @Override
    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }

    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity)null, intent, -1, options);
    }

看到这里,你是不是突然恍然大悟,为什么平常在使用非Activity的Context来startActivity的时候会需要添加FLAG_ACTIVITY_NEW_TASK;
[->Instrumentation.java]

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    // ... 省略无关代码
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();

        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
    }
    return null;
}

到这里我们发现真正调用的是ActivityManagerNative的startActivity方法。

Activity的startActivity

Activity类的startActivity方法相比Context而言直观了很多;这个startActivity通过若干次调用辗转到达startActivityForResult这个方法,在这个方法内部有如下代码:
[->]

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {
      ................
            Instrumentation.ActivityResult ar =
                    mInstrumentation.execStartActivity(
                            this, mMainThread.getApplicationThread(), mToken, this,
                            intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                        mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                        ar.getResultData());
            }
      ................
    }

可以得知,通过Activity和ContextImpl类启动Activity是一样的,他们都通过Instrumentation这个辅助类调用到了ActivityManagerNative的方法。

Hook AMS

前面说过,startActivity最终通过ActivityManagerNative的本地ActivityManagerProxy的startActivity方法远程调用了AMS的startActivity方法,ActivityManagerNative实际上就是ActivityManagerService这个远程对象的Binder代理对象;每次需要与AMS打交道的时候,需要借助这个代理对象通过驱动进而完成IPC调用。
这里我们多说一句:ActivityManagerNative是运行在app进程,而ActivityManagerService是系统服务运行在在system_Server进程中,在SystemServer#startBootstrapServices启动,system_Server是由Zygote进程孵化出来的。我们从包名也可以看得出来。
ActivityManagerNative所属包名为:package android.app;
ActivityManagerService所属包名为:package com.android.server.am;
这里我们先看下ActivityManagerService在什么时候被注册的
[->ActivityManagerService.java]

   public void setSystemProcess() {
            //注意这行代码 其中Context.ACTIVITY_SERVICE为 public static final String     ACTIVITY_SERVICE = "activity";
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this));
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));
  .....................
}

下面言归正传,在ActivityManagerNative中有一个getDefault()方法
[->ActivityManagerNative.java]

  /**
    *  这里返回的IActivityManager为ActivityManagerProxy对象
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            //获取的是ActivityManagerService
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            //返回的为ActivityManagerProxy对象
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
}

framework使用了一个单例把这个AMS的代理对象保存了起来;这样只要需要与AMS进行IPC调用,获取这个单例即可,所以我们需要Hook掉这个单例,就可以达到Hook AMS的效果(其实hook的是ActivityManagerProxy),在Virtualapk中我们就可以看到如下一段代码:
[->VirtualAPK\PluginManager.java]

    /**
     * hookSystemServices, but need to compatible with Android O in future.
     */
    private void hookSystemServices() {
        try {
          //系统ActivityManagerProxy的单例模式
            Singleton<IActivityManager> defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");
    //其中这里的ActivityManagerProxy为我们自己的ActivityManagerProxy,注意和系统的ActivityManagerProxy区别开来
            IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());

            // Hook IActivityManager from ActivityManagerNative
            ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);

            if (defaultSingleton.get() == activityManagerProxy) {
                this.mActivityManager = activityManagerProxy;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

可以看到在VirtualAPK中PluginManager类里,就是替换掉了ActivityManagerNative里原先ActivityManagerProxy的为VirtualAPK中自己的ActivityManagerProxy,而这个ActivityManagerProxy其实是一个动态代理,所有ActivityManagerNative中的方法都会经过这个代理。
[->自定义的ActivityManagerProxy.java]

 public static IActivityManager newInstance(PluginManager pluginManager, IActivityManager activityManager) {
        return (IActivityManager) Proxy.newProxyInstance(activityManager.getClass().getClassLoader(), new Class[] { IActivityManager.class }, new ActivityManagerProxy(pluginManager, activityManager));
    }

我们可以看到在ActivityManagerProxy的invoke方法,针对一些方法进行了处理
[->ActivityManagerProxy.java]

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("startService".equals(method.getName())) {
            try {
                return startService(proxy, method, args);
            } catch (Throwable e) {
                Log.e(TAG, "Start service error", e);
            }
        } else if ("stopService".equals(method.getName())) {
            try {
                return stopService(proxy, method, args);
            } catch (Throwable e) {
                Log.e(TAG, "Stop Service error", e);
            }
        } else if ("stopServiceToken".equals(method.getName())) {
            try {
                return stopServiceToken(proxy, method, args);
            } catch (Throwable e) {
                Log.e(TAG, "Stop service token error", e);
            }
        } else if ("bindService".equals(method.getName())) {
            try {
                return bindService(proxy, method, args);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        } else if ("unbindService".equals(method.getName())) {
            try {
                return unbindService(proxy, method, args);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        } else if ("getIntentSender".equals(method.getName())) {
            try {
                getIntentSender(method, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if ("overridePendingTransition".equals(method.getName())){
            try {
                overridePendingTransition(method, args);
            } catch (Exception e){
                e.printStackTrace();
            }
        }

        try {
            // sometimes system binder has problems.
            return method.invoke(this.mActivityManager, args);
        } catch (Throwable th) {
            Throwable c = th.getCause();
            if (c != null && c instanceof DeadObjectException) {
                // retry connect to system binder
                IBinder ams = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                if (ams != null) {
                    IActivityManager am = ActivityManagerNative.asInterface(ams);
                    mActivityManager = am;
                }
            }

            Throwable cause = th;
            do {
                if (cause instanceof RemoteException) {
                    throw cause;
                }
            } while ((cause = cause.getCause()) != null);

            throw c != null ? c : th;
        }

    }

上述关于startService等方法的处理,我们在后面说道service时再详细说明,这里我们只要知道VirtualAPK中是如何Hook AMS的就可以了。

启动插件Activity的限制

在Android中启动Activity有一个限制:必须在AndroidManifest.xml中显示声明使用的Activity;否则会遇到下面这种异常:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.dldemo/com.example.dldemo.MainActivity}: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.dldemo/com.example.dldemo.TestActivity1}; have you declared this activity in your AndroidManifest.xml?
                                                                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
                                                                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
                                                                      at android.app.ActivityThread.-wrap11(ActivityThread.java)
                                                                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
                                                                      at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                      at android.os.Looper.loop(Looper.java:148)
                                                                      at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                      at java.lang.reflect.Method.invoke(Native Method)
                                                                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

必须在AndroidManifest.xml中显示声明使用的Activity,这个硬性要求很大程度上限制了插件系统的发挥:因为插件的Activity必定不会在宿主程序中进行声明。那么我们改如何绕过这个限制呢?既然AndroidManifest文件中必须声明,那么我们就声明一个或者多个替身Activity(其实这个替身Activity在本地并不存在,只是为了绕过本地检查)好了,当需要启动插件中的某个Activity,就让系统以为启动的是AndroidManifest中声明的那个替身,暂时骗过系统,然后到合适的时候又替换为我们在插件中真正需要启动的Activity。接下来我们看下Virtualapk的Library库,在AndroidManifest中声明了各种启动模式的Activity,如下所示:

<!-- Stub Activities -->
        <activity android:name=".A$1" android:launchMode="standard"/>
        <activity android:name=".A$2" android:launchMode="standard"
            android:theme="@android:style/Theme.Translucent" />

        <!-- Stub Activities -->
        <activity android:name=".B$1" android:launchMode="singleTop"/>
        <activity android:name=".B$2" android:launchMode="singleTop"/>
        <activity android:name=".B$3" android:launchMode="singleTop"/>
        <activity android:name=".B$4" android:launchMode="singleTop"/>
        <activity android:name=".B$5" android:launchMode="singleTop"/>
        <activity android:name=".B$6" android:launchMode="singleTop"/>
        <activity android:name=".B$7" android:launchMode="singleTop"/>
        <activity android:name=".B$8" android:launchMode="singleTop"/>

        <!-- Stub Activities -->
        <activity android:name=".C$1" android:launchMode="singleTask"/>
        <activity android:name=".C$2" android:launchMode="singleTask"/>
        <activity android:name=".C$3" android:launchMode="singleTask"/>
        <activity android:name=".C$4" android:launchMode="singleTask"/>
        <activity android:name=".C$5" android:launchMode="singleTask"/>
        <activity android:name=".C$6" android:launchMode="singleTask"/>
        <activity android:name=".C$7" android:launchMode="singleTask"/>
        <activity android:name=".C$8" android:launchMode="singleTask"/>

        <!-- Stub Activities -->
        <activity android:name=".D$1" android:launchMode="singleInstance"/>
        <activity android:name=".D$2" android:launchMode="singleInstance"/>
        <activity android:name=".D$3" android:launchMode="singleInstance"/>
        <activity android:name=".D$4" android:launchMode="singleInstance"/>
        <activity android:name=".D$5" android:launchMode="singleInstance"/>
        <activity android:name=".D$6" android:launchMode="singleInstance"/>
        <activity android:name=".D$7" android:launchMode="singleInstance"/>
        <activity android:name=".D$8" android:launchMode="singleInstance"/>

那以上这些占坑的Activity是在什么时候被偷梁换柱的呢?这就涉及到Activity的启动过程了。

Activity启动过程

从上文的分析我们已经知道,Activity的startActivity最终会调用到ActivityManagerNative中的内部类ActivityManagerProxy的startActivity方法,然后会通过Binder IPC(至于Binder驱动是如何传递信息的,有时间我会再写一篇分析)调用到AMS所在进程的AMS的startActivity方法,Android系统的组件生命周期管理就是在AMS里面完成的。
[->ActivityManagerService]
ActivityManagerService#startActivity

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, options,
            UserHandle.getCallingUserId());
    }

直接调用了startActivityAsUser这个方法,接着是ActivityStarter的startActivityMayWait方法。
startActivityMayWait这个方法前面对参数进行了一系列处理,在这个方法内部对传进来的Intent进行了解析,并尝试从中取出关于启动Activity的信息。
然后这个方法调用了当前类的startActivityLocked方法,在startActivityLocked方法内部进行了一系列重要的检查:比如权限检查,Activity的exported属性检查等等。上文说过,启动没有在Manifestfest总生命的Activity会抛出异常也是在这里进行检查的,如下:
[->ActivityStarter.java]
ActivityStarter#startActivityLocked

   if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
            // We couldn't find a class that can handle the given Intent.
            // That's the end of that!
            //当前class不能解析
            err = ActivityManager.START_INTENT_NOT_RESOLVED;
        }

        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
            // We couldn't find the specific class specified in the Intent.
            // Also the end of the line.
            //当前class不存在
            err = ActivityManager.START_CLASS_NOT_FOUND;
        }

这里返回ActivityManager.START_CLASS_NOT_FOUND之后,在Instrument的execStartActivity返回之后会检查这个值,然后抛出异常:
[->Instrumentation.java]
Instrumentation#execStartActivity

 public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
           int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
}

public static void checkStartActivityResult(int res, Object intent) {
    switch (res) {
            case ActivityManager.START_INTENT_NOT_RESOLVED:
            case ActivityManager.START_CLASS_NOT_FOUND:
                if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                    throw new ActivityNotFoundException(
                            "Unable to find explicit activity class "
                            + ((Intent)intent).getComponent().toShortString()
                            + "; have you declared this activity in your AndroidManifest.xml?");
                throw new ActivityNotFoundException(
                        "No Activity found to handle " + intent);
    }
}

从这里,我们明白了必须在AndroidManifest.xml中显示声明使用的Activity的原因;然而这个校检过程发生在AMS所在的进程system_server,我们是不能更改system_server进程的东西的。

在明白了为什么一定要在注册表中声明Activity之后,我们来看下activity真正启动的地方。通过一开始的Activity启动图我们知道,调用最终会到达ActivityStackSupervisor的realStartActivityLocked方法。然后它调用了ApplicationThread的scheduleLaunchActivity方法,开始了真正的Activity对象创建以及启动过程。

ApplicationThread实际上是一个Binder对象,是app所在的进程与AMS坐在进程system_server通信的桥梁:
1.app进程会委托AMS进程完成Activity生命周期的管理以及任务栈的管理;这个通信过程AMS是server端,app进程通过持有AMS的client代理ActivityManagerNative完成通信过程;
2.AMS进程完成生命周期管理以及任务栈的管理后,会把控制权交还给app进程,让app进程完成Activity类对象的创建,以及生命周期的回调;这个通信过程也是通过Binder完成的。此时app为server端,它的Binder对象存在于ActivityThread的内部类ApplicationThread;AMS所在的client通过持有IApplicationThread的代理对象完成对于app进程的通信。

app进程内部的ApplicationThread与App的主线程并不在一个线程类,所以他们之间的通信需要通过Handler完成,这个Handler存在于ActivityThread中,它的名字叫做H。

ApplicationThread的 scheduleLaunchActivity方法,正式包装了参数最终使用Handler发送了一个消息,然后在ActivityThread中对消息进行处理:
[->ActivityThread.java]
ActivityThread#H#handleMessage

      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;

可以看到直接调用了handleLaunchActivity方法

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //.....省略代码

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();
        //执行启动Activity
        Activity a = performLaunchActivity(r, customIntent);
  }

  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
   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);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

          //第2步
          Application app = r.packageInfo.makeApplication(false, mInstrumentation);
 
  }

1.使用ClassLoader加载并通过反射创建Activity对象
2.如果Application还没有创建,那么创建Application对象并回调相应的生命周期方法

整个系统的Activity堆栈,Activity生命周期回调都是由AMS所在的系统进程system_server帮开发者完成的;Android的Framework层帮忙完成了诸如生命周期管理等繁琐复杂的过程,简化了应用层的开发。

启动插件中声明的Activity

通过上文的分析,我们已经了解了Activity的启动过程。之前我们说过,启动插件中的Activity会遇到的问题是必须在注册表中进行声明,我们也说了解决的思路是在注册表中声明替身Activity,然后在合适得到时候把这个假的替换成我们真正需要启动的Activity。
我们可以先启动一个已经存在在注册表中的替身Activity,让这个Activity今入AMS进程并接受检验;最终在创建Activity的时候替换成我们真正需要启动的Activity;这样就成功得到欺骗了AMS进程,完成了偷梁换柱的工作。

使用StubActivity绕过AMS

在启动Activity的控制权转交给AMS进程之前,我们需要想办法临时把TargetActivity替换成StubActivity,我们来看一下Virtualapk里面是如何做到的:

1.代理(hook)系统的Instrumentation
前面分析Activity的启动流程的时候,我们知道无论是通过Context还是Activity的startActivity方法最终都会通过Instrumentation的execStartActivity方法进行调用。
[->PluginManager.java]
PluginManager#hookInstrumentationAndHandler

private void hookInstrumentationAndHandler() {
        try {
            Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
            if (baseInstrumentation.getClass().getName().contains("lbe")) {
                // reject executing in paralell space, for example, lbe.
                System.exit(0);
            }
            //创建自定义Instrumentation 并注入到ActivityThread中
            final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
            Object activityThread = ReflectUtil.getActivityThread(this.mContext);
            ReflectUtil.setInstrumentation(activityThread, instrumentation);
            //将ActivityThread的H:Handler的mCallBack替换为自定义的CallBack
            ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
            this.mInstrumentation = instrumentation;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

它在PluginManager初始化的时候被调用,首先通过反射获取到了ActivityThread中的instrumentation,并通过VAInstrumentation对系统的Instrumentation进行了封装,其实VAInstrumentation就是系统Instrumentation的一个代理,然后这个VAInstrumentation 替换掉原来ActivityThread中的Instrumentation,VAInstrumentation 里重写了execStartActivity,realExecStartActivity等方法,为什么要重写这些方法,接下来我们一个个来分析。
首先是execStartActivity
[->VAInstrumentation.java]
VAInstrumentation#execStartActivity

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
 //将隐试启动插件里activity的意图转化为显示启动,因为在宿主通过隐式启动插件activity,是无法启动的,原因很简单,插件的activity并不在宿主apk中mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
        // null component is an implicitly intent
        if (intent.getComponent() != null) {
            Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(),
                    intent.getComponent().getClassName()));
            // resolve intent with Stub Activity if needed
            this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
        }

        ActivityResult result = realExecStartActivity(who, contextThread, token, target,
                    intent, requestCode, options);

        return result;
    }

VA的execStartActivity方法中主要做了以下这些事情:
1.将隐试启动插件里activity的意图转化为显示启动,因为在宿主通过隐式启动插件activity,是无法启动的,原因很简单,插件的activity并不在宿主apk中
mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
ComponentsHandler的transformIntentToExplicitAsNeeded做的就是这个事情,里面会通过mPluginManager.resolveActivity方法查找插件里面第一个符合隐式条件的第一个ResolveInfo,然后new component 设置进intent,大家可以追着代码进去看

public Intent transformIntentToExplicitAsNeeded(Intent intent) {
        ComponentName component = intent.getComponent();
        if (component == null) {
            ResolveInfo info = mPluginManager.resolveActivity(intent);
            if (info != null && info.activityInfo != null) {
                component = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
                intent.setComponent(component);
            }
        }

        return intent;
    }

2.临时把TargetActivity替换成StubActivity,我们前面说过,要进行偷梁换柱,也就是在这个地方

if (intent.getComponent() != null) {
            Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(),
                    intent.getComponent().getClassName()));
            // resolve intent with Stub Activity if needed
            this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
        }

/**
  *我们可以发现,会判断启动的Activity是不是插件里的,如果是,则将目标Activity的包名和TargetActivity的名字存储到intent中,接着通过dispatchStubActivity方法,根据要启动的TargetActivity是什么启动模式的来启动相应的代理StubActivity
  */
public void markIntentIfNeeded(Intent intent) {
        if (intent.getComponent() == null) {
            return;
        }

        String targetPackageName = intent.getComponent().getPackageName();
        String targetClassName = intent.getComponent().getClassName();
        // search map and return specific launchmode stub activity
        if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
            intent.putExtra(Constants.KEY_IS_PLUGIN, true);
            intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
            intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
            dispatchStubActivity(intent);
        }
    }
/**
 *dispatchStubActivity方法里面会通过TargetActivity是什么启动模式的来获取相应的代理StubActivity的类名并设置进intent中,这里就完成了偷梁换柱的过程。
 */
private void dispatchStubActivity(Intent intent) {
        ComponentName component = intent.getComponent();
        String targetClassName = intent.getComponent().getClassName();
        LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
        ActivityInfo info = loadedPlugin.getActivityInfo(component);
        if (info == null) {
            throw new RuntimeException("can not find " + component);
        }
        int launchMode = info.launchMode;
        Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
        themeObj.applyStyle(info.theme, true);
        String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
        Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]", targetClassName, stubActivity));
        intent.setClassName(mContext, stubActivity);
    }

3.调用系统Instrumentation的execStartActivity方法,进入正常启动Activity的流程

 ActivityResult result = realExecStartActivity(who, contextThread, token, target,
                    intent, requestCode, options);

private ActivityResult realExecStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ActivityResult result = null;
        try {
            Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class,
            int.class, Bundle.class};
            result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase,
                    "execStartActivity", parameterTypes,
                    who, contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

这里是通过反射调用系统Instrumentation的execStartActivity方法(因为api中标注了 {@hide}注释,属于不公开的api,只允许内部调用)
通过这个替换过程,在ActivityManagerNative的startActivity调用之后,system_server端收到Binder驱动的消息,开始执行ActivityManagerService里面真正的startActivity方法;这时候AMS看到的intent参数里面的组件已经是StubActivity了,因此可以成功绕过检查。

将StubActivity替换回TargetActivity

在上文中间TargetActivity替换为StubActivity以后,接下来就是跨进程通信,system_server管理Activity的生命周期,之前我们用替身StubActivity临时换了TargetActivity,肯定需要在合适的时候替换回来,Virtualapk替换的回来的地方是哪里呢?
在AMS进程里面我们是没有办法换回来的,因为我们不可能调用到其他进程的代码,因此我们要等AMS把控制权交给App所在进程,还记得前面我们说ApplicationThread所在的Binder线程池通过Handler与ActivityThread进行通信么?
那我们是不是可以拦截Handler的handleMessage时做处理呢?我们简单的看一下Handler的代码:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Handler类消息分发的过程:
1.如果传递的Message本身就有callback,那么直接使用Message对象的callback方法;
2.如果Handler类的成员变量mCallback存在,那么首先执行这个mCallback回调;
3.如果mCallback的回调返回true,那么表示消息已经成功处理;直接结束。
4.如果mCallback的回调返回false,那么表示消息没有处理完毕,会继续使用Handler类的handleMessage方法处理消息。

ActivityThread中的Handler类H重载了handleMessage方法:

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");
            ActivityClientRecord r = (ActivityClientRecord)msg.obj;

            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
        case RELAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
            ActivityClientRecord r = (ActivityClientRecord)msg.obj;
            handleRelaunchActivity(r);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

// 省略一万行代码。。。。
    }
}

从dispathMessage方法中,我们可以得出思路:我们可以拦截这一过程:把这个H类的mCallback替换为我们的自定义实现,这样dispathMessage就会首先使用这个自定义的mCallback,然后看情况使用H重载的handleMessage。
这个Handler.Callback是一个接口,我们可以使用动态代理或者普通代理完成Hook,在Virtualapk中使用普通的静态代理方式,还记得前面的VAInstrumentation类么?它不仅继承了系统的Instrumentation,同时还实现了 Handler.Callback 接口。
这里再次贴出前面PluginManager初始化的时候hookInstrumentationAndHandler的代码

private void hookInstrumentationAndHandler() {
        try {
            Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
            if (baseInstrumentation.getClass().getName().contains("lbe")) {
                // reject executing in paralell space, for example, lbe.
                System.exit(0);
            }

            final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
            Object activityThread = ReflectUtil.getActivityThread(this.mContext);
            ReflectUtil.setInstrumentation(activityThread, instrumentation);
            //HOOK HandlerCallback
            ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
            this.mInstrumentation = instrumentation;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

public static void setHandlerCallback(Context base, Handler.Callback callback) {
        try {
            Object activityThread = getActivityThread(base);
            Handler mainHandler = (Handler) ReflectUtil.invoke(activityThread.getClass(), activityThread, "getHandler", (Object[])null);
            ReflectUtil.setField(Handler.class, mainHandler, "mCallback", callback);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

果不其然,替换了ActivityThread中的Handler的mCallback为VAInstrumentation ,所以当ActivityThread的H进行dispathMessage的时候,必定会走到VAInstrumentation 的handleMessage方法,代码如下:
[->VAInstrumentation.java]

 @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == LAUNCH_ACTIVITY) {
            // ActivityClientRecord r
            Object r = msg.obj;
            try {
                Intent intent = (Intent) ReflectUtil.getField(r.getClass(), r, "intent");
                intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader());
                ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo");

                if (PluginUtil.isIntentFromPlugin(intent)) {
                    int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                    if (theme != 0) {
                        Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + "  after :0x" + Integer.toHexString(theme));
                        activityInfo.theme = theme;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return false;
    }

可以看到在VAInstrumentation 的handleMessage方法只是拦截了LAUNCH_ACTIVITY的处理,在里面将intent中的activityInfo.theme替换为插件的theme,并给intent设置了ClassLoader,这里为什么要设置一个ClassLoader?我想是因为在ActivityThread的performLaunchActivity方法会将其取出,然后设置进mInstrumentation.newActivity方法中
[->ActivityThread.java#performLaunchActivity]

 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);
            }
        } 

经过上面的调用,又会进入到VAInstrumentation 的newActivity

    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        try {
            cl.loadClass(className);
        } catch (ClassNotFoundException e) {
            LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
            String targetClassName = PluginUtil.getTargetActivity(intent);

            Log.i(TAG, String.format("newActivity[%s : %s]", className, targetClassName));

            if (targetClassName != null) {
                Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
                activity.setIntent(intent);
                try {
                    // for 4.1+
                    ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
                } catch (Exception ignored) {
                    // ignored.
                }
                return activity;
            }
        }
        return mBase.newActivity(cl, className, intent);
    }

首先是cl.loadClass(className),注意,我使用的demo,这里的className为com.didi.virtualapk.core.A$1,还有可能是其他的,但都会是在清单文件声明的stubActivity的名字


image.png

调用cl.loadClass(className)去加载这些类,肯定是会爆出ClassNotFoundException异常的,因为这些类并不存在,他们只是在清单文件中起到占坑的作用,用来欺骗系统的,这里的设计确实非常巧妙,接下来自然走到catch里,catch里自然是去构建真正需要加载的TargetActivity

Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);                
activity.setIntent(intent);

传入newActivity方法的是LoadedPlugin中的ClassLoader,这个ClassLoader已经是修改过的,可以加载插件和宿主里的类。关于classLoader,可以参考Android插件化学习之路(二)之ClassLoader完全解析
然后将intent设置进插件Activity 中,注意,这里的intent里的className还是com.didi.virtualapk.core.A$1
接下来会调用到ActivityThread中的performLaunchActivity 中的如下代码

if (r.isPersistable()) {
          mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
          mInstrumentation.callActivityOnCreate(activity, r.state);
   }

然后又进入到VAInstrumentation 的callActivityOnCreate方法中

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        final Intent intent = activity.getIntent();
        if (PluginUtil.isIntentFromPlugin(intent)) {
            Context base = activity.getBaseContext();
            try {
                LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
                ReflectUtil.setField(base.getClass(), base, "mResources", plugin.getResources());
                ReflectUtil.setField(ContextWrapper.class, activity, "mBase", plugin.getPluginContext());
                ReflectUtil.setField(Activity.class, activity, "mApplication", plugin.getApplication());
                ReflectUtil.setFieldNoException(ContextThemeWrapper.class, activity, "mBase", plugin.getPluginContext());

                // set screenOrientation
                ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
                if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
                    activity.setRequestedOrientation(activityInfo.screenOrientation);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        mBase.callActivityOnCreate(activity, icicle);
    }

最后这里就是进行一些替换的工作了,替换掉TargetActivity里的mResources,mBase,mApplication为LoadedPlugin中生成的可以用于加载插件资源的相应Resources和Context,为什么要进行这些替换工作?后续文章会进行详细讲解,最后就是调用系统Instrumentation的callActivityOnCreate其启动这个插件TargetActivity了,我们可以发现,到最后intent里的className还是com.didi.virtualapk.core.A$1,这是因为这个intent只是用来欺骗系统的作用。

 public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }

可以看见,接下来就是调用插件Activity的onCreate方法了,就完了加载插件Activity的过程。
你可能会问通过上面的方式启动的插件TargetActivity就具有生命周期了吗?答案是肯定的,大家可以通过demo去验证这一点,我们以onDestroy为例分析一下这个过程:
Activity的finish方法最终会通过ActivityManagerNative到AMS然后接着通过ApplicationThread到ActivityThread,然后通过H转发消息到ActivityThread的handleDestroyActivity,接着这个方法把任务交给performDestroyActivity完成。
[->ActivityThread.java#performDestroyActivity]
performDestroyActivity,关键代码如下:

ActivityClientRecord r = mActivities.get(token);
// ......
mInstrumentation.callActivityOnDestroy(r.activity);

通过mActivities拿到了一个ActivityClientRecord,然后直接把这个record里面的Activity交给Instrument类完成了onDestroy的调用。
这里的r.activity是StubActivity为什么它能正确完成对TargetActivity生命周期的回调呢?
答案是token。AMS与ActivityThread之间对于Activity的生命周期的交互,并没有直接使用Activity对象进行交互,而是使用一个token来标识,这个token是binder对象,因此可以方便地跨进程传递。Activity里面有一个成员变量mToken代表的就是它,token可以唯一地标识一个Activity对象,它在Activity的attach方法里面初始化;
在AMS处理Activity的任务栈的时候,使用这个token标记Activity,因此在demo里面,AMS进程里面的token对应的是StubActivity,但是在App进程里面,token对应的却是TargetActivity!因此,在ActivityThread执行回调的时候,能正确地回调到TargetActivity相应的方法。
为什么App进程里面,token对应的是TargetActivity呢?

ActivityClientRecord是在mActivities里面取出来的,确实是根据token取;那么这个token是什么时候添加进去的呢?我们看performLaunchActivity就完成明白了:它通过classloader加载了TargetActivity,然后完成一切操作之后把这个activity添加进了mActivities!另外,在这个方法里面我们还能看到对Ativity.attach方法的调用,它传递给了新创建的Activity一个token对象,而这个token是在ActivityClientRecord构造函数里面初始化的。

至此,整个Virtualapk框架对于插件Activity的管理就到此结束了,接下来还会继续分析其他组件的原理,最后,如果小弟有分析偏差的地方,欢迎矫正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容