Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >为什么我的应用程序占用这么多内存?

为什么我的应用程序占用这么多内存?
EN

Stack Overflow用户
提问于 2015-03-11 07:00:45
回答 1查看 1.8K关注 0票数 7

因此,我制作了一个游戏应用程序,使用位图和表面视图为一个学校项目。但是这个应用本身占用了这么多的内存!!只有当你启动它时,它才能达到60 to的内存,并且你玩的越多,它得到的越高(一度它得到了90 to的内存,并且游戏非常落后)。

在观看了Google /O 2011 (CruQY55HOk)之后,我重新描述了这可能是内存泄漏,因为应用程序是这样启动的:

玩了两分钟就这样结束了:

该应用程序本身看起来尽可能简单,有8位图形和不多的颜色:

所有的图像,我使用的重量只有400 in的,所以,在地狱为什么要这么多的拉姆?!我想可能是声音,但所有声音加起来只有4.45mb的内存,相当于应用程序接收量的1/10。我知道位图占用了很多内存,但这太荒谬了!

这是我的onLoad:

代码语言:javascript
运行
AI代码解释
复制
public GameView(Context c) {
        // TODO Auto-generated constructor stub
        super(c);
        this.c = c;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        ScoreParticleP = new PointF();
        NewScoreParticleP = new PointF();
        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;
        // it=blocks.iterator();
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        this.setKeepScreenOn(true);
        WindowManager wm = (WindowManager) c
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        this.screenw = display.getWidth();
        this.screenh = display.getHeight();
        this.differencew = (double) screenw / normalw;
        this.differenceh = (double) screenh / normalh;
        try{
            mediaPlayer = MediaPlayer.create(c, R.raw.nyan);
            while(mediaPlayer == null) {
                mediaPlayer = MediaPlayer.create(c, R.raw.nyan);
            }
        mediaPlayer.setLooping(true);
        if(mediaPlayer!=null)
        mediaPlayer.start();
        }
        catch(Exception e){

        }
        try{
        mediaPlayer2 = MediaPlayer.create(c, R.raw.remix);
        while(mediaPlayer2==null){
            mediaPlayer2 = MediaPlayer.create(c, R.raw.remix);
        }
        mediaPlayer2.setLooping(true);
        }
        catch(Exception e){

        }
        try{
        mediaPlayer3 = MediaPlayer.create(c, R.raw.weed);
        while(mediaPlayer3==null){
            mediaPlayer3 = MediaPlayer.create(c, R.raw.weed);
        }
        mediaPlayer3.setLooping(true);
        }
        catch(Exception e){

        }
        SharedPreferences prefs2 = c.getSharedPreferences(
                "Sp.game.spiceinspace", Context.MODE_PRIVATE);
        counter2 = prefs2.getInt("score", 0);
        this.sprite = BitmapFactory.decodeResource(getResources(),
                R.drawable.sprite, options);
        this.sprite = Bitmap.createScaledBitmap(sprite, sprite.getWidth() * 3,
                sprite.getHeight() * 3, false);
        this.heart = BitmapFactory.decodeResource(getResources(),
                R.drawable.heart);
        this.explosionheart=BitmapFactory.decodeResource(getResources(),
                R.drawable.explosionheart);
        this.heart = Bitmap.createScaledBitmap(heart, heart.getWidth() * 3,
                heart.getHeight() * 3, false);
        currentSpeed = new PointF(0, 0);
        currentDirection = new Point(0, 0);
        currentPosition = new Point(350, 350);
        this.background = BitmapFactory.decodeResource(getResources(),
                R.drawable.space);
        this.background2=BitmapFactory.decodeResource(getResources(),
                R.drawable.space2);
        this.electricExplosion = BitmapFactory.decodeResource(getResources(),
                R.drawable.effect_explosion);
        this.normalexplison = BitmapFactory.decodeResource(getResources(),
                R.drawable.effect_explosion2);
        this.background = Bitmap.createScaledBitmap(background,
                background.getWidth() * 5, background.getHeight() * 5, false);
        this.background2 = Bitmap.createScaledBitmap(background2,
                background2.getWidth() * 5, background2.getHeight() * 5, false);
        this.lost = BitmapFactory.decodeResource(getResources(),
                R.drawable.gameover);
        this.lostNew = BitmapFactory.decodeResource(getResources(),
                R.drawable.gameovernew);
        lostNew = FitAllDevices(lostNew);
        lost = FitAllDevices(lost);
        this.alien = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_alien);
        this.coin = BitmapFactory.decodeResource(getResources(),
                R.drawable.item_coin);
        partic = BitmapFactory.decodeResource(getResources(),
                R.drawable.particle_star);
        fire = BitmapFactory.decodeResource(getResources(),
                R.drawable.particle_fire);
        smoke = BitmapFactory.decodeResource(getResources(),
                R.drawable.particle_smoke);
        partic = Bitmap.createScaledBitmap(partic, partic.getWidth() * 2,
                partic.getHeight() * 2, false);
        fire = Bitmap.createScaledBitmap(fire, fire.getWidth() * 2,
                fire.getHeight() * 2, false);
        smoke = Bitmap.createScaledBitmap(smoke, smoke.getWidth() * 2,
                smoke.getHeight() * 2, false);
        electricExplosion = Bitmap.createScaledBitmap(electricExplosion,
                electricExplosion.getWidth() * 2,
                electricExplosion.getHeight() * 2, false);
        normalexplison = Bitmap.createScaledBitmap(normalexplison,
                normalexplison.getWidth() * 3,
                normalexplison.getHeight() * 3, false);
        this.alien = Bitmap.createScaledBitmap(alien, alien.getWidth() * 3,
                alien.getHeight() * 3, false);
        asteroid = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_astroid);
        bomb = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_spacebomb);
        asteroid = Bitmap.createScaledBitmap(asteroid, asteroid.getWidth() * 3,
                asteroid.getHeight() * 3, false);
        bomb = Bitmap.createScaledBitmap(bomb, bomb.getWidth() * 3,
                bomb.getHeight() * 3, false);
        goldasteroid = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_goldastroid);
        goldasteroid = Bitmap.createScaledBitmap(goldasteroid,
                goldasteroid.getWidth() * 3, goldasteroid.getHeight() * 3,
                false);
        mushroom = BitmapFactory.decodeResource(getResources(),
                R.drawable.item_mushroom);
        mushroom = Bitmap.createScaledBitmap(mushroom, mushroom.getWidth() * 4,
                mushroom.getHeight() * 4, false);
        coin = Bitmap.createScaledBitmap(coin, coin.getWidth() * 2,
                coin.getHeight() * 2, false);
        drug = BitmapFactory
                .decodeResource(getResources(), R.drawable.item_not);
        drug = Bitmap.createScaledBitmap(drug, drug.getWidth() * 4,
                drug.getHeight() * 4, false);
        rocket = BitmapFactory.decodeResource(getResources(),
                R.drawable.item_rocket);
        rocket = Bitmap.createScaledBitmap(rocket, rocket.getWidth() * 4,
                rocket.getHeight() * 4, false);
        electricExplosion = FitAllDevices(electricExplosion);
        alien = FitAllDevices(alien);
        normalexplison = FitAllDevices(normalexplison);
        explosionheart = FitAllDevices(explosionheart);
        mushroom = FitAllDevices(mushroom);
        drug = FitAllDevices(drug);
        rocket = FitAllDevices(rocket);
        bomb = FitAllDevices(bomb);
        asteroid = FitAllDevices(asteroid);
        goldasteroid = FitAllDevices(goldasteroid);
        sprite = FitAllDevices(sprite);
        heart = FitAllDevices(heart);
        player = new Spicy(sprite, heart);
        hit = soundPool.load(c, R.raw.hit, 1);
        pass = soundPool.load(c, R.raw.win, 1);
        //remix = soundPool.load(c, R.raw.remix, 1);
        destroy = soundPool.load(c, R.raw.destroy, 1);
        aliensound = soundPool.load(c, R.raw.alien, 1);
        alienexpload = soundPool.load(c, R.raw.explosion2, 1);
        //particlesound = soundPool.load(c, R.raw.particle, 1);
        bigexplosion=soundPool.load(c, R.raw.explosion, 1);
        gameLoopThread = new GameLoopThread(this);
        this.requestFocus();
        this.setFocusableInTouchMode(true);
        holder = getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                boolean retry = true;
                gameLoopThread.setRunning(false);
                while (retry) {
                    try {
                        gameLoopThread.join();
                        retry = false;
                    } catch (InterruptedException e) {
                    }
                }
            }

              public void surfaceCreated(SurfaceHolder holder) {     
                  if (gameLoopThread.getState()==Thread.State.TERMINATED) { 
                        gameLoopThread = new GameLoopThread(g);
                  }
                  gameLoopThread.setRunning(true);
                  gameLoopThread.start();
          }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {
            }
        });
    }

