【问题标题】:Out of memory: Heap Size Error after adding 10KB ImageButtons内存不足:添加 10KB ImageButtons 后出现堆大小错误
【发布时间】:2014-02-11 07:54:55
【问题描述】:

我正在 Eclipse 中开发一个以启动画面开头的 android 应用程序(在完成一些研究后怀疑这里存在内存泄漏)。启动屏幕进入由仅 7 个按钮组成的主菜单,这些按钮通向一些 Java Applet。整个应用程序运行良好,直到我将 7 个虚拟 (ImageButton) png 图像更改为最终确定的 7 个 png 图像。这些 png 图像的平均大小为 10KB,并且不认为它们是问题的原因(因为它们很小),即使这个问题是在我更改 ImageButtons 的这些 png 图像之后开始的。

老实说,除了重新调整图像大小之外,我不知道从哪里开始,因为它们的设计有点像像素大小(不是内存),以适应不同的设备。但我认为这个问题还有另一种解决方案,我作为初学者无法弄清楚。我希望有人可以帮助我解决这个问题:)

代码如下:

清单。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.letsfly.tryp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:debuggable="true" > 
        <activity
            android:name=".Splash"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.MAINACTIVITY" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name=".Testing"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.TESTING" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name=".trypOne"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.TRYPONE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name=".trypTwo"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.TRYPTWO" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name=".trypThree"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.TRYPTHREE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name=".trypFour"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.TRYPFOUR" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name=".trypFive"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.letsfly.tryp.TRYPFIVE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>


</manifest>

启动 XML。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"

    >

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="600dp"
            android:layout_height="800dp"
            android:layout_gravity="center"
            android:layout_marginTop="-180px"
            android:contentDescription="@string/button1"
            android:padding="0px"
            android:src="@drawable/trypsplash" />

    </LinearLayout>

启动 Java。

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class Splash extends Activity {


    @Override
    protected void onCreate(Bundle waitFiveSeconds) {
        // TODO Auto-generated method stub

        requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(waitFiveSeconds);
        setContentView(R.layout.splash);

        Thread timer = new Thread(){
            public void run(){
                try{
                    sleep(5000);
                }catch(InterruptedException e){
                    e.printStackTrace();

                }finally{
                    Intent openMenu = new 

Intent("com.letsfly.tryp.MAINACTIVITY");
                        startActivity(openMenu);

                }
            }
        };
        timer.start();
    }
}

MainActivity Java(按钮所在的位置)。

package com.letsfly.tryp;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;

public class MainActivity extends Activity {

    ImageButton button1;
    ImageButton button2;
    ImageButton button3;
    ImageButton button4;
    ImageButton button5;
    ImageButton button6;
    ImageButton button7;

    @Override
    protected void onCreate(Bundle savedInstanceState) {




        //replace yourActivity.this with your own activity or if you declared a context you can write context.getSystemService(Context.VIBRATOR_SERVICE);    

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        button1 = (ImageButton) findViewById(R.id.imageButton1);
        button2 = (ImageButton) findViewById(R.id.imageButton2);
        button3 = (ImageButton) findViewById(R.id.imageButton3);
        button4 = (ImageButton) findViewById(R.id.imageButton4);
        button5 = (ImageButton) findViewById(R.id.imageButton5);
        button6 = (ImageButton) findViewById(R.id.imageButton6);
        button7 = (ImageButton) findViewById(R.id.imageButton7);




        button1.setOnClickListener(new View.OnClickListener() {


            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent goToTrypOne = new Intent("com.letsfly.tryp.TRYPONE");
                startActivity(goToTrypOne);

            }
        });
        button2.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent goToTrypTwo = new Intent("com.letsfly.tryp.TRYPTWO");
                startActivity(goToTrypTwo);


            }
        });
        button3.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent goToTrypThree = new Intent("com.letsfly.tryp.TRYPTHREE");
                startActivity(goToTrypThree);

            }
        });
        button4.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent goToTrypTwo = new Intent("com.letsfly.tryp.TRYPFOUR");
                startActivity(goToTrypTwo);

            }
        });
        button5.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent goToTrypFive = new Intent("com.letsfly.tryp.TRYPFIVE");
                startActivity(goToTrypFive);

            }
        });
        button6.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

            }

        });
        button7.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

            }

        });

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

