You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发中如何让多个类访问同一个类实例?

解决多类共享同一实例的问题

嘿,我完全懂你的困扰——你想让GameView负责绘制炮塔,Logic负责更新它的位置,两者共用同一个炮塔实例,而不是各自创建新的,对吧?这在游戏开发里是非常常见的需求,咱们用两种实用的方法来解决它:


方法一:构造函数传递实例(推荐,灵活可控)

这种方法最直观,也最适合游戏场景(毕竟你可能以后会有多个炮塔实例)。核心思路是:在统一的入口处创建炮塔实例,然后把这个实例传递给需要它的GameView和Logic。

步骤1:修改GameView,接收外部传入的炮塔实例

原来的GameView里自己没初始化simpleTurret,直接调用会空指针,咱们改成从外部接收:

public class GameView extends SurfaceView implements Runnable {
    private boolean running = true;
    SurfaceHolder surfaceHolder = getHolder();
    SimpleTurret simpleTurret;

    // 构造函数新增SimpleTurret参数
    public GameView (Context context, SimpleTurret turret){
        super(context);
        this.simpleTurret = turret; // 绑定外部传入的共享实例
        Thread thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        while (running){
            DrawCanvas();
            // 加个小延时,避免线程占用过高CPU
            try {
                Thread.sleep(16); // 对应约60帧/秒的刷新频率
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void DrawCanvas(){
        Canvas canvas = surfaceHolder.lockCanvas();
        if (surfaceHolder.getSurface().isValid()){
            canvas.drawColor(Color.RED);
            // 现在simpleTurret已经有实例了,不会空指针
            canvas.drawBitmap(simpleTurret.getSprite(), simpleTurret.getX(), simpleTurret.getY(), null);
            surfaceHolder.unlockCanvasAndPost(canvas);
        }
    }
}

步骤2:修改Logic,同样接收共享实例

原来的Logic里自己创建了新的SimpleTurret,现在改成接收外部实例:

public class Logic implements Runnable{
    Boolean isRunning;
    private Thread logicThread;
    SimpleTurret simpleTurret;

    // 构造函数新增SimpleTurret参数
    public Logic(Context context, boolean running, SimpleTurret turret){
        this.simpleTurret = turret; // 绑定同一个共享实例
        logicThread = new Thread(this);
        logicThread.start();
        isRunning = running;
    }

    @Override
    public void run() {
        while (isRunning){
            simpleTurret.isMoving(); // 直接操作共享实例更新位置
            // 同样加延时,和绘制线程频率匹配
            try {
                Thread.sleep(16);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

步骤3:在入口处创建实例并传递

比如在你的MainActivity里(或者其他初始化游戏的地方),统一创建炮塔实例,再传给两个类:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 创建唯一的炮塔实例
    SimpleTurret sharedTurret = new SimpleTurret(this);
    // 把实例传给GameView和Logic
    GameView gameView = new GameView(this, sharedTurret);
    Logic gameLogic = new Logic(this, true, sharedTurret);
    setContentView(gameView);
}

方法二:单例模式(适合全局唯一的炮塔)

如果你确定整个游戏里只会有一个SimpleTurret,那可以用单例模式,让它全局只有一个实例,任何类都能直接获取。

修改SimpleTurret为单例类

public class SimpleTurret extends Turrets {
    // 静态变量存储唯一实例
    private static SimpleTurret instance;

    // 私有构造函数,禁止外部直接new
    private SimpleTurret(Context context){
        super(context);
        sprite = BitmapFactory.decodeResource(context.getResources(), R.drawable.test_sprite);
        health = 50;
        x = 300;
        y = 100;
        speed = 1;
    }

    // 全局获取实例的方法,确保只有一个实例
    public static synchronized SimpleTurret getInstance(Context context){
        if (instance == null){
            // 用ApplicationContext避免内存泄漏
            instance = new SimpleTurret(context.getApplicationContext());
        }
        return instance;
    }
}

在GameView和Logic中获取单例

// GameView的构造函数里
public GameView (Context context){
    super(context);
    // 获取全局唯一的炮塔实例
    simpleTurret = SimpleTurret.getInstance(context);
    Thread thread = new Thread(this);
    thread.start();
}

// Logic的构造函数里
public Logic(Context context, boolean running){
    // 获取同一个全局实例
    simpleTurret = SimpleTurret.getInstance(context);
    logicThread = new Thread(this);
    logicThread.start();
    isRunning = running;
}

额外注意事项

  1. 空指针问题:你原来的代码里GameView的simpleTurret和rapidFireTurret都没初始化,直接调用方法会崩溃,上面的两种方法都解决了这个问题。
  2. 线程安全:多线程操作同一个实例时,要注意变量的线程安全(比如炮塔的x、y可以用volatile修饰,或者用同步块)。
  3. 内存泄漏:单例模式里一定要用ApplicationContext,不要用Activity的Context,否则会导致Activity无法被回收。

内容的提问来源于stack exchange,提问作者Frenchie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:02:30