Android面试题集锦(二)

2016.7.22更新...........................................................................

(33):Activity间通过Intent传递数据大小有没有限制?

        有的,大小为40k;

(34):Android中可以从主界面点击图标进入程序,也可以从一个程序中跳转过去,两者有何区别呢?

        从主界面点击图标进去,默认开启的是<action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" />的Activity,但是从程序跳转到的Activity可以是任意的,只要你的Intent匹配了某一Activity,这个Activity就会被显示出来的;也就是说通过主界面图标启动的话,相对比较单一,只能启动指定启动的那个Activity,程序跳转启动的话更加灵活,只要满足的匹配条件就可以启动,但他们本质上是相同的;

(35):Android中的5大布局

        RelativeLayout(相对布局)、LinearLayout(线性布局)、FrameLayout(帧布局)、AbsoluteLayout(绝对布局)、TableLayout(表格布局)

(36):同一个应用中的不同Activity可以放到不同的Task任务栈中么?

        可以的,Activity中的android:taskAffinity属性就是为我们的当前Activity指定宿主栈也就是任务栈的,通常情况下如果我们不设定的话,属性值默认就是Manifest包名,我们可以在程序中通过Intent设置将要跳转的Activity所属的任务栈,具体来说就是让Intent包含FLAG_ACTIVITY_NEW_TASK标记;

(37):DVM进程、Linux进程和应用程序进程是同一概念吗?

        DVM进程是Dalvik虚拟机进程,他是Linux中的一个进程,所以DVM进程和Linux进程是一个进程;每个Android应用程序都运行在自己的进程中,而应用程序的这个进程是DVM分配出来的,确切的讲他拥有的是DVM虚拟机的实例,每来一个应用程序都会通过DVM启动时候开启的Zygote进程fork一个DVM实例也就是子进程出来给了新的应用程序,系统会给他分配一个单独的Linux UID,因而可以认为他们是同一个概念;从这里也可以看出来,每个应用实际上是作为一个独立的Linux进程存在的,可以防止虚拟机奔溃的情况下所有程序都关闭;

(38):DVM和JVM的简单区别

        (1):Davlik是基于寄存器的,他将局部变量放在65536个可用寄存器中的任何一个,而JVM是基于栈的,他把局部变量放到栈中;
        (2):java虚拟机运行的是java字节码,Dalvik虚拟机运行的是其专有的文件格式Dex(Dalvik Executable);
        (3):DVM主要负责的是进程隔离和线程隔离,因为每个应用程序在底层都会对应一个Dalvik虚拟机实例;
        (4):因为所有的Android应用的线程都对应一个Linux线程,因此DVM可以借助操作系统的线程调度和管理机制;
        (5):对于每个应用程序的虚拟机实例是怎么得到的呢?是通过称为孵化器的Zygote特殊虚拟机进程完成的,他再系统启动的时候被创建,当我们开启新的应用程序的时候会通过他来fork一个新的虚拟机实例出来分给新的应用程序;

(39):Activity之间传递数据的方式

        (1):使用Bundle封装数据,通过Intent传递

 Bundle bundle = new Bundle();//创建Bundle
				Book book = new Book("Android开发艺术探索", 79);
				bundle.putParcelable("book", book);
				Intent intent = new Intent(MainActivity.this, OtherActivity.class);//创建Intent对象
				intent.putExtras(bundle);//将携带有数据的Bundle对象附加到Intent里面
				startActivity(intent);
        通过将需要传递的数据封装到Bundle里面,随后将该Bundle添加到Intent里面就可以发送出去了,有一点需要注意的是封装到Bundle中的数据必须是可以序列化的对象,也就是必须是基本数据类型或者实现了Serialiable或者实现了Parcelable接口的对象才可以;

        (2):当然你可以直接使用Intent的一系列put方法进行数据传递,不需要Bundle进行封装;

        (3):使用Intent传递对象有个缺点就是对象必须是可以序列化的,如果遇到不可以序列化的对象该怎么办呢?解决方法就是使用静态变量的方式来传递;

        (4):通过Linux提供的剪切板来进行传递,一个Activity获取剪切板对象之后往剪切板里面写入数据,另一个Activity获取剪切板对象从里面读出数据;

        (5):通过自定义继承自Application的MyApplication对象,并且在该对象里面写需要传递的数据,这相当于是通过全局变量的方式进行传递了,因为整个系统中只有一个MyApplication对象,为了通过这种方式实现传递,需要在Manifest中的<application>标签下设置我们自定义的Application是该应用的Application,具体做法是加入:android:name = ".MyApplication";