MainActivity XML。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:gravity="center"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"

    tools:context=".MainActivity" >

   <ImageButton
        android:id="@+id/imageButton1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/button1"
        android:src="@drawable/button1"
        android:layout_weight="1"
        android:layout_gravity="center"/>
    <ImageButton
        android:id="@+id/imageButton2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/button2"
        android:src="@drawable/button2"
        android:layout_weight="1"
        android:layout_gravity="center" />
    <ImageButton
        android:id="@+id/imageButton3"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/button3"
        android:src="@drawable/button3"
        android:layout_weight="1"
        android:layout_gravity="center" />
    <ImageButton
        android:id="@+id/imageButton4"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/button4"
        android:src="@drawable/button4"
        android:layout_weight="1"
        android:layout_gravity="center" />
    <ImageButton
        android:id="@+id/imageButton5"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/button5"
        android:contentDescription="@string/button5"
        android:layout_weight="1"
        android:layout_gravity="center" />
    <ImageButton
        android:id="@+id/imageButton6"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/button6"
        android:src="@drawable/button6"
        android:layout_weight="1"
        android:layout_gravity="center" />
    <ImageButton
        android:id="@+id/imageButton7"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/button7"
        android:src="@drawable/button7"
        android:layout_weight="1"
        android:layout_gravity="center" />

</LinearLayout>

【问题讨论】:

  • 请添加logcat错误。
  • 图像文件的大小无关紧要,因为 PNG 和 JPG 应用图像压缩技术使图像数据小于实际大小。当您渲染这些图像或将它们应用于ImageButtonviews 时,图像被解码并占用比文件大小指示的更多的内存。例如,如果您的 PNG 为 1024x1024,全为红色,则文件可能非常小,但内存中的解码位图将是 1024 * 1024 * bytes_per_pixel 大。
  • 非常感谢您提供的这些信息,它确实可以解释并帮助很多,我的情况更有意义。再次感谢:)

标签: android bitmap out-of-memory heap-memory


【解决方案1】:

像素过大实际上是个问题,因为 Android 需要将压缩的 png 扩展为内存中未压缩的位图才能显示它们。

不要制作缩小以适应不同尺寸/密度的设备的大型 png,而是使用具有不同限定符的可绘制文件夹:

Images for mdpi devices: /res/drawable-mdpi
Images for hdpi devices: /res/drawable-hdpi
Images for xhdpi devices: /res/drawable-xhdpi
Images for xxhdpi devices: /res/drawable-xxhdpi

当您请求图像(例如,R.drawable.myImage)时,它会在具有与设备最匹配的密度限定符的文件夹中查找 myImage 文件。这使您可以自动确保小密度设备使用内存占用较小的图像。

更多信息在这里: http://developer.android.com/guide/practices/screens_support.html

【讨论】:

  • 感谢您的回复。这是我的第一个移动应用程序,所以很新而且理解起来很慢,所以请原谅。那么我应该将相同的 png 放在不同的文件夹中,还是相应地缩放它们?同样在我的代码中,我使用 id 并用id 调用它,如下所示:button1 = (ImageButton) findViewById(R.id.imageButton1); 这会有所不同,因为图像用作按钮而不仅仅是图像?更改该代码会干扰由button1控制的OnClickListener(),像这样开始......button1.setOnClickListener(new View.OnClickListener()
  • 您应该为每个文件夹相应地缩放它们 - 即 mdpi 应该具有比 hdpi 更小的图像。无论您在何处引用可绘制对象(不必是 ImageView),Android 都会根据可绘制文件夹限定符尝试为设备找到最合适的可绘制对象。您的点击监听器与此无关,不应有任何影响。
猜你喜欢
  • 2014-08-02
  • 2017-03-09
  • 2012-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多