Activity源码分析再分析

标题为什么叫源码分析再分析

看过几次源码,发现源码里面的东西太多了,如果不带着问题,或者没有重点的进行看的话,很容易就陷入了一个方法的调用之中,所以我决定借鉴一下其他人的分析,然后再加上自己的总结,如果有疑问我会在进行分析的过程中找一下对应的源码,自己理解一下。当然一定要附上原地址,这是对别人成果的一种肯定。感谢这位兄弟。

原地址:https://blog.csdn.net/lmq121210/article/details/82694781


Activity的启动的切入点

ActivityThread的main()启动Activity

App的启动流程:首先启动一个线程,然后通过AMS调度,进入到ActivityThred的main()方法

ActivityThread实际上就是我们常说的UI主线程

一个进程对应一个ActivityThread,用于调度Activity、Service的声明周期,Instrumentation的创建


看源码最好是从问题分析,要不还是那句话,很容易就跳进了(哎,这句是干啥的,哎,那句是干啥的。。。恭喜你,你已经迷失了)所以我总结了几个问题来分析:

  1. Application是如何创建的
  2. Application的onCreate()什么时候触发
  3. Activity是如何创建的
  4. Activity的onCreate()什么时候触发
  5. Activity的onStart()什么时候触发
  6. Activity的onResume什么时候触发

 

整体流程:

本来想画图表示的,但是没有想到用什么来画,如果用流程图的话后边的分支不好表述,所以直接采用文字概述的形式了,建议先看后边的源码分析再回过头来看这部分。

ActivityThread的main()
    执行looper的初始化:Looper.prepareMainLooper()
    实例化ActivityThread并调用其attach方法来启动Application和Activity
    获取UI主线程的Handler
    开始循环Looper():Looper.loop()
    会throw一个RuntimeException:looper的循环无法停止,如果停止就抛异常crash了
ActivityThread# thread.attach(false)
    通过入参进行不同的分支,都是启动Application
        当为false时走正常Application启动流程
        当为true时,表示从ActivityThread的systemMain中调用
    当为false时
        首先获取IActivityManager的代理类,即ActivityManagerService
            通过ActivityManager的getService方法获取,此方法通过一个单例模式的get方法返回,通过ServiceManager获取到                      ACTIVITY_SERVICE服务的binder,然后通过IActivityManager.Stub.asInterface(binder)返回AMS对象
        通过AMS调用其attachApplication方法
AMS的attachApplication
    调用自身的attachApplicationLocked()
AMS的attachApplicationLocked()
    调用IApplicationThread的代理对象(即ActivityThread的内部类ApplicationThread)的bindApplication()
    调用ActivityStackSupervisor的attachApplicationLocked方法来启动Activity

说明:在这里开始分支,attachApplictionLocked()方法中开始启动Application和Activity
Application的创建(黄色为Application)

ApplicationThread的bindApplication()
    通过入参对AppBindData对象进行初始化,此对象里面包含了Application启动的一些参数
    通过ActivityThread的Handler发消息
ActivityThread中的Handler的相关处理逻辑
    调用ActivityThread的handleBindApplication()
ActivityThread的handleBindApplication
    获取apk信息,包名,版本号等
    创建ContextImpl
    实例化LoadedApk的对象
    通过反射创建Instrumentation对象
    调用Instrumentation的init方法进行初始化其本身
    LoadedApk的makeApplication方法
    调用Instrumentation的callApplicationOnCreate(),直接调用了Application的onCreate,(调用Application的onCreate)
LoadedApk的makeApplication
    首先会判断mApplication对象是否为空,此处保证了一个应用只有一个Application对象
    调用Instrumentation的newApplication()创建Application
ActivityThread的mInstrumentation的newApplication()
    通过反射创建Application对象
    调用创建的Application对象的attach,进行初始化context和packageInfo
