【问题标题】:Android - onPause and WidgetLockerAndroid - onPause 和 WidgetLocker
【发布时间】:2012-01-17 00:33:25
【问题描述】:

我正在编写一个基本应用程序来测试加速度计的行为。目前我正在像这样注册和注销加速度计:

public void pause() {
    mSensorManager.unregisterListener(this);
}

public void resume(Context context) {
    mSensorManager.registerListener(this, mAccelerometer,     
            SensorManager.SENSOR_DELAY_FASTEST);        
}

我有 Activity 触发的暂停和恢复方法,因为加速度计在子类中运行。当我锁定屏幕屏幕时,程序会中断。实际上,我必须结束该过程才能使程序再次运行。我目前正在使用 WidgetLocker。该程序适用于股票锁屏。我的假设是简历永远不会被运行。

有没有办法确保传感器重新注册?如果他们使用正常锁屏之外的任何内容,我不希望我的程序中断。

编辑:添加了完整的源代码。关于这个问题的主要逻辑发生在 MainGamePanel.java 的底部

TiltBallActivity.java

package com.tornquist.nathan;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class TiltBallActivity extends Activity{
    /** Called when the activity is first created. */
    MainGamePanel viewPanel;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Window state functions.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        //This works without declaring a viewPanel instance here.
        //The instance declaration is needed to pass the 
        //onPause and onResume commands though.
        viewPanel = new MainGamePanel(this);
        setContentView(viewPanel);
    }

    //Restarts the accelerometer after onPause
    protected void onResume() {

        viewPanel.resume(this);
        super.onResume();
    }

    //Standard Method run when the Application loses focus.
    //This runs the pause() function in the viewPanel so that
    //the accelerometer can be paused.
    protected void onPause() {

        viewPanel.pause();
        super.onPause(); 
    }
}

MainThread.java

package com.tornquist.nathan;

import com.tornquist.nathan.MainGamePanel;
import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

    private SurfaceHolder surfaceHolder;
    private MainGamePanel gamePanel;
    private boolean running;
    public void setRunning(boolean running) {
        this.running = running;
    }

    public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
        super();
        this.surfaceHolder = surfaceHolder;
        this.gamePanel = gamePanel;
    }

    @Override
    public void run() 
    {
        Canvas canvas;
        while (running) {
            canvas = null;
            // try locking the canvas for exclusive pixel editing on the surface
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder) {
                    // update game state
                    this.gamePanel.update();

                    // draws the canvas on the panel
                    this.gamePanel.onDraw(canvas);
                }
            } finally {
                // in case of an exception the surface is not left in
                // an inconsistent state
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }   // end finally
        }
    }
}

MainGamePanel.java

package com.tornquist.nathan;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainGamePanel extends SurfaceView implements SensorEventListener, SurfaceHolder.Callback 
{
    //Variable Declarations.
    private MainThread thread;
    public int xColor;
    public int yColor;
    public int zColor;

    public float xPos;
    public float yPos;
    public float oldXPos;
    public float oldYPos;

    public int screenWidth;
    public int screenHeight;

    private SensorManager mSensorManager;
    private Sensor mAccelerometer;    

    Paint paint;

    public MainGamePanel(Context context)
    {
        //Standard Initialization
        super(context);

        //This line adds a callback for the touch screen.  
        //This allows you to actually capture touch input.
        getHolder().addCallback(this);

        thread = new MainThread(getHolder(),this);

        xColor = 100;
        yColor = 100;
        zColor = 100;

        paint = new Paint();
        paint.setAntiAlias(true);

        Display display = ((Activity) context).getWindowManager().getDefaultDisplay(); 
        screenWidth = display.getWidth();
        screenHeight = display.getHeight();

        yPos = screenHeight/2;
        xPos = screenWidth/2;
        oldYPos = yPos;
        oldXPos = xPos;

        //This registers the accelerometer.  Without registering it, the onSensorChanged
        //event will never be called, and you cannot get the accelerometer values.
        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);

        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);

        //Also required for touch input.
        setFocusable(true);
    }

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

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // at this point the surface is created and
        // we can safely start the game loop
        thread.setRunning(true);
        thread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // try again shutting down the thread
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        // check if in the lower part of the screen we exit
            if (event.getY() > getHeight() - 50) {
                thread.setRunning(false);
                ((Activity)getContext()).finish();
            }

            if (xColor < 235)
                xColor = xColor + 20;
            else
                xColor = 0;
            if (yColor < 235)
                yColor = yColor + 20;
            else
                yColor = 0;
            if (zColor < 235)
                zColor = zColor + 20;
            else
                zColor = 0;

            yPos = event.getY();
            xPos = event.getX();
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) 
    {
        //canvas.drawColor(Color.CYAN);
        canvas.drawColor(Color.rgb(xColor, yColor, zColor));
        int xInvert = (int) (255 - xColor);
        int yInvert = (int) (255 - yColor);
        int zInvert = (int) (255 - zColor);

        paint.setColor(Color.rgb(xInvert, yInvert, zInvert));
        paint.setStyle(Style.FILL);
        canvas.drawCircle(xPos, yPos, 50, paint);
    }

    public void update() {
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {        
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        oldYPos = yPos;
        oldXPos = xPos;

        xColor = (int) (255 + (event.values[0])*11);
        if (xColor > 255)
            xColor = 255;
        if (xColor < 0)
            xColor = 0;

        yColor = (int) (255 + (event.values[1])*11);
        if (yColor > 255)
            yColor = 255;
        if (yColor < 0)
            yColor = 0;

        zColor = (int) (255 + (event.values[2])*11);
        if (zColor > 255)
            zColor = 255;
        if (zColor < 0)
            zColor = 0;

        xPos = xPos + -1*(event.values[0])*5;
        yPos = yPos + event.values[1]*5;

        if (xPos < 50)
            xPos = 50;
        if (xPos > screenWidth - 50)
            xPos = screenWidth - 50;
        if (yPos < 50)
            yPos = 50;
        if (yPos > screenHeight - 50)
            yPos = screenHeight - 50;

        if ((oldYPos == yPos) && (oldXPos == xPos))
        {
            invalidate();
        }

    }

    public void pause() {
        mSensorManager.unregisterListener(this);

        //thread.setRunning(false);
        //((Activity)getContext()).finish();
    }

    public void resume(Context context) {
        //mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);

        //mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);      
    }


}

