为什么我的Android应用程序在目前一切正常的情况下运行两个实例?
我有一个包含三个活动的应用程序为什么我的Android应用程序在目前一切正常的情况下运行两个实例?,android,debugging,android-activity,Android,Debugging,Android Activity,我有一个包含三个活动的应用程序 从SharedReferences加载数据的splashscreenActivity(启动器活动)。然后在第二次之后继续MainActivity并退出(finish())本身 负责UI并对变量和AlarmManager进行必要更改的主要活动 仅由AlarmManager使用的广播接收器。BroadCastReceiver根据Splash的静态值发送通知 一切都很顺利,直到我将材料设计添加到依赖项中,并更新了我的所有布局和所有这些东西。我不确定这是否是导致这个错误的
package com.example.breathe;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
public class SplashScreen extends AppCompatActivity {
public static boolean breatheToggled = false, persistence = true;
public static int interval = 0;
public static boolean TESTING = false;
public static String BreathePreferencesKey="THE_KEY_TO_THE_VALLEY_OF_ETERNAL_PREFERENCES",
breatheToggleString = "Isthetoggleswitchonoroff",
breatheIntervalString = "IntervalForBreatheReminder",
breathePersistenceString = "StringForPersistenceSwitchKey";
public static void log(String x){ Log.d("jitin", x); }
public static void trace(String x){ Log.d("trace", x); }
@Override
protected void onDestroy() {
super.onDestroy();
trace("Splash Destroyed");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
// AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
//LOAD DATA FROM PREFERENCES
SharedPreferences preferences = getSharedPreferences(BreathePreferencesKey, 0);
breatheToggled = preferences.getBoolean(breatheToggleString, breatheToggled);
interval = preferences.getInt(breatheIntervalString, interval);
persistence = preferences.getBoolean(breathePersistenceString, persistence);
log("Read Preferences :"+
"\nMainSwitch : "+String.valueOf(breatheToggled) +
"\nInterval : "+String.valueOf(interval) +
"\nPersistance: "+String.valueOf(persistence) +
"\nAutoCancel : "+String.valueOf(!persistence)
);
trace("Splash created, going to Main in 1 sec");
//GO TO MAIN ACTIVITY AFTER DELAY
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
goToMain();
}
}, 1000);
}
// public void goToMain(View view){ goToMain();}
private void goToMain() {
startActivity((new Intent(SplashScreen.this, MainActivity.class))
//ALL LOADED DATA TO BE PASSED TO MAIN CLASS AS INTENT
.putExtra(breatheToggleString, breatheToggled)
.putExtra(breatheIntervalString, interval)
.putExtra(breathePersistenceString, persistence)
);
finish();
}
}
MainActivity.java>
package com.example.breathe;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.ToggleButton;
import static com.example.breathe.SplashScreen.TESTING;
import static com.example.breathe.SplashScreen.log;
import static com.example.breathe.SplashScreen.trace;
public class MainActivity extends AppCompatActivity {
public static boolean toggleState, persistence;
private int interval;
private SeekBar intervalBar;
private ToggleButton toggleButton;
private TextView intervalText;
private Switch persistenceSwitch;
//Intent to BroadCast liSTENER
PendingIntent calltoBroadcastListener;
//ALARM MANAGER
AlarmManager AM;
@Override
protected void onDestroy() {
super.onDestroy();
trace("Main destroyed");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
toggleButton = findViewById(R.id.toggleButton);
intervalText = findViewById(R.id.intervalValue);
intervalBar = findViewById(R.id.intervalBar);
persistenceSwitch = findViewById(R.id.persistenceSwitch);
trace("Main created");
//OBTAINING DATA FROM INTENT, WHICH COMES FROM SHAREDPREFERENCES LOADED IN THE SPLASH ACTIVITY
Intent intentData = getIntent();
toggleState = intentData.getBooleanExtra(SplashScreen.breatheToggleString, SplashScreen.breatheToggled);
toggleBreatheSwitch(toggleState);
interval = intentData.getIntExtra(SplashScreen.breatheIntervalString, SplashScreen.interval);
setInterval();
intervalBar.setOnSeekBarChangeListener(seekBarChangeListener);
persistence = intentData.getBooleanExtra(SplashScreen.breathePersistenceString, SplashScreen.persistence);
persistenceSwitch.setChecked(!persistence);
persistenceSwitch.setOnCheckedChangeListener(switchChangeListener);
calltoBroadcastListener = PendingIntent.getBroadcast(
this,
1,
new Intent(this, BreathBroadcastReceiver.class),
PendingIntent.FLAG_UPDATE_CURRENT
);
AM = (AlarmManager) getSystemService(ALARM_SERVICE);
}
Switch.OnCheckedChangeListener switchChangeListener = new Switch.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (compoundButton.getId() == R.id.persistenceSwitch) {
persistence = !b;
//set state to Shared Preferences
SharedPreferences.Editor preferences = getSharedPreferences(SplashScreen.BreathePreferencesKey, 0).edit();
preferences.putBoolean(SplashScreen.breathePersistenceString, persistence);
preferences.apply();
log("Saved preference persistence : " + persistence + ", AutoCancel : " + !persistence);
}
}
};
SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@SuppressLint("SetTextI18n")
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
switch (seekBar.getId()){
case R.id.intervalBar:
setInterval(i);
vibrate(30);
default:
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
toggleBreatheSwitch(false);
log("Saved Prefereces : interval : "+interval);
SharedPreferences.Editor preferences = getSharedPreferences(SplashScreen.BreathePreferencesKey, 0).edit();
preferences.putInt(SplashScreen.breatheIntervalString, (seekBar.getProgress()+1)*15);
preferences.apply();
}
};
@SuppressLint("ShortAlarm")
private void setAlarm(){
if (AM != null) {
if (!TESTING)AM.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
0,
1000*60*interval,
calltoBroadcastListener
);
else AM.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, 1000*60*1, calltoBroadcastListener);
log("Alarm Set");
} else log("Alarm is NULL");
}
private void toggleBreatheSwitch(boolean thetoggleState){
toggleState = thetoggleState;
//set toggle state to shared preferences;
toggleButton.setChecked(toggleState);
SharedPreferences.Editor preferences = getSharedPreferences(SplashScreen.BreathePreferencesKey, 0).edit();
preferences.putBoolean(SplashScreen.breatheToggleString, toggleState);
preferences.apply();
log("Saved preferences : MainSwitch : "+ toggleState);
//set changes to UI
if (toggleState){ //REMINDER ON
toggleButton.setTextColor(getResources().getColor(R.color.colorAccent));
setAlarm();
} else { //REMINDER OFF
toggleButton.setTextColor(getResources().getColor(R.color.colorWhite));
if (AM != null) AM.cancel(calltoBroadcastListener);
log("Alarms Revoked");
}
vibrate(50);
}
public void toggleBreatheSwitch(View view) {
//dummy function to handle calls from UI
toggleBreatheSwitch(((ToggleButton)view).isChecked());
}
private void vibrate(int mSecs){
try{ //To avoid vibrating when device is in total silence
if (((AudioManager)getSystemService(AUDIO_SERVICE)).getRingerMode() == AudioManager.RINGER_MODE_SILENT) return;
} catch (Exception ignore){}
Vibrator vibe = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if ((vibe != null) && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vibe.vibrate(VibrationEffect.createOneShot(mSecs, VibrationEffect.EFFECT_TICK));
else
vibe.vibrate(mSecs);
}
private void setInterval(){
//Function to update UI - set intervalText and intervalBar values
setInterval(interval/15 - 1);
intervalBar.setProgress(interval/15 - 1);
}
@SuppressLint("SetTextI18n")
private void setInterval(int seekbarValue){
//Function to update UI - set the intervalText
int hours, minutes;
interval = (seekbarValue+1)*15;
hours = interval/60;
minutes = interval%60;
if (hours != 0 ) intervalText.setText(hours + "Hr ");
if (minutes != 0)
if (hours != 0)
intervalText.setText(intervalText.getText().toString() + minutes +"min");
else intervalText.setText(minutes +"min");
}
}
BreathBroadcastReceiver.java>
package com.example.breathe;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import static com.example.breathe.SplashScreen.log;
import static com.example.breathe.SplashScreen.trace;
public class BreathBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
trace("Broadcast Receiver received, sending notification");
sendBreatheNotification(context);
}
public void sendBreatheNotification(Context context) {
String NotificationChannelID = "Channel001";
NotificationManager notifier = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notifier != null) {
NotificationChannel chan = new NotificationChannel (
NotificationChannelID,
"Primary Breathe Reminders",
NotificationManager.IMPORTANCE_DEFAULT);
chan.setSound(
Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.m),
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
);
notifier.createNotificationChannel(chan);
}
if (notifier != null){
NotificationCompat.Builder nBuilder = new NotificationCompat.Builder(context, NotificationChannelID)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_lungs)
.setContentTitle("Breathe")
.setContentText("Don't Forget to Breath Deep.")
.setContentInfo("SOME INFO");
// .setSubText("some sub text as well")
if (!MainActivity.persistence) nBuilder.setTimeoutAfter(1000*10);
notifier.notify(1, nBuilder.build());
log("Breath Notification sent");
} else log("Notifier null, Notification not displayed");
}
}
manifest>
MainActivitylayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="18dp"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatToggleButton
android:id="@+id/toggleButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:checked="true"
android:onClick="toggleBreatheSwitch"
android:padding="24dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/intervalBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:max="15"
android:progress="9"
app:layout_constraintBottom_toTopOf="@+id/toggleButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/intervalText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/reminder_interval"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toTopOf="@+id/intervalBar"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/intervalValue"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
app:layout_constraintBaseline_toBaselineOf="@+id/intervalText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/intervalText" />
<Switch
android:id="@+id/persistenceSwitch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:checked="true"
android:text="@string/persistence_text"
android:textOff="disabled"
android:textOn="enabled"
app:layout_constraintBottom_toTopOf="@+id/intervalText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/voiceText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Notification Voice"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toTopOf="@+id/radioGroup"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:orientation="vertical"
android:gravity="fill"
app:layout_constraintBottom_toTopOf="@+id/persistenceSwitch"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/radioButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Male" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/radioButton2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Female" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>
您可能会看到这个讨厌的、长期存在的Android bug: 要进行检查,请在设备上安装应用程序,然后关闭应用程序(设置->应用程序->应用程序->强制退出),然后点击主屏幕上的图标启动应用程序,查看其现在是否正常运行
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="18dp"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatToggleButton
android:id="@+id/toggleButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:checked="true"
android:onClick="toggleBreatheSwitch"
android:padding="24dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/intervalBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:max="15"
android:progress="9"
app:layout_constraintBottom_toTopOf="@+id/toggleButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/intervalText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/reminder_interval"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toTopOf="@+id/intervalBar"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/intervalValue"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
app:layout_constraintBaseline_toBaselineOf="@+id/intervalText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/intervalText" />
<Switch
android:id="@+id/persistenceSwitch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:checked="true"
android:text="@string/persistence_text"
android:textOff="disabled"
android:textOn="enabled"
app:layout_constraintBottom_toTopOf="@+id/intervalText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/voiceText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Notification Voice"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toTopOf="@+id/radioGroup"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:orientation="vertical"
android:gravity="fill"
app:layout_constraintBottom_toTopOf="@+id/persistenceSwitch"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/radioButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Male" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/radioButton2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Female" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>