Activity的创建(绿色表示Activity,粉色为onCreate、onStart、onResume
ActivityStackSupervisor的attachApplicationLocked
    调用ActivityStackSupervisor的realStartActivityLocked()
    调用IApplictionThread的代理类(ApplicationThread)的scheduleLaunchActivity()
ApplicationThread的scheduleLaunchActivity
    通过入参对ActivityClientRecord对象初始化,用来创建Activity
    通过ActivityThread的Handler发消息
ActivityThread中的Handler的相关处理逻辑
    调用ActivityThread的HandleLaunchActivity()
ActivityThread的handleLaunchActivity
    调用自身的performLaunchActivity()
    调用自身的handleResumeActivity()
ActivityThread的performLaunchActivity()
    获取待启动的Activity的Component对象
    创建Activity的ContextImpl
    调用Instrumentation的newActivity,内部直接通过反射创建Activity
    获取Application对象
    创建Activity的配置,如label等
    创建Activity的window
    设置Activity的主题
    调用Instrumentation的callActivityOnCreate()
    调用Activity的performStart()

    调用Instrumentation的callActivityOnRestoreInstanceState()
Instrumentation的callActivityOnCreate()
    调用activity的performCreate()
Activity的performCreate()
    调用自身的onCreate()
    调用自身的performCreateCommon()如果存在fragment,此处初始化fragment
Activity的performStart()
    调用Instrumentation的callActivityOnStart()
    触发fragment的onStart()
Instrumentation的callActivityOnStart()
    触发Activity的onStart()
ActivityThread的handleResumeActivity()
    调用自身的performResumeActivity, 此时activity并未显示
    向window中添加DecorView

 


ActivityThread的main():

 public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    ...
    // Looper初始化
    Looper.prepareMainLooper();
    // 实例化ActivityThread
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    // 获取到ui线程的handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    ...
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // looper开始循环
    Looper.loop();
    // 如果停止looper的循环则报错
    throw new RuntimeException("Main thread loop unexpectedly exited");
    }

ActivityThread# thread.attach(false):

private void attach(boolean system) {
    ...
    if (!system) {
        ...
        // 获取IActivityManager的代理类,即ActivityManagerService
        final IActivityManager mgr = ActivityManager.getService();
        try {
            // 创建Application
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        ...
    } else {
        // 这个分支也是直接创建Application,而且流程更为简单,但是没有找到是什么情况下才会走
        // 在ActivityThread的systemMain()中调用,暂时不关注
        // Don't set application object here -- if the system crashes,
        // we can't display an alert, we just want to die die die.
        android.ddm.DdmHandleAppName.setAppName("system_process",
                UserHandle.myUserId());
        try {
            mInstrumentation = new Instrumentation();
            ContextImpl context = ContextImpl.createAppContext(
                    this, getSystemContext().mPackageInfo);
            mInitialApplication = context.mPackageInfo.makeApplication(true, null);
            mInitialApplication.onCreate();
        } catch (Exception e) {
            throw new RuntimeException(
                    "Unable to instantiate Application():" + e.toString(), e);
        }
    }

    ...
}

ActivityManager# activityManager.getService():

// 获取IActivityManagerSingleTon的实例
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}
// 单例模式,未看到get(),猜测是get()直接返回create()的对象
private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

ActivityManagerService# mgr.attachApplication(mAppThread);

public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        ...
        // 调用自己(ActivityManagerService)创建Application
        attachApplicationLocked(thread, callingPid);
        ...
    }
}

