Seems like my little android app is running multip

2019-07-02 16:56发布

问题:

I know this is very lame but I'm totally new to android development. I'm writing a sensor based app which will change wallpaper each time the phone is shaked. Once the app is minimized it runs in background.

It works wonderfully when I run it for the first time.But When I minimize it and re-open it looks like 2 instances of the app are running. So it goes on. Every time I minimize and open the app, looks like one more instance is started in parallel.

Problem it causes:
1: Multiple instances of same app listening to "shake"
2: Multiple instances of same app trying to change wallpaper
3: Wallpaper change made by the the last instance of the app is noticeable

I tried setting below:
android:clearTaskOnLaunch="true"
android:launchMode="singleInstance"

Nothing works. Kindly help.

Below is my activity class & Manifext file

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="abhijit.android.sensor"
        android:versionCode="1"
        android:versionName="1.0" >

        <uses-sdk
            android:minSdkVersion="14"
            android:targetSdkVersion="19" />
         <uses-permission android:name="android.permission.VIBRATE" />
         <uses-permission android:name="android.permission.SET_WALLPAPER" />

        <application
            android:name="abhijit.android.sensor.GlobalVarible"
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="abhijit.android.sensor.SensorTestActivity" 
                android:label="@string/app_name" 
                android:clearTaskOnLaunch="true"
                android:launchMode="singleInstance" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>

    </manifest>

SensorTestActivity.java

    package abhijit.android.sensor;

    import java.io.IOException;
    import java.util.Date;
    import java.util.Random;

    import android.app.Activity;
    import android.app.AlertDialog;
    import android.app.WallpaperManager;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.graphics.Color;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    import android.os.Bundle;
    import android.os.Vibrator;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;

    public class SensorTestActivity extends Activity implements SensorEventListener {
      private SensorManager sensorManager;
      private boolean color = false;
      private View view;
      TextView text;
      private long lastUpdate;
      Random rnd = new Random(); 
      int colors; 
      private Vibrator vibrate;
      GlobalVarible globalVariable;
      WallpaperManager myWallpaperManager;
      Button b1,b2,b3;
      private static String AppVersion ="WALL-e v0.7 (Beta)";


    /** Called when the activity is first created. */

      @Override
      public void onCreate(Bundle savedInstanceState) {
    //    requestWindowFeature(Window.FEATURE_NO_TITLE);
    //    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sensor_test);
        view = findViewById(R.id.textView);
        text = (TextView) findViewById(R.id.textView);
        view.setBackgroundColor(Color.GREEN);
        b1 = (Button) findViewById(R.id.Enable_button);
        b2 = (Button) findViewById(R.id.Dis_button);
        b3 = (Button) findViewById(R.id.Exit_button);


        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        vibrate = (Vibrator) getSystemService(VIBRATOR_SERVICE);
        myWallpaperManager = WallpaperManager.getInstance(getApplicationContext());
        lastUpdate = new Date().getTime();

        // Calling Application class (see application tag in AndroidManifest.xml)
        globalVariable = (GlobalVarible) getApplicationContext();

        //Set name and email in global/application context
        globalVariable.setCounter(0);
        globalVariable.setWall(1);
        System.out.println("Counter set to :" + globalVariable.getCounter());    

      }

      @Override
      public void onSensorChanged(SensorEvent event) 
      {
        if  ((event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)  & globalVariable.appEnabled) {getAccelerometer(event);}
      }
      private void getAccelerometer(SensorEvent event) {


        float[] values = event.values;
        // Movement
        float x = values[0];
        float y = values[1];
        float z = values[2];


        float accelationSquareRoot = (x * x + y * y + z * z) / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);
    //    long actualTime = event.timestamp;

        long actualTime = (new Date()).getTime()  + (event.timestamp - System.nanoTime()) / 1000000L;

        if (accelationSquareRoot >= 3) //
        {
            long timediff = actualTime - lastUpdate;

          if (timediff < 1000) {
              return;
          }

    //      Toast.makeText(this, "Detected device movement & working !!! ", Toast.LENGTH_SHORT).show();
          globalVariable.setCounter(globalVariable.getCounter()+1);

          vibrate.vibrate(300);

          if (color) 
          {
              colors= Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
              view.setBackgroundColor(colors);
          } else {
             view.setBackgroundColor(colors);
          }
          color = !color;

          System.out.println("Counter # " + globalVariable.getCounter());

          if(globalVariable.getCounter()==1)
          {

              globalVariable.setCounter(0);               

              try 
              {
                  switch (globalVariable.getWall()) 
                  {
                            case 1:myWallpaperManager.setResource(R.drawable.wall1);break;              
                            case 2:myWallpaperManager.setResource(R.drawable.wall2);break;
                            case 3:myWallpaperManager.setResource(R.drawable.wall3);break;
                            case 4:myWallpaperManager.setResource(R.drawable.wall4);break;
                            case 5:myWallpaperManager.setResource(R.drawable.wall5);break;
                            case 6:myWallpaperManager.setResource(R.drawable.wall6);break;
                            case 7:myWallpaperManager.setResource(R.drawable.wall7);break;
                            case 8:myWallpaperManager.setResource(R.drawable.wall8);break;
                            case 9:myWallpaperManager.setResource(R.drawable.wall9);break;
                            case 10:myWallpaperManager.setResource(R.drawable.wall10);break;
                            case 11:myWallpaperManager.setResource(R.drawable.wall11);break;
                            case 12:myWallpaperManager.setResource(R.drawable.wall12);break;
                            case 13:myWallpaperManager.setResource(R.drawable.wall13);break;
                            case 14:myWallpaperManager.setResource(R.drawable.wall14);break;
                            case 15:myWallpaperManager.setResource(R.drawable.wall15);break;
                            case 16:myWallpaperManager.setResource(R.drawable.wall16);break;
                            case 17:myWallpaperManager.setResource(R.drawable.wall17);break;
                            case 18:myWallpaperManager.setResource(R.drawable.wall18);break;
                            case 19:myWallpaperManager.setResource(R.drawable.wall19);break;
                            case 20:myWallpaperManager.setResource(R.drawable.wall20);break;
                            case 21:myWallpaperManager.setResource(R.drawable.wall21);break;
                            case 22:myWallpaperManager.setResource(R.drawable.wall22);break;
                            case 23:myWallpaperManager.setResource(R.drawable.wall23);break;
                            case 24:myWallpaperManager.setResource(R.drawable.wall24);break;
                            case 25:myWallpaperManager.setResource(R.drawable.wall25);break;
                            case 26:myWallpaperManager.setResource(R.drawable.wall26);break;
                            case 27:myWallpaperManager.setResource(R.drawable.wall27);break;
                            default:break;
                  }           

                   Toast.makeText(this, "Successfully set as wallpaper to :"+globalVariable.getWall(), Toast.LENGTH_SHORT).show();
                   System.out.println("Counter : Wallerpaper Set to #"+globalVariable.getWall());

                   globalVariable.setWall(globalVariable.getWall()+1); 
                   if(globalVariable.getWall()>27) globalVariable.setWall(1);

                   lastUpdate = actualTime;

                  } catch (IOException e) 
                  {Toast.makeText(this, "Error set as wallpaper :", Toast.LENGTH_SHORT).show();}

          }      
        }
      }

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

      }

      @Override
      protected void onResume() {
        super.onResume();
        // register this class as a listener for the orientation and
        // accelerometer sensors
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
        Toast.makeText(this, "Sensor detection resumed !!!", Toast.LENGTH_SHORT).show();
      }

      @Override
      protected void onPause() {
        // unregister listener
        super.onPause();
    //    sensorManager.unregisterListener(this);
        Toast.makeText(this, "Sensor detection running in background !!!", Toast.LENGTH_SHORT).show();
    //    System.exit(0);
      }
      public void b1_Onclick(View v)
      {
          System.out.println("new Enabled!!!");
          globalVariable.appEnabled=true;
          sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
          text.setText("Application is [ ENABLED ]");

      }

      public void b2_Onclick(View v)
      {
          System.out.println("new Disbled!!!");
          globalVariable.appEnabled=false;
          sensorManager.unregisterListener(this);
          text.setText("Application is [ DISABLED ]");
      }

      public void b3_Onclick(View v)
      {
          System.out.println("new exit!!!");
          sensorManager.unregisterListener(this);
          this.onPause();
          this.finish();
      }

      public void b4_Onclick(View view)
      {

              AlertDialog.Builder dlgAlert  = new AlertDialog.Builder(this);

              dlgAlert.setMessage(AppVersion + " \n \nCopyright \u00a9 2014, \nAbhijit Mishra");
              dlgAlert.setTitle(AppVersion);
              dlgAlert.setPositiveButton("OK", null);
              dlgAlert.setCancelable(true);
              dlgAlert.create().show();

              dlgAlert.setPositiveButton("Ok", new DialogInterface.OnClickListener(){public void onClick(DialogInterface dialog, int which){}});      
      }


    } 