2016.7.23更新...........................................................................

(40):谈谈你对Context的认识

        我们知道Android程序和java程序最大的区别就在于,在java中,你只需要一个main函数作为入口就可以跑起来了,Android程序是需要一个完整的工程环境的,并且Activity、Service、Broadcast等系统组件并不是像普通的java对象一样随便new一下就能创建实例的,而是需要各自的运行环境上下文,即这里的Context,可以这么讲,Context是维持Android程序中个组件能够正常工作的核心功能类,包含了应用程序的环境信息,通过他我们可以获得应用程序的资源和类,Context的继承结构图是这样的:

                      

                                                      图片来自于:http://write.blog.csdn.net/postedit/51992250

        Context是抽象类,有两个类直接继承自他:ContextWrapper以及ContextImpl,从名字上可以看出ContextWrapper是上下文功能的封装类,而ContextImpl是上下文功能的实现类,也就是说关于Context内方法的真正实现都是在ContextImpl里面了,而ContextWrapper又有三个实现类,Application/Service/ContextThremeWrapper,而我们常见的Activity又是ContextThemeWrapper的子类,也就是Application/Service/Activity都是ContextWrapper的子类了;一个应用程序中Context的数量 = Application的数量+Service的数量+Activity的数量,又因为一个应用程序中Application只有一个,所以实际上应用程序中Context的数量 = Service的数量+Activity的数量+1;

        通常我们可以在Activity或者Service中通过getApplication()获得Application实例,但是在BroadcastReceiver中却不行,原因在于getApplication方法是Activity和Service自己添加的,并不是Context中本身就有的,如果我们想在BroadcastReceiber中获得Application实例的话,可以通过getApplicationContext方法,也就是说getApplication的适用范围不如getApplicationContext;

        使用Application的时候需要注意的是,它本身已经是全局唯一的了,这点是由系统来保证的,如果我们想要自己实现一个类似于单例模式的Application类,不仅没必要,而且经常会出错,原因在于你写单例的时候通常是将构造函数写成private类型,并且对外提供一个public方法来获得这个实例,而实例的获得是通过new的方式实现的,前面已经说过Android中并不能随便new一个上下文对象的子类就能抛弃程序来,他是需要Android环境支撑的,你只是new了一个实例出来,但这个实例其实是跟Android没什么关系的,而系统在创建Application的时候是会有当前环境信息的,这点需要注意一下; 

(41):Android中Service和Thread的区别

        (1):Service是Android四大组件之一,创建他是依赖于Android环境的,并不是随随便便new一个就可以,默认情况下,Local Service是运行在主线程中的,而Remote Service是运行在独立进程的主线程中的,因此不能在Service中进行一些耗时操作,要想在Service中做些耗时操作只能在它里面创建线程实现;而Thread并不是Android所独有的,java中本身就有,他并不是运行在主线程中的;

        (2):Service的优先级较高,系统在资源不足的时候首先杀死的是Activity以及Thread,随后才可能杀死Service;

        两者根本不是一个级别的东西,Service是四大组件,而Thread仅仅就是用来处理异步任务的类而已,它可以在Application/Activity/Service中创建;

        在使用Thread的时候,我们一般是在Application/Activity/Service里面创建,在他们生命周期结束的时候如果我们不需要这个Thread的话,要记住记得将线程停止掉,因为如果不在当前环境下停止的话,这个Thread将没法停止;

(42):Activity中onSaveInstanceState被执行的场景有哪些呢?

        (1):当用户按下HOME键的时候

        (2):长按HOME键开启别的应用程序时

        (3):锁屏时

        (4):从一个Activity切换到新的Activity时

        (5):屏幕方向发生切换时