【问题讨论】:

  • 你使用pauseresume,还是onPauseonResume
  • onPause 和 onResume。 pause 和 resume 在子类中。它们由 onPause 和 onResume 方法执行。正如我上面所说,当我不使用 WidgetLocker 时,这非常有效。不过,我需要针对任何情况进行编程。
  • 您也可以发布您的活动代码吗?

标签: java android accelerometer onpause


【解决方案1】:

我认为您可以使用 ACTION_SCREEN_ON 和 ACTION_SCREEN_OFF Intent 操作,而不必处理 onPause 和 onResume 事情。所以是这样的:

mReceiver = new BroadcastReceiver() {
     public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (Intent.ACTION_SCREEN_OFF.equals(action)) {
        //deregister
    }
    else if (Intent.ACTION_SCREEN_ON.equals(action)) {
        //register
    }
}};

IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
registerReceiver(mReceiver, filter); 

filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(mReceiver, filter);

并在 onDestroy() 方法中注销此接收器


好的,我想我找到了解决方案。 onSurfaceDestroyed 方法代码中的 thread.join 部分以某种方式阻止了活动。我对您的代码进行了一些修改,这对我有用:

public MainGamePanel(Context context)
{
    ...

    //Also required for touch input.
    setFocusable(true);

    //start thread
    thread.setRunning(true);
    thread.start();
}

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

@Override
public void surfaceCreated(SurfaceHolder holder) {
    //continue the thread
    synchronized (thread) {
        thread.pleaseWait = false;
        thread.notifyAll();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    //pause the thread
    synchronized (thread) {
        thread.pleaseWait = true;
    }
}

还有线程类

public class MainThread extends Thread {

private SurfaceHolder surfaceHolder;
private MainGamePanel gamePanel;
private boolean running;
public boolean pleaseWait = true;
public void setRunning(boolean running) {
    this.running = running;
}

public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
    super();
    this.surfaceHolder = surfaceHolder;
    this.gamePanel = gamePanel;
}

@Override
public void run() 
{
    Canvas canvas;
    while (running) {
        if(!pleaseWait) {
            canvas = null;
            // try locking the canvas for exclusive pixel editing on the surface
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder) {
                    // update game state
                    this.gamePanel.update();

                    // draws the canvas on the panel
                    this.gamePanel.onDraw(canvas);
                }
            } finally {
                // in case of an exception the surface is not left in
                // an inconsistent state
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }   // end finally            
        }
        else {
            synchronized (this) {
                try {
                    wait();
                } catch (Exception e) { }
            }
        }
    }
}
}

并在onDestroy方法中销毁线程

【讨论】:

  • 我当然会尝试,但最终这并不能解决问题。如果有人切换应用程序,我的应用程序将继续消耗电池。解决问题的唯一方法是,如果他们总是从我的应用程序中锁定屏幕,然后解锁屏幕以直接返回它。
  • 我调试你的代码:我认为电池耗尽问题是由于线程。我觉得你应该暂停一下。再想一想,当我调试你的代码时,onResume 不会为我触发。
  • onResume 没有触发是我要问的全部问题。
  • 这似乎工作得很好。你能试着解释一下到底出了什么问题吗?线程没有被暂停并最终暂停一半的应用程序破坏了它?我需要添加 onDestroy 方法吗? --- 编辑:我可以只使用 thread = null;在 onDestroy() 方法中?
  • 是的,您的代码的主要问题是 thread.join 部分。 onSurfaceDestroy 将始终在 onPause 之后触发,并且您的代码会阻止活动进程,因为您没有调用 thread.setRunning(false)。这不能解决您的问题,因为您必须重新初始化 onSurfaceCreate 中的线程。一个更优雅的解决方案是暂停线程并继续它。在 onDestroy: 在 MainGamePanel 中创建一个停止方法,并在那里复制你的线程停止代码,但将运行字段设置为 false。您不能将线程设置为 null,因为线程将继续执行
【解决方案2】:

我认为,如果您使用 onCreate() 进行注册并使用 onDestroy() 取消注册,那将无法解决您的问题。因为您的 onDestory 在屏幕锁定时不会被调用,这可能会解决您的问题。

【讨论】:

  • 我需要在屏幕锁定时取消注册加速度计。否则电池会很快耗尽。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 2015-05-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
相关资源
最近更新 更多