Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logic to use multiple webview in TabLayout to handle memory issue

I have almost 30 webview in a Tablayout. Everything works fine but my app consumes a lot of memory and will just close because of memory issue. This is what I get in log

04-05 21:00:09.458 19720-19801/com.example.choman.webview A/chromium: [FATAL:memory.cc(19)] Out of memory. size=16777216

This is my java file. Basically all the remaining 29 fragments contain the same code with just a change in the url. I am not sure how to handle this.

public class stackOverflow extends Fragment {


    private WebView webView;
    private ProgressBar progressBar1;
    private SwipeRefreshLayout mSwipeRefreshLayout1;




    public stackOverflow() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment


        return inflater.inflate(R.layout.fragment_tab1, container, false);


    }



    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {


        progressBar1 = (ProgressBar) view.findViewById(R.id.progressBar1);

        webView = (WebView) view.findViewById(R.id.website_detail_1);

        webView.setWebViewClient(new MyAppWebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setDomStorageEnabled(true);

        webView.loadUrl("http://www.stackoverflow.com");


        WebSettings webSettings = webView.getSettings();

        webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        webView.getSettings().setAppCacheEnabled(true);
        webSettings.setDomStorageEnabled(true);

        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);


        mSwipeRefreshLayout1 = (SwipeRefreshLayout) view.findViewById(R.id.swipe1);
        mSwipeRefreshLayout1.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                webView.loadUrl("http://www.stackoverflow.com");
            }
        });

        if (mSwipeRefreshLayout1.isRefreshing()) {
            mSwipeRefreshLayout1.setRefreshing(false);
        }


        webView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
                    webView.goBack();
                    return true;
                }

                return false;
            }
        });

    }

    public class MyAppWebViewClient extends WebViewClient {

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            //view.findViewById(R.id.progressBar1).setVisibility(View.GONE);
            Log.i("pageFinished", "yesss");
            //progressBar.setVisibility(View.INVISIBLE);
            progressBar1.setVisibility(View.GONE);


            if (mSwipeRefreshLayout1.isRefreshing()) {
                mSwipeRefreshLayout1.setRefreshing(false);
            }


        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }


        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }


    }
}

Mainactivity

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {
    public WebView view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);


        tabLayout.addTab(tabLayout.newTab().setText("WebView1").setTag("WebView1"));
//all the way down to .....
tabLayout.addTab(tabLayout.newTab().setText("WebView30").setTag("WebView30"));        

        final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
        viewPager.setOffscreenPageLimit(6);


        final PagerAdapter adapter = new TabPagerAdapter(getSupportFragmentManager(), tabLayout
                .getTabCount());
        viewPager.setAdapter(adapter);

        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(
                tabLayout));
        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });


        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @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;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        //    int id = item.getItemId();

//        //noinspection SimplifiableIfStatement
//        if (id == R.id.action_settings) {
//            return true;
//        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {

        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);

        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.WebView1) {

            viewPager.setCurrentItem(0);

//all the way down to..30

 if (id == R.id.WebView30) {

            viewPager.setCurrentItem(30);






        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }
}

This is the code for TabPagerAdapter

public class TabPagerAdapter extends FragmentStatePagerAdapter
{

    int tabCount;


    public TabPagerAdapter(FragmentManager fm, int numberOfTabs)
    {
        super(fm);
        this.tabCount = numberOfTabs;

    }


    @Override
    public Fragment getItem(int position)
    {
        switch (position)
        {
            case 0:
//
                WebViewFragment1 tab1 = new WebViewFragment1();
                return tab1;

   //all the way to tab 29

    webViewFragment30 tab30 = new WebViewFragment30();
return tab30



            default:
                return null;

        }

    }

    @Override
    public int getCount()
    {
        return tabCount;
    }
}
like image 330
choman Avatar asked Apr 05 '16 15:04

choman


2 Answers

Use ViewPager. ViewPager will hold maximum 3 fragments in memory at any time(without changing the default offscreen page limit). The 3 fragments are the current visible fragment, left and right side fragments to the current fragment.

If you want tabs, you could create a custom View which looks like the system Tabs and add it above the ViewPager. Use PageTransformer with ViewPager to track the ViewPager swipe and scroll the custom Tab accordingly. Then listen to the clicks of Tabs and scroll the ViewPager based on the Tab pressed.

Update:

I totally forgot that we could attach a TabLayout with a ViewPager instead of creating custom Tabs.

tabLayout.setUpWithViewPager(viewpager) will work.

Thanks to @choman

like image 169
Bob Avatar answered Oct 02 '22 15:10

Bob


viewPager.setOffscreenPageLimit(6); This line is causing the issue. This line tells the view pager to keep (6+6+1 = 13) pages in memory which should be costly in your case since each page has 6.5 Mb of contents. You need to change it to viewPager.setOffscreenPageLimit(1) to fix the OutOfMemory issue.

like image 21
Abhishek V Avatar answered Oct 02 '22 14:10

Abhishek V