Hello! I bring up the background, which will scroll slowly (as in Parallax Scrolling), the size of the scrollable image is 16k + by 720 pixels (7mb). When launched on powerful devices, it shows FPS 29 (HTC One), and on cheap Chinese tablets only 3 FPS (Texet TM-7024), and then throws it out altogether. Please help, how else can you optimize this code? I attach all the code of the class in which the scrolling goes.


package ru.zein4.g_break.views; import android.annotation.SuppressLint; import android.content.*; import android.graphics.*; import android.util.*; import android.view.*; import android.widget.*; public class background_menu extends SurfaceView implements SurfaceHolder.Callback { GameThread thread; int screenW; int screenH; int bgrW; int bgrH; int bgrScroll; int dBgrY; //Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΎΠΊΡ€ΡƒΡ‚ΠΊΠΈ background'a Bitmap bgr, bgrReverse; boolean reverseBackroundFirst; long timeNow; long timePrev = 0; long timePrevFrame = 0; long timeDelta; Paint exPaint = new Paint(); Rect fromRect1, toRect1, fromRect2, toRect2; public background_menu(Context context) { super(context); init(); } public background_menu (Context context, AttributeSet attrs) { super(context, attrs); init(); } public background_menu (Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public void init() { BitmapFactory.Options op = new BitmapFactory.Options(); op.inPreferenceConfig = Bitmap.Config_RGB565 bgr = BitmapFactory.decodeResource(getResources(), R.drawable.so_large_background, op); reverseBackroundFirst = false; //инициализируСмся bgrScroll = 0; //ΠΠ°Ρ‡Π°Π»ΡŒΠ½Π°Ρ позиция ΠΏΡ€ΠΎΠΊΡ€ΡƒΡ‚ΠΊΠΈ dBgrY = 1; //Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΎΠΊΡ€ΡƒΡ‚ΠΊΠΈ getHolder().addCallback(this); setFocusable(false); exPaint.setAntiAlias(false); exPaint.setFilterBitmap(true); } @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); screenW = w; screenH = h; bgr = Bitmap.createScaledBitmap(bgr, w * 2, h, true); bgrW = bgr.getWidth(); bgrH = bgr.getHeight(); //Π”Π΅Π»Π°Π΅ΠΌ Π²Ρ‚ΠΎΡ€ΠΎΠΉ ΠΎΡ‚Π·Π΅Ρ€ΠΊΠ°Π»Π΅Π½Π½Ρ‹ΠΉ Π±ΠΈΡ‚ΠΌΠ°ΠΏ Matrix matrix = new Matrix(); matrix.setScale(-1, 1); //ΠžΡ‚Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠΎ Π³ΠΎΡ€ΠΈΠ·ΠΎΠ½Π°Π»ΠΈ bgrReverse = Bitmap.createBitmap(bgr, 0, 0, bgrW, bgrH, matrix, true); //Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ Π±ΠΈΡ‚ΠΌΠ°ΠΏ ΠΈΠ· ΠΎΡ‚Π·Π΅Ρ€ΠΊΠ°Π»Π΅Π½Π½ΠΎΠΉ ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Ρ‹ } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); fromRect1 = new Rect(0, 0, bgrW - bgrScroll, bgrH); toRect1 = new Rect(bgrScroll, 0, bgrW, bgrH); fromRect2 = new Rect(bgrW - bgrScroll, 0, bgrW, bgrH); toRect2 = new Rect(0, 0, bgrScroll, bgrH); if (!reverseBackroundFirst) { canvas.drawBitmap(bgr, fromRect1, toRect1, exPaint); canvas.drawBitmap(bgrReverse, fromRect2, toRect2, exPaint); } else{ canvas.drawBitmap(bgr, fromRect2, toRect2, exPaint); canvas.drawBitmap(bgrReverse, fromRect1, toRect1, exPaint); } if ((bgrScroll += dBgrY) >= bgrW) { bgrScroll = 0; reverseBackroundFirst = !reverseBackroundFirst; } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { thread = new GameThread(getHolder(), this); thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; thread.setRunning(false); while (retry) { try { thread.join(); retry = false; } catch (Exception e) {e.printStackTrace();} } } class GameThread extends Thread { private SurfaceHolder surfaceHolder; private background_menu gameView; private boolean run = false; public GameThread(SurfaceHolder surfaceHolder, background_menu gameView) { this.surfaceHolder = surfaceHolder; this.gameView = gameView; } public void setRunning(boolean run) { this.run = run; } public SurfaceHolder getSurfaceHolder() { return surfaceHolder; } @SuppressLint("WrongCall") @Override public void run() { Canvas c; while (run) { c = null; //Π”Π΅Π»Π°Π΅ΠΌ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ background'a кадронСзависимым. Число 16 Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ максимально допустимый FPS Π±ΡƒΠ΄Π΅Ρ‚ 60 timeNow = System.currentTimeMillis(); timeDelta = timeNow - timePrevFrame; if (timeDelta < 16) { try { Thread.sleep(16 - timeDelta); } catch (Exception e) { e.printStackTrace(); } } timePrevFrame = System.currentTimeMillis(); try { c = surfaceHolder.lockCanvas(null); if (c != null) { synchronized (surfaceHolder) { gameView.onDraw(c); } } } finally { if (c != null) { surfaceHolder.unlockCanvasAndPost(c); } } } } } } 

Now I calculated the amount of RAM needed to decode a resource: 16200*720*4 = ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ 47 ΠΌΠ± . Somewhere I heard that the amount of required memory can be reduced using Bitmap.Config_RGB565 . How to apply this config?


UPDATE So, now I screwed the config to the bitmap and did the drawing without anti-aliasing, the FPS increased from 3 to 14 on Chinese devices. But optimization is still required.


  • one
    I started to learn the android current, but maybe we should divide the picture, say for (1000x720) x16 and keep only 3-4 in memory? // - 1 // is the current // + 1 // + 2 or is there zero sense from this? according to my calculations it is ((47/16)*4)= 12mb (or 15mb if +\- 2 blocks) - ProkletyiPirat
  • @ProkletyiPirat, I have already tried to do something like this, but when I needed a transition between the blocks, then black bands often appeared and jerked at their junction. - Helisia
  • maybe just loading was performed sequentially and not in parallel? or rendered without caching? - ProkletyiPirat
  • @ProkletyiPirat, honestly, I don’t remember. But then it was a great blow to my nerves ... - Helisia
  • one
    @SuperCreeper and now it’s clear that you are trying to make a la "Mario" and the background is represented by one picture, from whether it is of such insane resolution. Then you can make tiles for sure there! (that's why I wanted to look at the picture), and apparently when you had black stripes and twitching you took each element in turn, instead of collecting everything and then bringing it out all at once. - ProkletyiPirat

3 answers 3

I have exhausted the limit of comments to the question, therefore I will answer this way.

I didn’t see any good articles on the tile graphics itself (and I wasn’t looking honestly). I saw an example of tile graphics in one book, but the author of this book is deservedly insulted throughout the Internet (for good reason).




bicycle:

the idea of ​​tiles + caching (always with a cache!) is very simple

  1. create a Ρ„ΠΎΠ½ object inside store a common map (array of pixels)
  2. we create an object a ΡΡ‚Ρ€ΠΎΠΈΡ‚Π΅Π»ΡŒ Ρ„ΠΎΠ½Π° that internally stores the Ρ„ΠΎΠ½ and the size of the background in the cells (N x M), it also stores information about the size of the cell in pixels (and that which does not fit will color the default color)
  3. create a function that inserts a tile into a cell
  4. when all tiles are inserted in one fell swoop we draw the whole array

We spice this soup hells with spices in the form of parallel loading of tiles, etc. buns.




correct implementation

  1. We take the editor of the tiles (they are dead!), we adjust the size in pixels and the number of tiles and a little bit we draw (or copy-paste).
  2. at the output we have a bunch of tiles sorted and sawn, as well as an xml document describing the map, in especially good editors there are also xml sets (house, bridge, tree, etc)
  3. we find a library for working with tiles (their master is already dead)
  4. We write an adapter that pulls out the order of tiles from the xml and shoves them into the library along with the tiles themselves.

small remnants (those that cannot be cut into tiles) bays with a stretching image (9Patch)

Also in the normal library, tile animation will be implemented and it can even combine N tiles into one.

    Why not make 10-20 images, and keep them in control, which will solve memory problems for you? (for example ListView )

    • Because then on this SurfaceView you plan to draw other objects. And to divide such a huge image into pieces will be even more difficult. - Helisia
    • The problem is that either you are reducing the image (loss of quality), or trying to present it as a collection. As another "not very beautiful option" - you will try to shove the image into the webview, and do the remaining operations on another layer. But ask yourself about the correctness of the idea itself. - jimpanzer

    Is this background special? Or is there a sequence?

    1. You can look towards 9Patch.

    2. And one more tip. Make the most concise jpge image. I think that you simply do not need PNG, since you already have the whole area filled up.

    3. Also I recommend to look at old devices 2.3. Most likely there will be OutOfMemory right away.

    4. Break the picture into tiles and use them.

    • On the phone with 2.1, does not throw it out with the updated code - Helisia