(43):Service和Activity之间怎么通信?

        (1):通过Binder来实现,虽然Binder在绝大部分情况下是用于进程间通信的,但是他也同样可以用于进程内部的通信,为了通过Binder实现Service与Activity之间的通信,我们需要做一下几件事:

        首先,在Service内部创建一个继承自Binder的内部类,并且通过onBind方法返回这个Binder类的实例;接着在Activity中创建一个ServiceConnection的匿名内部类,并且重写里面的onServiceConnected以及onServiceDisconnected方法,在Activity中调用了bindService方法之后,我们便可以在onServiceConnected方法中获取到Service端onBind方法返回的Binder对象了,接着我们对这个Binder对象进行向下转型,得到我们自定义类型的那个Binder实例,有了这个实例便可以调用实例里面的方法进行适当的操作了;

        (2):通过Broadcast来实现,在当Service端需要和Activity进行通信的时候,发出一条广播,我们在Activity端注册该广播便可以接收到发出的广播内容,从而进行界面视图方面的变化了;

2016.7.24更新...........................................................................

(44):IntentService和Service的区别

        (1):Service是运行在主线程中的,因此不能在他里面做耗时操作,否则可能出现ANR异常;而IntentService默认里面会创建一个HnadlerThread类型的子线程来处理Intent请求,该子线程内部存在MessageQueue消息队列以及Looper对象;

        (2):IntentService在我们的任务执行结束之后会自动调用stopSelf结束掉,但是Service需要我们手动结束;

        (3):IntentService对Intent请求的处理是串行执行的,因为每来一个Intent请求只是放到IntentService内部的MessageQueue队列里面的,每次Looper从里面取出来一个执行,直到MessageQueue中不存在消息为止,便结束IntentService;

(45):造成内存泄漏的原因有哪些?

        (1):类的静态变量持有大数据对象

        静态变量长期持有大数据对象的应用,阻止垃圾回收,因为静态变量的生命周期是整个应用程序;

        (2):非静态内部类或者匿名内部类中存在引用外部类的静态实例

        非静态内部类或者匿名内部类中的静态变量持有一个外部类实例的引用,会长期维持着外部类的引用,阻止被回收掉,原因在于非静态内部类或者匿名内部类默认情况下是会隐式的持有一个他们外部类的引用的,又由于如果在内部类中存在引用外部类的静态实例的话,这个实例的生命周期是整个应用程序的,也就导致了整个外部类的生命周期是整个应用程序,如果外部类是Activity的话,假如我们已经确定该Activity不再需要了,但是因为非静态内部类或者匿名内部类的引用导致不会被GC回收,解决措施是将非静态内部类或者匿名内部类设置成static类型,这样他们内部就不会默认持有外部类的引用了,同时可以将其用到的关于外部类的对象设置成弱引用方式;

        (3):资源对象未关闭

        资源对象比如Cursor游标,File对象,Bitmap资源,往往用到了缓冲,我们在不需要的时候一定要记得及时关闭他们,以便他们的缓冲及时被回收,如果我们仅仅只是将他们的引用设置成null而不关闭他们,往往会造成内存泄漏,因此在使用这些对象的时候一定注意最后手动调用他们的close方法关闭掉,一般这个关闭操作是出现在try catch finally语句的finally部分的,在关闭之后将这些对象赋值为null即可;

        (4):单例模式造成的内存泄漏

        在Android中使用单例模式的时候,由于单例模式的静态特性往往会导致该单例的生命周期和应用的生命周期一样长,而我们在创建单例对象的时候经常要传入Context上下文,我们都知道Android中的Context有三种,如果传入的是Application的Context,自然没什么问题,因为这个Context本身的作用域就是整个应用;但是如果传入的是Activity的Context就有可能造成内存泄漏了,因为在Activity退出的时候,由于他的Context被单例对象所持有着,那么造成了他不会被回收掉,出现内存泄漏;

        解决方法是:在Android中创建单例对象的时候,如果要涉及到传入Context上下文,最好传入Application的Context,因为Application的Context并不是万能的,对于Dialog而言,他是只能出现在Activity中的,Application可以通过getApplicationContext获得,最好不要通过getApplication,因为该方法仅适合于Activity和Service不适合于BroadcastReceiver等其他组件中;

        (5):尽量避免使用static成员变量

        因为将一个变量声明为static的话,意味着其生命周期将和应用程序是一样的,这会带来一系列问题,比如你的App进程加入设计成是常驻内存的,即使你切换到后台,由static所持有的对象资源将不会释放,而系统的管理机制会将占用内存较大的后台进程优先回收掉,这回导致你的应用程序经常性的被回收;

        (6):Handler造成的内存泄漏

        因为Handler的生命周期和Activity是不一致的,所以经常会带来内存泄漏的问题,比如有这样一种情形,我们在Activity中定义了一个继承自Handler的非静态内部类,并且通过他发送了一条消息,该消息会在10分钟之后返回当前时间,接着我们将Activity退出了,但是此时Activity并不会被GC回收掉的,原因在于我们的消息任务还在MessageQueue中排队,那么Handler是无法释放的,而Handler本身又持有外部类Activity的引用,那么也就导致了Activity没有释放了,造成内存泄漏,解决措施和第(2)点一样,也是将非静态内部类设置成static类型的,同时为了高效率的回收,我们可以将所引用的外部类的实例在内部类中设置成WeakReference类型,也就是弱引用类型,此外为了防止Looper对象的内存泄漏,我们可以在Activity销毁的时候调用removeCallbackAndMessages方法,移出MessageQueue里面的所有消息;

        (7):注册监听器之后没有解注册

        Android中存在许多需要register与unregister的监听器,我们需要在适当的时候及时unregister那些监听器,自己手动add的listener一定要记得及时remove这个listener;

        (8):一些不良的编程习惯

        比如在使用Bitmap的时候没有调用recycle方法,对于Bitmap对象在不使用的时候,我们应该首先使用recycle方法释放掉内存然后再设置他为null,如果直接设置null的话只是将java层面的对象吧释放掉了,但是Bitmap的操作是要用到C层面的东西的,recycle的作用就是释放掉C层面的;在比如构造ListView的Adapter时候,没有使用缓存的convertView,每次都是创建新的convertView;