我做错了什么吗?我的应用程序只是一个小游戏,位图从一边出来,然后转到另一边,一开始很慢,但是你玩得越多,我就会明白如果屏幕上有很多位图,我会理解这么多内存,但是当屏幕上只有播放器和背景时,它需要60 my!

我很乐意给这个应用程序发电子邮件,让他自己看看它有多简单,但却无缘无故地花了多少钱。

编辑:

我提到这个应用程序会占用更多的内存,我知道这与我创建新的位图,然后移动它们,然后删除它们有关,你播放的次数越多,创建的位图就越多,但我尽力将它们删除,然后再循环,以确保垃圾收集器收集到它们。我想知道如何将使用量降到最低,并使我的游戏仍然是可玩的:

这就是我如何“生成”暴徒(暴民获取我在onLoad上加载的位图):

代码语言:javascript
运行
AI代码解释
复制
private void spawnMob() {
    if (timer2 == 0) {
        int mobtype = randInt(1, 40);
        switch (mobtype) {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
        case 15:
            Mob m = new Mob(alien, MobEffect.comeback, 1);
            Point p = new Point(0, 0);
            p.y = randInt(0, screenh - alien.getHeight());
            p.x = screenw + alien.getWidth();
            spawned.put(p, m);
            break;

        case 16:
        case 17:
        case 18:
        case 19:
        case 20:
        case 21:
        case 22:
        case 23:
        case 24:
        case 25:
        case 26:
        case 27:
        case 28:
        case 29:
        case 30:
        case 31:
        case 32:
            Mob m2 = new Mob(asteroid, MobEffect.dissapire, 0);
            Point p2 = new Point(0, 0);
            p2.y = randInt(0, screenh - asteroid.getHeight());
            p2.x = screenw + asteroid.getWidth();
            spawned.put(p2, m2);
            break;
        case 33:
        case 34:
        case 35:
        case 36:
        case 37:
        case 38:
        case 39:
            Mob m3 = new Mob(goldasteroid, MobEffect.dissapire, 1);
            Point p3 = new Point(0, 0);
            p3.y = randInt(0, screenh - goldasteroid.getHeight());
            p3.x = screenw + goldasteroid.getWidth();
            spawned.put(p3, m3);
        case 40:
            if (counter > 3) {
                Mob m4 = new Mob(bomb, MobEffect.expload, 1, 5, false,
                        false);
                Point p4 = new Point(0, 0);
                p4.y = randInt(0, screenh - bomb.getHeight());
                p4.x = screenw + bomb.getWidth();
                spawned.put(p4, m4);
            } else {
                Mob m5 = new Mob(asteroid, MobEffect.dissapire, 0);
                Point p5 = new Point(0, 0);
                p5.y = randInt(0, screenh - asteroid.getHeight());
                p5.x = screenw + asteroid.getWidth();
                spawned.put(p5, m5);
            }
            break;
        }
        if (rocketspeed >= 10) {
            timer2 = randInt(1, 8);
        } else {
            if (bleedoff != 35) {
                if (bleedoff > 1)
                    timer2 = randInt(35 / (int) bleedoff,
                            150 / (int) bleedoff);
                else
                    timer2 = randInt(35, 150);
            } else
                timer2 = randInt(1, 10);
        }
    } else {

        timer2--;

    }
}

