用碎片耗尽内存

时间:2017-08-29 20:39:02

标签: java android android-fragments out-of-memory

我正在使用android studio制作书籍应用程序。这本书由35个图像组成,每个图像的文件大小约为4.8MB(参见编辑)。我们目前正在使用选项卡式活动并为每个页面创建一个片段来处理它们。但这意味着所有片段(所有35个图像)都在同时处理,这会导致内存问题。在Android工作室崩溃之前我可以在手机上获得16张图像,而项目中的其他人可以使用模拟器在他的笔记本电脑上显示33张图像。

我们有35个遵循此模板的java类:

package com.example.leesie.bookautism;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

/**
 * Created by leesie on 23/08/2017.
 */

public class Tab1Fragment extends Fragment {
    private static final String TAG = "Tab1Fragment";

    private Button btnTEST1;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.tab1_fragment,container,false);

        btnTEST1 = (Button) view.findViewById(R.id.btnTEST1);

        btnTEST1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getActivity(), "TESTING BUTTON CLICK 1",Toast.LENGTH_SHORT).show();
            }
        });

        return view;
    }
}

谁的xml文件如下:

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:textSize="25sp"
        android:text="Tab1"
        android:layout_marginTop="40dp"
        android:id="@+id/textTab1"/>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/Tab1ImageView"
        android:src="@drawable/tab1"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btnTEST1"
        android:text="TESTBTN 1"/>

</RelativeLayout>

主要活动是:

        package com.example.leesie.bookautism;

    import android.os.Bundle;
    //import android.support.design.widget.TabLayout;
    import android.support.v4.view.ViewPager;
    import android.support.v7.app.AppCompatActivity;
    //import android.support.v7.widget.Toolbar;
    import android.util.Log;
    //import android.view.LayoutInflater;
    //import android.view.View;
    //import android.view.ViewGroup;
    //import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

private static final String TAG = "MainActivity";

private SectionsPageAdapter mSectionsPageAdapter;

private ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.d(TAG, "onCreate: Starting.");

    /* In case we need a toolbar THIS OR THAT
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    */

    // Create the adapter that will return a fragment for each of the three
    // primary sections of the activity.
    mSectionsPageAdapter = new SectionsPageAdapter(getSupportFragmentManager());

    // Set up the ViewPager with the sections adapter.
    mViewPager = (ViewPager) findViewById(R.id.container);
    setupViewPager(mViewPager);

    /* In case we need tabs THIS OR THAT
    mSectionsPageAdapter = new SectionsPageAdapter(getSupportFragmentManager());

    // Set up the ViewPager with the sections adapter.
    mViewPager = (ViewPager) findViewById(R.id.container);
    setupViewPager(mViewPager);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(mViewPager);
    */

}

private void setupViewPager(ViewPager viewPager) {
    SectionsPageAdapter adapter = new SectionsPageAdapter(getSupportFragmentManager());
    adapter.addFragment(new CoverFragment(), "COVER");
    adapter.addFragment(new CopyrightFragment(), "COPYRIGHT");
    adapter.addFragment(new Tab1Fragment(), "TAB1");
    adapter.addFragment(new Tab2Fragment(), "TAB2");
    adapter.addFragment(new Tab3Fragment(), "TAB3");
    ...
    adapter.addFragment(new Tab33Fragment(), "TAB33");

    viewPager.setAdapter(adapter);
}

}

最后,页面适配器:

package com.example.leesie.bookautism;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by JohnB on 23/08/2017.
 */

public class SectionsPageAdapter extends FragmentPagerAdapter {

    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    public SectionsPageAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }
}

我们想到的可能解决方案可能是创建三个活动,其中每个活动都有三分之一的片段,一旦显示活动A中的最后一张图片,就会从活动A移动到活动B. 这是一个可能的解决方案吗?

这似乎是编写代码的一种相当差的方式,是否有更好的解决方案?

此外,是否有任何资源可以帮助解决我们的问题?

非常感谢

修改

我错了,所有图片在一起的文件大小为4.8MB。

2 个答案:

答案 0 :(得分:1)

首先,4.8MB PNG,JPEG或WebP文件巨大。该图像的分辨率必须在每面数万个像素。没有具有该分辨率屏幕的Android设备。另外,作为Bitmap的解压缩图像将是数十或数百MB的堆空间,这是您不具备的。坦率地说,我很难相信在遇到OutOfMemoryError之前你可以加载16个。

所以,你需要决定的第一件事是:拥有如此高分辨率的图像有什么意义?现在,您正在使用ImageView,这意味着您的首要任务是将这些图片的分辨率降低到合理的范围。 1080p(1920x1080,根据所需的宽高比提供或拍摄)将是一个合理的起点,或者可能更低,具体取决于您的目标受众。

接下来,您似乎将这些图像作为可绘制资源。如果是这样,请确保它们位于res/drawable-nodpi/res/drawable-anydpi/,而不是任何其他可绘制目录。理想情况下,根本不要将它们作为资源,而是使用资产或其他东西,以便完全控制内存管理。

您可能还会重新考虑媒体格式的选择,切换到HTML。例如,my main book作为APK文件发布,并且在比您的应用需要更少的磁盘空间中,我有超过4,500页的素材,包括数百个屏幕截图和其他图像。

  

这是一个可能的解决方案吗?

不是真的。您的问题不是直接与片段有关。您的问题在于图像,特别是因为您正在使用资源。仅仅因为你使用第二个活动并不会导致第一个活动的记忆神奇地自由释放。

  

这似乎是编写代码的一种相当差的方法,是否有更好的解决方案?

您没有说明如何实施标签。如果您使用的是基于ViewPager的内容,请确保您使用的是FragmentStatePagerAdapter或其他PagerAdapter实施内容,而这些内容并不能保留内存中的所有内容。您需要确保其中一些图像尽快耗尽内存,一种方法是确保ViewPagerPagerAdapter可以在用户导航时释放页面。

答案 1 :(得分:1)

如果您一次只显示一个页面,则可以对每个具有生命周期的页面使用ViewPager,并销毁未使用的(看不见的)位图。

如果你必须在拇指的情况下显示所有内容,你可以加载options.inScale = 4或更多,这样你可以占用更少的内存空间。