(46):避免OOM异常的措施有哪些呢?

        (1):使用更加轻量级的数据结构

        可以考虑使用ArrayMap/SparseArray来代替HashMap,原因在于通常的HashMap更加占用内存,因为他需要一段空间来存储我们的Mapping操作,而很多情况下这些空间是不会得到充分利用的,ArrayMap采用两个数组来实现,一个数组用于存储key值hash之后的顺序列表,另一个数组存储按key的顺序记录的key-value值,当你想要获得某个value值的时候,ArrayMap会计算输入key转换之后的hash值,然后对通过二分查找的方式寻找到这个hash值在hash值数组中的位置index,有了这个index我们便可以在另外一个数组中直接访问到需要的键值对了,如果第二个数组键值对中的key和当前输入的key不一致,则发生了碰撞冲突,为了解决这个问题,我们会以该key为中心,上下去查找比对,直到找到匹配的值为止;而对于SparseArray来说,他的高效性体现在他避免了对key和value的自动装箱操作,并且避免了装箱之后的解箱操作;

        (2):尽量在Android中使用Enum枚举类型,原因在于枚举类型在运行的时候会带来额外的内存占用;

        (3):减少Bitmap对象的内存占用

        Bitmap是极容易消耗内存的家伙,通常我们可以这么做:使用它的inSampleSize缩放比例属性,在图片加载到内存之前,我们首先获取到图片大小,计算出适当的缩放比例出来,避免不必要的大图加载占用内存操作;其次我们可以使用适当的解码格式,ARGB_8888/ARGB_4444/RGB_565解码方式不同,每个像素点占用的字节数就不同;

        (4):使用更小的图片

        尽量使用更小的图片不仅仅可以减少内存的使用,还可以避免出现大量的InflationException,假设有一张很大的图片被XML文件直接引用,很有可能在初始化的时候就会导致内存不足而引发InflationException

        (5):尽量对内存中的对象进行复用,具体实现措施有以下几种:

        重用系统自带的资源,比如字符串/颜色/图片/动画等;

        注意在ListView/GridView等出现大量重复子组件的视图中对ConvertView的复用;

        使用LRU对Bitmap图片进行缓存;

        使用inBitmap的高级特性提高Android系统在Bitmap分配与释放执行效率上的提升,使用inBitmap可以告诉Bitmap解码器尝试使用已经存在的内存区域,新解码的Bitmap会尝试使用之前那张Bitmap在heap中所使用的内存区域,而不是取内存重新申请一块区域来存放Bitmap,这样的话,即使有上千张图片,也只会占用屏幕所能显示图片数量的内存大小,但是使用inBitmap有两个限制条件,一是新申请的Bitmap大小必须小于或者等于已经赋值的Bitmap大小;二是新申请的Bitmap解码方式必须与旧Bitmap的解码方式相同,大家都是ARGB_8888就都是,我们可以创建一个包含多种典型可重用的Bitmap对象池,这样的话,后续的Bitmap就都能找到合适的"模板"去复用了;

        (6):避免在onDraw方法中执行对象的创建

        因为onDraw方法的调用频率比较高,一定要注意避免在这个方法里面进行创建对象的操作,因为他会迅速增加内存的使用,容易引起频繁的GC操作,甚至可能出现内存的抖动现象;

        (7):StringBuilder的使用

        在大量使用到字符串拼接操作的地方可以用StringBuilder来代替“+”,但是有一点需要注意的就是,StringBuilder是非线程安全的;

        (8):注意单例对象中不合理的持有其他对象这种情况,因为单例的生命周期是和应用保持一致的,使用不合理很容易会导致持有对象的泄漏;