在onDraw上,我确保删除这些暴徒,这样当它们不在屏幕上时,它们就不会占用额外的内存:

代码语言:javascript
运行
AI代码解释
复制
    Iterator<Map.Entry<Point, Mob>> spawnedEntry = spawned
                .entrySet().iterator();
        while (spawnedEntry.hasNext()) {
            Map.Entry<Point, Mob> entry = spawnedEntry.next();
            if(entry.getValue().destroycomplete||entry.getValue().dissapired){
                spawnedEntry.remove();
            }
            else
            entry.getValue().draw(canvas, entry.getKey());

也是在黑帮班:

代码语言:javascript
运行
AI代码解释
复制
if(!MobEffectstarted)
        if(!destroycomplete)
    c.drawBitmap(mob,p.x,p.y, null);
        else
            mob.recycle();

编辑2:

这就是eclipse的内存工具告诉我的:

确切地说是位图。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-11 07:41:32

为什么我的应用程序在启动时消耗这么多内存?

您的原始图像文件被压缩。您正在解压缩它们,并将完整的位图存储在内存中。(关于rowBytes *高度,以字节为单位;找出位图大小)您也正在调整其中的许多大小;例如,背景被缩放到原来大小的五倍。由于图像是二维的,所以内存消耗的大小是二次的。

示例:大小为500x500的图像;它在行之间分配4个字节(32位模式)。这导致位图大小为500x4x500= 1MB。

如果缩放此映像的倍数为2,则分配的内存不会增加一倍,而是:1000x4x1000= 4MB。

,我能做什么?

  • 如果您不需要一个alpha通道,您可能需要减少位图的位深。例如,使用Bitmap.Config.RGB_565进行解码。
  • 不要通过指定样本大小来加载大于需要的位图。(这里有更多关于这一点的信息:加载大位图)
  • 确保尽可能多地重复使用位图。
  • 只在需要时加载位图。你同时需要两个背景吗?
  • 您的背景似乎只是一个梯度;您可以使用LinearGradient类来绘制它,从而避免了巨大的背景图像。(这同样适用于你的方格,如果它们不仅仅是占位符。)

关于代码中的内存泄漏:

  • 确保你不会不断地制造新的暴徒。将“死气沉沉”的暴徒保存在一个单独的列表中,并在需要时将它们放回演员名单中。只有当你真的没有“待定”的暴徒时,才创建新的。
  • 不要在经常被调用的方法中创建对象。(例如:任何抽签()方法。)

如果你是在前蜂窝测试,你需要回收任何分配的位图,因为他们可能不会从本机内存释放,否则。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28990100

复制
相关文章
矩阵范数的等价性(原创)[通俗易懂]
设 F=R F = R \mathbb F=\mathbb R 或 C, C , \mathbb C, 对于任意两个 Fn×n F n × n \mathbb F^{n \times n} 上的范数 ∥⋅∥α ‖ ⋅ ‖ α \Vert \cdot\Vert_{\alpha} 与 ∥⋅∥β, ‖ ⋅ ‖ β , \Vert \cdot\Vert_{\beta}, 若存在常数 C1>0,C2>0, C 1 > 0 , C 2 > 0 , C_1 \gt 0, C_2 \gt 0, 使得 ∀X∈Fn×n, ∀ X ∈ F n × n , \forall \mathbf {X} \in \mathbb F^{n \times n},
全栈程序员站长
2022/09/29
1.3K0
Python中表达式与语句
Python中我暂时并未发现谁对着两个名词的明确定义;我对这两个名词的理解就是,表达式就是你想要执行的对象,语句就是你的具体执行操作。
py3study
2020/02/10
5640
门控时钟和逻辑等价性检查
每当问到“怎么降低动态功耗”,一般的答案就是插门控时钟。那为什么插门控时钟就能降低动态功耗呢?门控时钟一定能插得进去吗?对逻辑等价性检查(LEC)有什么影响?
ExASIC
2021/11/02
1.3K0
门控时钟和逻辑等价性检查
C++语言的表达式模板:表达式模板的入门性介绍
原标题:C++ Expression Templates: An Introduction to the Principles of Expression Templates 原作者:Klaus Kreft与Angelika Langer 原文链接: http://www.angelikalanger.com/Articles/Cuj/ExpressionTemplates/ExpressionTemplates.htm 翻译:Magi Su 翻译已经过原作者许可,转载请先征求原作者的许可。图片均取自原文,如果有水印为CSDN所打和老子没关系。出于清晰起见,文章中所有模板中的class都被改为typename。 模板(template)最早是以将类型(type)参数化为目的引入C++语言的。(译注1)链表 (list)是一个典型的例子。实际编码的时候,人们并不希望为保存不同类型变量的链表 分别编码,而是希望在编写的时候能够使用一个占位符(placeholder)来代替具体的类型 (即是模板参数),而让编译器来生成不同的链表类(模板的实例化)。 时至今日,模板的使用已经远远超过C++模板的发明者所预期的范畴。模板的使用已经涵盖 了泛型编程,编译时求值,表达式模板库,模板元编程,产生式编程(generative programming)等诸多领域。在这篇文章中,我们仅限于探讨一些表达式模板的编程知识, 侧重于编写表达式模板程序库这个方面。 我们必须指出:表达式模板库是相当复杂的。出于这个原因,我们读到过的关于表达式模 板的介绍都不是很容易理解的。因此,本文的作者希望能够通过本文为表达式模板提供一 个通俗的介绍,同时又不失对具体实现细节的阐述,从而对读者阅读模板库的代码能够起 到帮助。作者希望提取出表达式模板编码的一些原则性知识。有关于此领域的更多细节可 以参考其他著作。
sofu456
2019/07/09
2.6K0
C++语言的表达式模板:表达式模板的入门性介绍
C.163: 重载只用于基本等价的操作
Having the same name for logically different functions is confusing and leads to errors when using generic programming.
面向对象思考
2020/03/25
2860
【C语言笔记】数组与指针不等价
数组遍历方式一:使用指针遍历数组元素,p++等价于(p++),即指针指向的地址每次后移一个单位,然后再取地址上的值。这里的一个单位是sizeof(int)个字节。
正念君
2019/06/26
8030
对for循环中表达式和循环体的执行顺序详解
由上面的执行结果不难看出for循环中除了表达式1为了初始化变量,其的循环是表达式2——循环体——表达式3——表达式2这样的循环。
PHP开发工程师
2021/06/02
9920
【集合论】等价类 ( 等价类概念 | 等价类示例 | 等价类性质 | 商集 | 商集示例 )★
商集的本质 : 商集 本质是一个 集合 , 集合中的元素是 等价类 , 该等价类是基于
韩曙亮
2023/03/28
1.3K0
【集合论】等价类 ( 等价类概念 | 等价类示例 | 等价类性质 | 商集 | 商集示例 )★
Python中表达式int(&#39;0x10, 36)的值是。。。
在Python中,int()可用来把实数转换为整数,或者把数字字符串按指定进制转换为十进制数,详见文末的相关阅读。 然而,下面的代码又应该如何解释呢? >>> int('0x10', 36) 42804 按照传统意义的解释,0x开头表示十六进制,而试图把十六进制数看作36进制数并转换为十进制数,上面的代码应该出错,但是却又没有出错。把'0x10'当作36进制,那么x又表示什么呢?执行下面的代码试试: >>> import string >>> for ch in string.ascii_lowercase
Python小屋屋主
2018/04/16
9930
C++ 自由存储区是否等价于堆?
“free store” VS “heap” 当我问你C++的内存布局时,你大概会回答: “在C++中,内存区分为5个区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区”。 如果我接着问你自由存储区与堆有什么区别,你或许这样回答: “malloc在堆上分配的内存块,使用free释放内存,而new所申请的内存则是在自由存储区上,使用delete来释放。” 这样听起来似乎也没错,但如果我接着问: 自由存储区与堆是两块不同的内存区域吗?它们有可能相同吗? 你可能就懵了。 事实上,我在网上
Tencent JCoder
2018/07/02
3.5K0
机器学习中的基本问题——log损失与交叉熵的等价性
log\left ( 1+exp\left ( -m \right ) \right )
felixzhao
2019/01/31
1.3K0
等价域名
如果一个服务有多个域名入口,通过这些入口访问得到的内容一样,那么称这些域名为等价域名。 比如,通过等价域名,可以提供 3 个一模一样的文件或者接口服务。
陈少文
2022/07/17
3040
等价域名
【集合论】等价关系 ( 等价关系概念 | 等价关系示例 | 等价关系与闭包 )
由上边可以看出 , 等价关系是用于分类的 , 同一年出生的人可以划分到一个等价类中 ;
韩曙亮
2023/03/28
1.2K0
机器学习中的基本问题——log损失与交叉熵的等价性
1、log损失 image.png 2、交叉熵 image.png
felixzhao
2018/03/20
1.2K0
机器学习中的基本问题——log损失与交叉熵的等价性
黑盒测试的等价类划分法_黑盒测试等价类输出
等价类划分是一种典型的黑盒测试方法,这一设计方法完全不用考虑程序的内部结构,也就是说其只根据需求规格说明书。
全栈程序员站长
2022/11/10
6890
黑盒测试的等价类划分法_黑盒测试等价类输出
java OJ题目判断输入结束(与C语言的EOF结束等价)
/* * java 作Oj题目是会有输入若干数据的情况,不好判断输入结束符, * 类似于C语言中的EOF符号 * 在这里提供了一种方法 * */
用户3030674
2018/09/14
2.5K0
企业面试题: javascript中表达式parseInt("9")+parseFloat('7')的结果是什么?
parseFloat() 所解析的字符串中第一个小数点是有效的,而parseInt() 遇到小数点会停止解析,因为小数点并不是有效的数字字符。
舒克
2019/08/09
8900
html中表单提交
表单提交代码 1、源代码分析 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body
bamboo
2019/01/29
1.6K0
html中表单提交
论强化学习和概率推断的等价性:一种全新概率模型
选自arXiv 作者:Sergey Levine 机器之心编译 参与:张倩、刘晓坤 虽然强化学习问题的一般形式可以有效地推理不确定性,但强化学习和概率推断的联系并不是很明显。在本文中,UC Berkeley EECS 助理教授 Sergey Levine 提出了一种新的概率模型和理论框架,证明了强化学习的一般形式即最大熵强化学习与概率推断的等价性。在原则上,将问题形式化为概率推断,可以应用多种近似推断工具,将模型以灵活、强大的方式进行扩展。 概率图模型(PGM)为机器学习研究者提供了一种广泛适用的工具(K
机器之心
2018/06/08
7690
C++的lambda表达式
从C++11开始,C++也支持使用lambda表达式(匿名函数)。Lambda表达式是一种便捷的方式,可以定义一个函数对象,而无需使用显式的函数对象类型或函数指针语法。
叶茂林
2023/07/30
1820

相似问题

布尔表达式的等价性

23

正则表达式等价性

40

C中wstring的等价性

22

C std的等价性::查找

12

比较rpn (后缀)表达式的等价性

214
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文