GlobalVariable.Java

    package abhijit.android.sensor;

    import android.app.Application;

    public class GlobalVarible extends Application{


       public int counter,wall;
       boolean appEnabled=true;


       public int getWall() {
        return wall;
    }

    public void setWall(int wall) {
        this.wall = wall;
    }

    public int getCounter() {
        return counter;
    }

       public void setCounter(int counter) {
           this.counter = counter;
    }




    }

Please let me know what should I do next so that Only 1 thread of the the application runs even if I minimize and bring it back up.

Regards, Abhijit

回答1:

The problem is here:

  @Override
  protected void onResume() {
    super.onResume();
    // register this class as a listener for the orientation and
    // accelerometer sensors
    sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
  }

  @Override
  protected void onPause() {
    // unregister listener
    // sensorManager.unregisterListener(this);
  }

You're not unregistering the listener, so it is registered multiple times.

I understand that what you want to do is to keep listening even if the activity is paused, and you could use a boolean flag to only register once. But it's probably not a good idea. Activites that are not visible can be finished by the system at any time.

For these kinds of use cases a Service would be far more appropriate (as a bonus, you could set it up to start on BOOT_COMPLETED, so that you don't need to re-run the app to set up this listener when you restart the device).

So, in short, I would recommend:

  • Create a Service. In onCreate(), register the service as a listener to SensorManager (very much as you do here).
  • When your activity runs, send an Intent to start the service (see docs).
  • Optionally, specify a listener for ACTION_BOOT_COMPLETED so that the service is restarted when the device restarts. See this answer.