(47):在Activity中使用AsyncTask造成的内存泄漏问题怎么解决?

        我们一般使用AsyncTask都是在Activity中通过创建一个非静态内部类实现的,但这种方式在上面的内存泄漏部分已经提到过可能会造成内存泄漏,原因就在于非静态内部类默认是会持有外部类的引用的,如果我们的AsyncTask任务内部存在静态变量引用了外部Activity的一些对象的话,会导致Activity即使已经销毁了,但是他所占用的内存空间并没有释放,为此我们可以采用以下方式来解决:

        (1):如果在AsyncTask内部要引用Activity对象实例的话,我们可以使用弱引用的方式持有Activity对象;

        (2):重新AsyncTask的onCancel方法,在它里面实现一些关闭资源的操作;

        (3):在Activity退出的时候,调用AsyncTask的onCancel方法,结束掉其占有的资源,这样的话便保证了Activity和AsyncTask生命周期的同步;

(48):显式Intent与隐式Intent的区别

        显式Intent指的是明确指定目标组件名称的Intent,隐式Intent指的是没有明确指定目标组件名称的Intent,对于隐式Intent当在创建一个Activity的时候,我们通过设置的intent-filter标签来匹配当前Activity是不是Intent想要的Activity,在intent-filter里面会有三方面的内容:action/category/data;

(49):Android中常见的动画有哪些呢?

        Android中常见的动画有三类:View动画、帧动画、属性动画;

        View动画:包括四大类型,平移、缩放、旋转、透明度,但该动画并不会改变View属性的值,只会改变View的位置,也就是说一个按钮在动画之后虽然不在原来的位置了,但是原来的位置还是可以出发点击事件的;

        帧动画:类似于放电影,一帧一帧的切换图片以达到动画的效果,这种动画一定要注意OOM异常的发生;

        属性动画:3.0开始出现的,他作用的对象不仅仅是View,可以是任意的Object对象,并且是通过反射不断的改变Object的属性,动画结束之后View的属性是实实在在发生变化的;

(50):不使用动画如何实现一个动态的View?

        开启一定Timer定时器,每隔一段时间通过反射修改View对象的某个属性,而后调用invalidate方法进行重绘即可,其实就是属性动画的实现原理。

(51):invalidate与postInvalidate的区别?

        invalidate和postInvalidate方法都是用于视图重绘的,后者最终也会调用前者,两者最大的区别在于一个可以用于子线程,一个只能用于主线程;

        Android的UI操作并不是线程安全的,invalidate只能用在主线程中,如果现在子线程中使用invalidate的话,我们需要在主线程中创建一个Handler对象,并且利用该Handler将绘制过程切换到主线程上面;

        而postInvalidate是可以用在子线程中的,其实他也是通过Handler来实现切换到主线程的操作的,只是系统给你做了封装而已,最后调用的还是invalidate方法;