ActivityManagerService# attachApplicationLocked(thread, callingPid)

    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
        ...
        try {
            ...
            if (app.instr != null) {
                // thread是IApplicationThread的代理对象, 实际上是ActivityThread的内部类ApplicationThread
                thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            } else {
                // thread是IApplicationThread的代理对象, 实际上是ActivityThread的内部类ApplicationThread
                thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            }
            ...
        } catch (Exception e) {
            ...
            return false;
        }

        ...

        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                // 这里面就要启动Activity了
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }
        ...
        return true;
    }

 ActivityThread#ApplicationThread# thread.bindApplication(...) 

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,
        ProfilerInfo profilerInfo, Bundle instrumentationArgs,
        IInstrumentationWatcher instrumentationWatcher,
        IUiAutomationConnection instrumentationUiConnection, int debugMode,
        boolean enableBinderTracking, boolean trackAllocation,
        boolean isRestrictedBackupMode, boolean persistent, Configuration config,
        CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
        String buildSerial) {

    ...

    AppBindData data = new AppBindData();
    data.processName = processName;
    // 初始化传入的数据
    ...
    data.buildSerial = buildSerial;
    // 通过ActivityThread的handler 发送消息
    sendMessage(H.BIND_APPLICATION, data);
}

ActivityThread# Handler的处理信息的逻辑:


private class H extends Handler {
    ...
    public static final int BIND_APPLICATION        = 110;
    ...

    ...

    public void handleMessage(Message msg) {
        ...
        switch (msg.what) {
            ...
            case BIND_APPLICATION:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            ...
        }
        ...
    }

}

ActivityThread# handleBindApplication(data):

    private void handleBindApplication(AppBindData data) {
        ...
        // 获取Apk信息,包名、版本号等。
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
        ...
        // 创建ContextImpl
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        updateLocaleListFromAppContext(appContext,
                mResourcesManager.getConfiguration().getLocales());
        ...
        // Continue loading instrumentation.
        if (ii != null) {
            ...
            // 获取App信息
            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            // 前面已经创建了,这里为什么又创建一个
            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

            try {
                // 反射创建Instrumentation对象,后续很重要,实际上Application和Activity就是它创建的
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            // 初始化Instrumentation对象
            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
            }
        } else {
            mInstrumentation = new Instrumentation();
        }
        ...
        try {
            // 创建Application
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
            ...
            try {
                // 触发Application创建完成的onCreate()方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                ...
            }
        } finally {
            ...
        }
    }

LoadedApk# makeApplication(data.restructedBackupMode, null):

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    // 这里添加限制,如果app已经创建则直接返回,这个就是为什么app只有一个Application对象的原因
    if (mApplication != null) {
        return mApplication;
    }
    ...
    try {
        ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // 调用ActivityThread的Instrumentation来创建Application
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        ...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            // 触发app创建完成的onCreate()方法
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            ...
        }
    }
    return app;
}

Instrumentation#  mActivityThread.mInstrumentation.newApplication():

static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    // 通过反射创建Application对象
    Application app = (Application)clazz.newInstance();
    app.attach(context);
    return app;
}

 Instrumentation# instrumentation.callApplicationOnCreate(app); 这里面就会触发Application的onCreate方法

public void callApplicationOnCreate(Application app) {
    app.onCreate();
}

 ActivityStackSupervisor#  mStackSupervisor.attachApplicationLocked(app)

boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
    ...
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        ...
        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
            ...
            ActivityRecord hr = stack.topRunningActivityLocked();
            if (hr != null) {
                if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                        && processName.equals(hr.processName)) {
                    try {
                        // 真正启动Activity的地方
                        if (realStartActivityLocked(hr, app, true, true)) {
                            didSomething = true;
                        }
                    } catch (RemoteException e) {
                        ...
                    }
                }
            }
        }
    }
    ...
    return didSomething;
}

  ActivityStackSupervisor#  realStartActivityLocked(hr, app, true, true):

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
    ...
    try {
        ...
        // binder机制,调用的是IApplicationThread的代理对象ApplicationThread
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                System.identityHashCode(r), r.info,
                // TODO: Have this take the merged configuration instead of separate global and
                // override configs.
                mergedConfiguration.getGlobalConfiguration(),
                mergedConfiguration.getOverrideConfiguration(), r.compat,
                r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                r.persistentState, results, newIntents, !andResume,
                mService.isNextTransitionForward(), profilerInfo);
        ...
        }

    } catch (RemoteException e) {
        ...
    }
    ...
    return true;
}

