美文网首页
Activity启动过程剖析

Activity启动过程剖析

作者: 唯夜 | 来源:发表于2016-10-25 12:21 被阅读148次

    Activity启动过程剖析

    你同样可以在Github上看到这篇文章:
    https://github.com/onlynight/ActivityStartPrinciple

    写在前面

    在看这篇文章之前你需要了解android的IPC通信机制里面的ADIL的原理,还有一些常用的设计模式比如代理模式你也需要有所了解,了解这些会让你更容易理解android源码。阅读本文的读者我都默认你是了解这些东西的,如果你正好还没没有看过这些东西,那么我想你推荐我的另外几篇文章,希望能让你快速了解android的IPC的AIDL的设计原理:

    下面我们开始正片。

    阅读源码的方法

    Android源码数量相当多我们不能从头逐一查看所有文件中的源码,你需要理出一条线索来,你感兴趣的线索比如本文讲的Activity的启动过程分析就是从startActivity函数开始然后逐步深入去查看其中的奥妙。还有一点需要说的是看源码的时候不要太深入细节,我们想了解的代码的逻辑而不是其中如何实现的(当然这个是我看源码的主要目的,如果你的出发点是如何实现的那我们关注的重点就不一样了),梳理代码逻辑让我们能更好的了解android的工作原理,从而在遇到问题的时候不再抓虾而能精准的定位到问题的所在,希望这篇文章能够帮到你。这里同样有一篇文章推荐给你:Android源码阅读配置

    需要了解的几个类

    开始之前你需要对几个类有所了解知道他们的职责这样有助于理解源码:

    1.Activity

    An activity is a single, focused thing that the user can do. Almost all
    activities interact with the user, so the Activity class takes care of
    creating a window for you in which you can place your UI with
    {@link #setContentView}. While activities are often presented to the user
    as full-screen windows, they can also be used in other ways: as floating
    windows (via a theme with {@link android.R.attr#windowIsFloating} set)
    or embedded inside of another activity (using {@link ActivityGroup}).

    Activity是我们日常中最常用的组件,它通常显示给用户的是一个全屏的窗口,或者一个浮动的窗口(例如音乐播放器的浮动歌词),或者嵌入其他的Activity使用ActivityGroup。它Android四大组件中唯一能和用户交互的组件,这里就不再多做解释了。

    2.Instrumentation

    Base class for implementing application instrumentation code. When running
    with instrumentation turned on, this class will be instantiated for you
    before any of the application code, allowing you to monitor all of the
    interaction the system has with the application. An Instrumentation
    implementation is described to the system through an AndroidManifest.xml's
    instrumentation tag.

    当Instrumentation打开的时候,在你的应用代码执行之前会首先实例化一个instrumentation,你可以监听所有系统应用交互。
    用于执行具体操作的类,辅助Activity的监控和测试。

    3.ActivityManagerNative、ActivityManagerProxy、IActivityManager、ActivityManagerService

    IPC需要的类,了解过aidl的童鞋应该都不陌生。需要有一个继承IInterface的接口定义ipc的能力接口,需要有个继承Binder类的驱动类,还需要有一个远程服务的代理类Proxy代理远程服务的操作,实现Binder的类可能是个抽象类,还需要有类实例化这个Binder或者继承它实现所有抽象方法。

    1. IActivityManager就是继承了IInterface的能力接口。
    2. ActivityManagerNative继承了Binder实现了IInterface接口。
    3. ActivityManagerProxy有一个mRemote其类型是Binder,远程服务代理。
    4. ActivityManagerService继承了ActivityManagerNative并且实现了它的所有抽象方法。

    类结构图如下:

    类关系图

    源码逻辑梳理

    首先我们来看startActivity这个函数:

    // Activity.java
    
    
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }
    
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
                @Nullable Bundle options) {
        if (mParent == null) {
            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());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }
    
            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }
    

    可以看到startActivity函数调用的还是startActivityForResult只是传入的默认requestCode为-1,这里就提醒我们requestCode需要大于等于0,否则和直接调用startActivity是一样的效果不会回调onActivityResult

    startActivityForResult出现分支判断是否有mParentmParent即为ActivityGroup,实际上两个分支执行的代码是相同的只是是谁启动activity的区别,startActivityFromChild这个函数可以验证这个说法:

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

    可以看到这个函数的内部实现和startActivityForResultmParent==null的分支代码基本相同只是传入的embeddedID不同,下面我们顺着主线继续往往下看,startActivityForResult中调用了Instrumention.execStartActivity方法:

    //Instrumention.java
    
    public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
    

    这里就有一些比较复杂的代码,本着研究的心理总要把代码是如何实现的看个清楚,其实不然,你大可根据变量和方法的命名大概知道是什么作用就可以继续看代码了,例如这个mActivityMonitors我们看到它的类型是List<ActivityMonitor>,通过名字和类型我们就大概知道它是用来记录Activity的,大概分析下这里应该不是真正启动Activity的地方这里应该是请求activity返回的检测。继续往下看,这里调用了ActivityManagerNative.startActivity方法,我们继续深入:

    //ActivityManagerNative.java
    
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
    
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
    
        return new ActivityManagerProxy(obj);
    }
    
    
    class ActivityManagerProxy implements IActivityManager
    {
        private IBiner mRemote;
    
        public ActivityManagerProxy(IBinder remote)
        {
            mRemote = remote;
        }
    
        public IBinder asBinder()
        {
            return mRemote;
        }
    
        public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
                String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            data.writeString(callingPackage);
            intent.writeToParcel(data, 0);
            data.writeString(resolvedType);
            data.writeStrongBinder(resultTo);
            data.writeString(resultWho);
            data.writeInt(requestCode);
            data.writeInt(startFlags);
            if (profilerInfo != null) {
                data.writeInt(1);
                profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                data.writeInt(0);
            }
            if (options != null) {
                data.writeInt(1);
                options.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
            reply.readException();
            int result = reply.readInt();
            reply.recycle();
            data.recycle();
            return result;
        }
    
        ...
    }
    

    这里看到ActivityManagerNative.getDefault中的gDefault是一个单例其中的Binder就是我们要的远程Binder了,ServiceManager.getService获取到的Binder,这里我们先放放先不看ServiceManager的内部实现,只要我们知道这个远程Binder是从这里获取到的就可以了。拿到这个Binder后通过ActivityManagerNative.asInterface方法将其强转为IActivityManager接口:

    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
    
        return new ActivityManagerProxy(obj);
    }
    

    如果这个Binder在本地能找到那么就直接返回,如果找不到就通过Binder创建一个远程服务的代理类。ActivityManagerProxy将传入的IBinder作为远程服务的Binder,可以看到ActivityManagerProxy.startActivity方法的本质实际上是调用了mRemote.transact方法,请求远程服务完成操作。懂AIDL的朋友都知道transactonTransact是成对出现的,onTransact才是真正的实现。那么问题来了这个onTransact实在哪里实现的呢,当然是ActivityManagerProxy.mRemote已经实现了,现在我们再倒回来找找这个mRemote是从哪里来的。

    //ServiceManager.java
    
    /**
     * Returns a reference to a service with the given name.
     * 
     * @param name the name of the service to get
     * @return a reference to the service, or <code>null</code> if the service doesn't exist
     */
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
    
    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }
    
        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }
    

    看到这些熟悉的命名方式ServiceManagerIServiceManagerServiceManagerNative,那么这个ServiceManager也是AIDL实现,我们再来看下这个AIDL的远程代理从哪里来的,这里没有gDefault但是有个静态方法getIServiceManager来获取IInterface接口,继续往里深入:

    //BinderInternal.java
    
    /**
     * Return the global "context object" of the system.  This is usually
     * an implementation of IServiceManager, which you can use to find
     * other services.
     */
    public static final native IBinder getContextObject();
    
    //ServiceManagerNative.java
    
    /**
     * Native implementation of the service manager.  Most clients will only
     * care about getDefault() and possibly asInterface().
     * @hide
     */
    public abstract class ServiceManagerNative extends Binder implements IServiceManager{}
    

    这里获取IBinder是通过native方式来实现的,这里我们就不再深入了。我们知道ServiceManager是用来获取其他service的接口那么它的实现必然跟普通的service有所不同,再回到我们之前的方法ServiceManager.getService("activity"),中通过activity参数获取到了一个Binder这个Binder必然是已经继承自Binder并且实现了IActivityManager接口的类,这样的类我们现在只能定位到ActivityManagerNative这个类,但是这个类是个抽象类它并没有实现IActivityManager中的方法,我们再全局搜索一下ActivityManagerNative这个类看有没有实例化这个类的地方或者继承这个类的分抽象类,很幸运我们找到了ActivityManagerService这个类继承了ActivityManagerNative并且实现了接口的方法。

    ServiceManager中有个sCache其类型是HashMap<String, IBinder>,既然有getService那么会有相应的addService将service加入到其中:

    //ServiceManager.java
    
    private HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
    
    /**
     * Returns a reference to a service with the given name.
     * 
     * @param name the name of the service to get
     * @return a reference to the service, or <code>null</code> if the service doesn't exist
     */
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
    
    
    /**
     * Returns a reference to a service with the given name.
     * 
     * @param name the name of the service to get
     * @return a reference to the service, or <code>null</code> if the service doesn't exist
     */
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
    

    好了我们再来看看activity这个service是怎么被添加到里面去的,在ActivityManagerService中找到了相应的代码:

    // ActivityManagerService.java
    
    
    public void setSystemProcess() {
        try {
            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));
    
            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS);
            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
    
            synchronized (this) {
                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
                app.persistent = true;
                app.pid = MY_PID;
                app.maxAdj = ProcessList.SYSTEM_ADJ;
                app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                mProcessNames.put(app.processName, app.uid, app);
                synchronized (mPidsSelfLocked) {
                    mPidsSelfLocked.put(app.pid, app);
                }
                updateLruProcessLocked(app, false, null);
                updateOomAdjLocked();
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }
    

    这个setSystemProcess方法在系统启动的时候会被调用,那么ActivityManagerService就被添加到ServiceManager中去了,这里可以看出ActivityManagerService是一个系统级的Service在系统启动的时候就被加载了。

    找到来了ActivityManagerNative.gDefault中的源头我们再回过头来理顺Activity的启动过程,我们知道transact后的真正执行者是onTransact方法,那么我们这里的onTransact方法就是ActivityManagerService.onTransact实际就是ActivityManagerNative.onTransact

    // ActivityManagerNative.java
    
    /** {@hide} */
    public abstract class ActivityManagerNative extends Binder implements IActivityManager {
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            switch (code) {
                case START_ACTIVITY_TRANSACTION: {
                    data.enforceInterface(IActivityManager.descriptor);
                    IBinder b = data.readStrongBinder();
                    IApplicationThread app = ApplicationThreadNative.asInterface(b);
                    String callingPackage = data.readString();
                    Intent intent = Intent.CREATOR.createFromParcel(data);
                    String resolvedType = data.readString();
                    IBinder resultTo = data.readStrongBinder();
                    String resultWho = data.readString();
                    int requestCode = data.readInt();
                    int startFlags = data.readInt();
                    ProfilerInfo profilerInfo = data.readInt() != 0
                            ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
                    Bundle options = data.readInt() != 0
                            ? Bundle.CREATOR.createFromParcel(data) : null;
                    int result = startActivity(app, callingPackage, intent, resolvedType,
                            resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
                    reply.writeNoException();
                    reply.writeInt(result);
                    return true;
                }
            }
        }
    }
    
    
    class ActivityManagerProxy implements IActivityManager
    {
        public ActivityManagerProxy(IBinder remote)
        {
            mRemote = remote;
        }
    
        public IBinder asBinder()
        {
            return mRemote;
        }
    
        public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
                String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            data.writeString(callingPackage);
            intent.writeToParcel(data, 0);
            data.writeString(resolvedType);
            data.writeStrongBinder(resultTo);
            data.writeString(resultWho);
            data.writeInt(requestCode);
            data.writeInt(startFlags);
            if (profilerInfo != null) {
                data.writeInt(1);
                profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                data.writeInt(0);
            }
            if (options != null) {
                data.writeInt(1);
                options.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
            reply.readException();
            int result = reply.readInt();
            reply.recycle();
            data.recycle();
            return result;
        }
    }
    

    ActivityManagerNative中的mRemote.transact发送了一个START_ACTIVITY_TRANSACTION消息,接着onTransact方法真正将方法最终落地调用了startActivity方法,由于ActivityManagerNative是抽象类,那么就要查看它子类中的这个方法:

    //ActivityManagerService.java
    
    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());
    }
    
    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) {
        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false, ALLOW_FULL_ONLY, "startActivity", null);
        // 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);
    }
    

    继续深入查看mActivityStarter.startActivityMayWait:

    // ActivityStarter.java
    
    /**
     * Controller for interpreting how and then launching activities.
     *
     * This class collects all the logic for determining how an intent and flags should be turned into
     * an activity and associated task and stack.
     */
    class ActivityStarter {
    
        ...
    
        final int startActivityMayWait(IApplicationThread caller, int callingUid,
                String callingPackage, Intent intent, String resolvedType,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                IBinder resultTo, String resultWho, int requestCode, int startFlags,
                ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
                Bundle bOptions, boolean ignoreTargetSecurity, int userId,
                IActivityContainer iContainer, TaskRecord inTask) {
            
            ...
            //化繁为简,省略的这些代码就是为了给startActivityLocked构造参数
    
            final ActivityRecord[] outRecord = new ActivityRecord[1];
            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                    aInfo, rInfo, voiceSession, voiceInteractor,
                    resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                    options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                    inTask);
    
            ...
        }
    
    
        final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
                String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
                String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
                ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
                ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
                TaskRecord inTask) {
            ...
            try {
                mService.mWindowManager.deferSurfaceLayout();
                err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                        true, options, inTask);
            } finally {
                mService.mWindowManager.continueSurfaceLayout();
            }
            ...
        }
    
        private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
            ...
            mWindowManager.executeAppTransition();
            ...
        }
        ...
    }
    

    我们就深入到这里就好了不再深入研究代码,mWindowManager的类型是WindowManagerService也是一个aidl的实现,具体实现有兴趣的读者可以深入研究WindowManagerService的内部实现。

    本文实际上就是详细讲了ActivityManagerService是如何与前端链接上的,Android中许多其他的服务例如MediaCenter等都是IPC的实现,可以根据本文的的线索和思路查看相应的源码。在梳理完相应的代码逻辑以后就可以关注内部的代码实现了,最后希望本文能够真正帮到你理解Activity的启动过程。

    相关文章

      网友评论

          本文标题:Activity启动过程剖析

          本文链接:https://www.haomeiwen.com/subject/gieruttx.html