Search code examples
javaandroidmultithreadinghandlersurfaceview

How to call another activity from drawing thread (SurfaceView)


The goal is something like this: I start the app, MainActivity starts a drawing thread. This thread draws until some events inside it happens(in my case value i>155) Then this thread stops drawing (but it should not be deleted, and values should not be lost) and starts another activity (a usual xml - file activity) When I want I should be able to get to my drawing thread and resume communicating with it from the place I stooped

To solve the problem I have decided to use a SurfaceView thread and Canvas inside (It would be good to make it a class, not to write it inside MainActivity, because I'm going to insert there a lot of code). I managed to start a thread and to draw something on my screen, but I can't change the activity. The best attempt was to create a handler inside MainActivity and to send there a message from my thread. The thread works OK, but it just freezes and nothing happens when it is time to change the Activity. In the log tab I can see that the thread sends a message, but the MainActivity can't see and handle it.

What should I do? What is the right eay of solving the problem? Please, help a beginner).

MainActivity code:

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends Activity {

    public  Handler  h  =new  Handler() {
        public void handleMessage(android.os.Message msg) {
            Log.i("","recieved");

            if(msg.what==1){
                Intent intent = new Intent(MainActivity.this,ScrollingActivity.class);
                MainActivity.this.startActivity(intent);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new DrawView(this));

    }

    class DrawView extends SurfaceView implements SurfaceHolder.Callback {

        private DrawThread drawThread;

        public DrawView(Context context) {
            super(context);
            getHolder().addCallback(this);
        }

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

        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            drawThread = new DrawThread(getHolder());
            drawThread.setRunning(true);
            drawThread.start();
        }

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

    }

}

DrawThread class code:

import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Looper;
import android.util.Log;
import android.view.SurfaceHolder;

class DrawThread extends Thread {
    Canvas canvas;
    int i=0;
    private boolean running = false;
    private SurfaceHolder surfaceHolder;

    public DrawThread(SurfaceHolder surfaceHolder) {
        this.surfaceHolder = surfaceHolder;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public void run() {
        Looper.prepare();
        while (running) {
            canvas = null;
            try {
                canvas = surfaceHolder.lockCanvas(null);
                draw();
            } finally {
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }
    public void draw(){

        canvas.drawColor(Color.rgb(i,i,i));
        i++;
        if(i>155){
            i=0;
            running=false;
            sender snd = new sender();
            snd.send(1);


        }
    }
    private class sender extends MainActivity{
        public void send(int i){
            Log.i("","sending");
            try {
                h.sendEmptyMessage(i);
                Log.i("","sent");
            } catch (Exception e) {
                e.printStackTrace();
            }


        }
    }
}

Solution

  • The answer was easy:I had to replace everything into one file (into MainAcitivity class, as here https://startandroid.ru/ru/uroki/vse-uroki-spiskom/311-urok-141-risovanie-dostup-k-canvas.html) and to create a runnable, wich calls an activity. Then every time you open MainActivity the thread of graphics starts, and when it is time to change the Activity it calls that runnable using runOnUIThread(runnable);. It pauses MainActivity and starts a new activity(!! any pausing MainActivity destroys our graphical thread, any resuming starts it again, as I have understood). On closing the new activity you get back to the MainActivity and our thread starts again. In case you do not want to lose your values of graphical thread, you may declare those values inside your activity class, not inside thread class(as the thread will be destroyed on closing the Activitie's screen.(I do not see any other ways out)

    P. S. If there are any mistakes, please, correct me. But the code should work correctly.

    import android.app.Activity;
    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    public class MainActivity extends Activity {
        int i=0;
        //boolean firstRun=true;
        Runnable call = new Runnable() {
            public void run() {
                Log.i("","recieved");
                Intent intent=new Intent(MainActivity.this,ScrollingActivity.class);
    
                 startActivity(intent);
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(new DrawView(this));
        }
    
        class DrawView extends SurfaceView implements SurfaceHolder.Callback {
    
            private DrawThread drawThread;
    
            public DrawView(Context context) {
                super(context);
                getHolder().addCallback(this);
            }
    
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                       int height) {
    
            }
    
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Log.i("","created");
                    drawThread = new DrawThread(getHolder());
                    drawThread.setRunning(true);
                    drawThread.start();
            }
    
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                Log.i("","destroyed");
                boolean retry = true;
                drawThread.setRunning(false);
                while (retry) {
                    try {
                        drawThread.join();
                        retry = false;
                    } catch (InterruptedException e) {
                    }
                }
            }
    
            class DrawThread extends Thread {
    
                private boolean drawing=true;
                private boolean running = false;
                private SurfaceHolder surfaceHolder;
    
                public DrawThread(SurfaceHolder surfaceHolder) {
                    this.surfaceHolder = surfaceHolder;
                }
    
                public void setRunning(boolean running) {
                    this.running = running;
                }
    
                @Override
                public void run() {
                    Canvas canvas;
                    while (running) {
                        Log.i("","running");
                        if (drawing) {
                            canvas = null;
                            try {
                                canvas = surfaceHolder.lockCanvas(null);
                                if (canvas == null)
                                    continue;
                                canvas.drawColor(Color.rgb(i, i, i));
                                i++;
                                if (i == 100) {
                                    drawing = false;
                                    runOnUiThread(call);
                                }
                                if(i>255){
                                    i=0;
                                }
                            } finally {
                                if (canvas != null) {
                                    surfaceHolder.unlockCanvasAndPost(canvas);
                                }
                            }
                        }
                    }
                }
            }
    
        }
    
    }