(52):Fragment里面可以嵌套Fragment吗?会带来什么问题呢?

        Fragment里面是可以嵌套Fragment的,但是可能在使用的使用你会发现有如下问题:

        (1):假如Activity中嵌套有Fragment,而Fragment中嵌套有子Fragment,Fragment可以通过ChildFragmentManager来管理这些子Fragment,子Fragment可以通过getParentFragment获得当前Fragment的宿主Fragment,但是当你第一次从Activity启动Fragment,再去启动Fragment的子Fragment的时候,是存在指向Activity的变量的,当你退出Fragment之后回到Activity,然后再次进入Fragment,你会发现抛出类似于Fragment的ID或者Tag重复的异常,原因在于Fragment被detached之后都会被reset掉,但是他并不会对ChildFragmentManager中的Fragment做reset,这样导致你下次开启之前开启过的Fragment的时候会发现ChildFragment池中其实已经存在该Fragment的异常,解决措施就是在你调用detached方法reset Fragment的时候同时将ChildFragmentManager中的Fragment也reset掉;

        (2):当我们从一个Activity启动一个Fragment,接着在Fragment中实例化一些子Fragment,在子Fragment中有返回的启动另外一个Activity,即通过startActivityForResult的方式启动,可以发现在子Fragment中收不到onActivityResult的值,如果在子Fragment中通过getActivity.startActivityForResult的方式启动Activity,则只有Activity可以收到onActivityResult,如果是以getParentFragment.startActivityForResult的方式启动的话,那么只有Activity以及父Fragment可以收到,子Fragment还是收不到;解决措施是通过getParentFragment.startActivityForResult先在父Fragment中获得返回值,然后通过父Fragemnt传递给他的子Fragment;

(53):如何在Activity中动态的添加Fragment

        首先应该知道的就是Activity是通过FragmentManager来管理添加或者移出当前Activity的Fragment对象的,所以首先我们应该获得FragmentManager对象,有了这个对象之后我们就可以利用该对象的replace方法来讲布局中某一块地方替换成我们想要填充的Fragment了,但是有个注意点就是,为了防止在修改Activity界面Fragment的时候出现错误,Android对整个添加或者移出操作进行了事务处理,所以在使用replace或者remove方法之前,你应该调用FragmentManager的beginTransaction方法开启事务,在replace或者remove方法之后调用FragmentManager的commit方法提交事务;还有一点就是动态添加Fragment的时候,Activity布局文件中的标签是<FrameLayout>而静态添加的话,布局文件中的标签是<fragment>;

(54):Android中的MVC模式与MVP模式

        MVC:Model(模型层)、View(视图层)、Control(控制层)

        View层一般通过XML进行界面的描述

        Control层主要是由Activity实现的,因此我们应该尽量少的在Activity中进行业务代码的编写,而应该通过Activity交割给Model业务逻辑层来进行处理,这也就是为什么Activity中要设置5s来判断当前Activity是不是没响应的原因了,因为系统根本不建议你在Activity做过多的事;

        Model层就是我们进行业务逻辑代码编写的地方,这一层你可以进行操作数据库,访问网络等操作,将结果返回给Activity,通过Activity调用显示界面的方法显示在View上面;

        通过在Activity层调用模型层的方法,具体的数据库操作,网络访问等操作是由模型层实现的,接着模型层以回调的方式返回数据给Activity,Activity将这些数据显示到View上面,这样避免了Activity部分代码国语复杂,大部分操作都由Model层来处理了;

        MVP:Model(模型层)、View(视图层)、Presenter(主导器)

        MVP模式是对MVC模式的进一步解耦,在这种模式中首先会对View层以及Model层各自抽象出来一个接口层,我们称之为IView与IModel,而Presenter层可以认为是用来分担Activity任务的,也即Activity中再抽出一个Presenter层来作为纽带,在这个纽带中通过IView接口把数据送给View,通过IModel接口通知Model来进行数据库操作等等,而Activity层只需要定义一个Presenter对象,利用该对象进行业务处理过程即可;

(55):dp,dip,dpi,px,sp各指的是什么呢?

        dp与dip(device independent pixels)是一个意思,指的是设备独立像素,与设备屏幕有关系;
        dpi(dot per inch)屏幕像素密度,指的是每英寸多少像素
        px(pixels)像素
        sp(scaled pixels)主要用于字体大小的

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值