ActivityThread#ApplicationThread  app.thread.scheduleLaunchActivity(...):

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    ...
    ActivityClientRecord r = new ActivityClientRecord();
    r.token = token;
    // 初始化Record中数据
    ...
    r.overrideConfig = overrideConfig;
    ...
    // 给ActivityThread的handler发消息
    sendMessage(H.LAUNCH_ACTIVITY, r);
}

ActivityThread#handler 处理H.LAUNCH_ACTIVITY的逻辑:

case LAUNCH_ACTIVITY: {
    ...
    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
    ...
} break;

ActivityThread# handleLaunchActivity:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
    // 启动activity
    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        ...
        // 处理Activity的onResume
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        ...
    } else {
        // If there was an error, for any reason, tell the activity manager to stop us.
        try {
            ActivityManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

ActivityThread# performLaunchActivity(r, customIntent):

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    // 获取待启动的Activity的Component对象
    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
            mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }
    // 创建Activity的ContextImpl
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 调用Instrumentation的newActivity,内部直接通过反射创建activity对象
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        ...
        }
    } catch (Exception e) {
        ...
    }

    try {
        // 获取Application对象
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (activity != null) {
            // 创建Activity的配置,如label等
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (r.overrideConfig != null) {
                config.updateFrom(r.overrideConfig);
            }
            // 创建Activity的window
            Window window = null;
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);
            // 设置Activity的主题的参数
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);

            ...
            // 设置Activity的主题
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }
            ...
            // 调用Activity的onCreate方法
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            ...
            // 调用Activity的onStart方法
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            // 调用Activity的OnRestoreInstanceState方法()
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            ...
        }
        ...
        mActivities.put(r.token, r);

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        ...
    }

    return activity;
}

Instrumentation# callActivityOnCreate(activity, r.state, r.persistentState):

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

Activity# performCreate(icicle, persistentState):

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    ...
    // 调用Activity的onCreate方法了
    onCreate(icicle, persistentState);
    ...
    // 这里面就开始如果有fragment就开始初始化fragment了
    performCreateCommon();
}

 Activity# activity.performStart():

final void performStart() {
    ...
    // 触发Activity的onStart方法,里面直接调用了Activity的onStart()
    mInstrumentation.callActivityOnStart(this);
    ...
    // 触发fragment的onStart的地方
    mFragments.dispatchStart();

}

ActivityThread# handleResumeActivity():

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ...
    // 触发Activity的onResume(),但此时Activity并没有Resume
    // TODO Push resumeArgs into the activity for consideration
    r = performResumeActivity(token, clearHide, reason);

    if (r != null) {
        ...
        // If the window hasn't yet been added to the window manager,
        // and this guy didn't finish itself or start another activity,
        // then go ahead and add the window.
        if (!willBeVisible) {
            ...
        }
        if (r.window == null && !a.mFinished && willBeVisible) {
            ...
            if (r.mPreserveWindow) {
                ...
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    // 向window中添加DecorView
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }
        } else if (!willBeVisible) {
            ...
        }
        // The window is now visible if it has been added, we are not
        // simply finishing, and we are not starting another activity.
        if (!r.activity.mFinished && willBeVisible
                && r.activity.mDecor != null && !r.hideForNow) {
            if (r.newConfig != null) {
                // Activity的OnConfigurationChanged触发的地方
                performConfigurationChangedForActivity(r, r.newConfig);
                ...
            }
            ...
        }

        // Tell the activity manager we have resumed.
        if (reallyResume) {
            try {
                ActivityManager.getService().activityResumed(token);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    } else {
        ...
    }
}


    

 

startActivity启动Activity

可以直接看https://blog.csdn.net/lmq121210/article/details/82705952

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值