web-dev-qa-db-ja.com

通知を再開し、アクティビティを再作成しない方法は?

私はこれを理解したと思っていましたが、この質問についてのいくつかのデバッグの後: 通知をキャンセル不可/削除不可にする方法 ランダムな順序でアクティビティがまだonCreated()とonDestroyed()を取得していることに気づきました。

活動の私のマニフェスト:

<activity
        Android:name="***.***.***.*****"
        Android:configChanges="orientation|keyboardHidden"
        Android:label="@string/app_name"
        Android:screenOrientation="portrait"
        Android:launchMode="singleTop" >
        <intent-filter>
            <action Android:name="Android.intent.action.MAIN" />

            <category Android:name="Android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

また、launchmodes singleTask、singleInstanceを試しました。

通知のインテントコード:

Intent intent = new Intent(context, MyClass.class);
    intent.setAction(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    //intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
    //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    //intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
    //intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

ご覧のとおり、関連性があると思われるすべてのフラグを試しましたが、運が悪かったです...

これにより、AlarmManagerを再起動したり、通知がクリックされるたびにalarmmanager start-taskを起動したりするなど、不要なアーティファクトが発生します。これは避けたいです。

助言がありますか?

編集:私はこのような質問がたくさんあることを知っていますが、提供されている解決策のどれもここではうまくいかないようです...:/

Edit2:リクエストにより、ここに私のクラスがあります:

package ***.***.***;

import Android.net.ConnectivityManager;
import Android.net.NetworkInfo;
import Android.os.Build;
import Android.os.Bundle;
import Android.annotation.TargetApi;
import Android.app.Activity;
import Android.app.AlarmManager;
import Android.app.NotificationManager;
import Android.app.PendingIntent;
import Android.content.Context;
import Android.content.Intent;
import Android.content.SharedPreferences;
import Android.graphics.PorterDuff;
import Android.support.v4.app.FragmentActivity;
import Android.support.v4.app.NotificationCompat;
import Android.support.v4.app.TaskStackBuilder;
import Android.util.Log;
import Android.view.Menu;
import Android.view.View;
import Android.widget.Button;
import Android.widget.TextView;
import Android.widget.Toast;


public class MyClass extends FragmentActivity {


private static String userName;
String password;
private static Boolean LoggedIn = false;
private static Boolean RunningState = false;
private static Boolean OnlineState = false;
private static String LastReportTime;

private static Boolean isVisible = true;
private static Boolean firstStart = true;

private static TextView onlineTextView;
private static TextView reportTimeTextView;
private static TextView runningStatusTextView;
private static TextView userLoggedInTextView;

private static Context context;

public static final String PREFS_NAME = "Settings";

public static final String NOTIFICATION_RUNNING_OK = "Reporting Active";
public static final String NOTIFICATION_USER_STOPPED = "Reporting Stopped";
public static final String NOTIFICATION_NO_NETWORK = "No Network Connected";
public static final String NOTIFICATION_NO_CONNECTION = "No Connection To Server";

public static final int NOTIFICATION_ID = 10;

public static final int LOGIN_REQUEST_CODE = 1;
public static final int WAKEUP_LOGIN_REQUEST_CODE = 2;

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);

    Log.d("MyClass", "Main onCreate() Called");

    loadVariables();

    com.remobjects.sdk.TypeManager.setPackage("com.remobjects.sdk");
    //if (firstStart)
    //{
        Log.d("MyClass", "Main onCreate() firstStart Called");
        if (RunningState && checkConnection())
        {
           // After runLogin(), onResume() gets called here again immediately
            setLoginCode(LOGIN_REQUEST_CODE);
            runLogin();
        }
        else
            init();
    //}
}

@Override
protected void onStart() {
    // TODO Auto-generated method stub
    super.onStart();
}

@Override
public void onResume()
{
    super.onResume();

    Log.d("MyClass", "Main onResume() Called");
    //firstStart gets set to false during login
    if (!firstStart)
    {
        Log.d("MyClass", "Main onResume() !firstStart Called");
        loadVariables();
        setVisible(true);
        updateUI();
    }
}

@Override
protected void onPause()
{
    super.onPause();
    saveVariables();
    setVisible(false);
}

@Override
protected void onStop()
{
    super.onStop();

    saveVariables();
    setVisible(false);
}

@Override
public void onDestroy()
{
    super.onDestroy();
    //cancelNotification();
    Log.e("MyClass", "onDestroy() called");
    saveVariables();
    setVisible(false);
    //setFirstStart(true);
}


private void loadVariables()
{
    SharedPreferences sharedPrefs = getSharedPreferences(PREFS_NAME, 0);

    userName = sharedPrefs.getString("userName", "");
    RunningState = sharedPrefs.getBoolean("RunningState", true);
    LoggedIn = sharedPrefs.getBoolean("LoggedIn", false);
    OnlineState = sharedPrefs.getBoolean("OnlineState", false);
    LastReportTime = sharedPrefs.getString("LastReportTime", "");

    context = this.getApplicationContext();
}

private static void saveVariables()
{
    SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
    SharedPreferences.Editor editor = settings.edit();

    editor.putString("userName", userName);
    editor.putBoolean("RunningState", RunningState);
    editor.putBoolean("LoggedIn", LoggedIn);
    editor.putBoolean("OnlineState", OnlineState);
    editor.putString("LastReportTime", LastReportTime);

    editor.commit();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.activity_my_class, menu);
    return true;
}

private Boolean checkConnection()
{
    Log.d("MyClass", "checkConnection()");
    ConnectivityManager cnnxManager = (ConnectivityManager) 
            getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo ni = cnnxManager.getActiveNetworkInfo();

    if (ni != null && ni.isAvailable() && ni.isConnected()) 
    {
        OnlineState = true;
        return true;
    }
    OnlineState = false;
    return false;
}

public void runLogin()
{
    Intent intent = new Intent(context, LoginActivity.class);
    startActivityForResult(intent, getLoginCode());
    Log.d("MyClass", "runLogin()");
}

private void init()
{
    Log.d("MyClass", "init()");
    setContentView(R.layout.activity_field_agent);

    onlineTextView = (TextView)findViewById(R.id.onlineStatusTextView);
    reportTimeTextView = (TextView)findViewById(R.id.lastReportTimeTextView);
    runningStatusTextView = (TextView)findViewById(R.id.runningStatusTextView);
    userLoggedInTextView = (TextView)findViewById(R.id.userLoggedInTextView);

    findViewById(R.id.button_online).getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
    findViewById(R.id.button_highRisk).getBackground().setColorFilter(0xFFFFA500, PorterDuff.Mode.MULTIPLY);
    findViewById(R.id.button_alarm).getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);

    setVisible(true);

    updateUI();

    if (RunningState)
    {
        setupAlarmManager(AlarmManager.INTERVAL_FIFTEEN_MINUTES);
        // Here onResume() gets called again
        updateNotificationText(NOTIFICATION_RUNNING_OK);

        Button temp = (Button)findViewById(R.id.button_online);
        temp.setCompoundDrawablesWithIntrinsicBounds(R.drawable.check_box, 0, R.drawable.check_box, 0);
    }
    else
    {
        //cancelAlarmManager();

        updateNotificationText(NOTIFICATION_USER_STOPPED);

        Button temp = (Button)findViewById(R.id.button_offline);
        temp.setCompoundDrawablesWithIntrinsicBounds(R.drawable.check_box, 0, R.drawable.check_box, 0);
    }
}

private void updateUI() 
{
    Log.d("MyClass", "updateUI()");

    updateUserLoggedInStatus(userName);

    updateOnlineStatus(OnlineState);

    updateRunningStatus(RunningState);

    updateReportTimeStatus(LastReportTime);
}

public void offDutyButton_click(View view)
{
    cancelAlarmManager();
    Button temp = (Button)findViewById(R.id.button_offline);
    temp.setCompoundDrawablesWithIntrinsicBounds(R.drawable.check_box, 0, R.drawable.check_box, 0);

    temp = (Button)findViewById(R.id.button_online);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_highRisk);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_alarm);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}

public void onDutyButton_click(View view)
{
    Button temp = (Button)findViewById(R.id.button_online);
    temp.setCompoundDrawablesWithIntrinsicBounds(R.drawable.check_box, 0, R.drawable.check_box, 0);

    temp = (Button)findViewById(R.id.button_offline);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_highRisk);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_alarm);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
    //cancelAlarmManager();
    setupAlarmManager(AlarmManager.INTERVAL_FIFTEEN_MINUTES);
}

public void highRiskButton_click(View view)
{
    Button temp = (Button)findViewById(R.id.button_highRisk);
    temp.setCompoundDrawablesWithIntrinsicBounds(R.drawable.check_box, 0, R.drawable.check_box, 0);

    temp = (Button)findViewById(R.id.button_online);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_offline);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_alarm);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}

public void alarmButton_click(View view)
{
    Button temp = (Button)findViewById(R.id.button_alarm);
    temp.setCompoundDrawablesWithIntrinsicBounds(R.drawable.check_box, 0, R.drawable.check_box, 0);

    temp = (Button)findViewById(R.id.button_online);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_highRisk);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);

    temp = (Button)findViewById(R.id.button_offline);
    temp.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}

public static void setButtonIcon(int inId)
{

}

public static void showToast(String inString, Context context)
{
    Toast.makeText(context, inString.toString(), Toast.LENGTH_SHORT).show();
}

public static void updateOnlineStatus(Boolean inStatus)
{
    if (isVisible)
    {
        if (inStatus)
            onlineTextView.setText("Online");
        else
            onlineTextView.setText("Offline");
    }
}

public static void updateReportTimeStatus(String inString)
{
    if (isVisible)
        reportTimeTextView.setText(inString);
}

public static void updateRunningStatus(Boolean inStatus)
{
    if (isVisible)
    {
        if (inStatus)
            runningStatusTextView.setText("Reporting");
        else
            runningStatusTextView.setText("Not Reporting");
    }
}

public static void updateUserLoggedInStatus(String inString)
{
    if (isVisible)
        userLoggedInTextView.setText(inString);
}


//
//
// Getters and Setters
//
//
public static void setLoggedIn(Boolean inBool)
{
    LoggedIn = inBool;
}

public static Boolean getLoggedIn()
{
    return LoggedIn;
}

public static void setRunningState(Boolean inBool)
{
    RunningState = inBool;
}

public static Boolean getRunningState()
{
    return RunningState;
}

public static void setVisible(Boolean inBool)
{
    isVisible = inBool;
}

public static Boolean getVisible()
{
    return isVisible;
}

public static void setUsername(String inString)
{
    userName = inString;
}

public static String getUsername()
{
    return userName;
}

public static void setLastReportTime(String inString)
{
    LastReportTime = inString;
}

public static String getLastReportTime()
{
    return LastReportTime;
}

public static Context getAppContext()
{
    return MyClass.context;
}

public static void setLoginCode(int code)
{
    SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
    SharedPreferences.Editor editor = settings.edit();

    editor.putInt("LoginCode", code);

    editor.commit();
}

public static int getLoginCode()
{
    SharedPreferences sharedPrefs = context.getSharedPreferences(PREFS_NAME, 0);

    return sharedPrefs.getInt("LoginCode", 1);
}

public static void setFirstStart(Boolean inBool)
{
    firstStart = inBool;
}

public static Boolean getFirstStart()
{
    return firstStart;
}

//
//
//
//
//
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  switch(requestCode) {
    case (LOGIN_REQUEST_CODE) : {
      if (resultCode == Activity.RESULT_OK) {
        LoggedIn = data.getBooleanExtra("LoggedIn", false);
        userName = data.getStringExtra("Username");

        init();
      }
      break;
    }
    case (WAKEUP_LOGIN_REQUEST_CODE) : {
        if (resultCode == Activity.RESULT_OK) {
          LoggedIn = data.getBooleanExtra("LoggedIn", false);
          userName = data.getStringExtra("Username");

          cancelAlarmManager();
          setupAlarmManager(AlarmManager.INTERVAL_FIFTEEN_MINUTES);
        }
        break;
    }
  }
}

//
//
// AlarmManager
//
//

public static void setupAlarmManager(long interval)
{
    AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    Intent alarmIntent = new Intent(context, LaunchReceiver.class); 
    PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, alarmIntent, 0);
    alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, interval,  pendingAlarmIntent);

    RunningState = true;
    updateRunningStatus(RunningState);

    updateNotificationText(NOTIFICATION_RUNNING_OK);

    Log.d("MyClass", "AlarmManager Started");
}


public static void cancelAlarmManager()
{
    Intent intent = new Intent(context.getApplicationContext(), LaunchReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    alarmMgr.cancel(pendingIntent);

    RunningState = false;
    updateRunningStatus(RunningState);

    updateNotificationText(NOTIFICATION_USER_STOPPED);

    Log.d("MyClass", "AlarmManager Stopped");

    Intent serviceIntent = new Intent(context, MonitorService.class);
    context.stopService(serviceIntent);
    Log.d("MyClass", "Stopping MonitorService");
}



//
//
// Notification
//
//

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static void createNotification()
{
    NotificationManager notificationManager = (NotificationManager)context.getSystemService(NOTIFICATION_SERVICE);

    NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                            .setContentTitle("blablabla")
                            .setContentText("Getting Status")
                            .setSmallIcon(R.drawable.ic_launcher)
                            .setOngoing(true)
                            .setAutoCancel(false);

    Intent intent = new Intent(context, MyClass.class);
    intent.setAction(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    //intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
    //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    //intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
    //intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    stackBuilder.addParentStack(MyClass.class);
    stackBuilder.addNextIntent(intent);

    PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,  PendingIntent.FLAG_UPDATE_CURRENT);

    builder.setContentIntent(pendingIntent);

    /*Notification noti = builder.build();
    noti.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;*/

    notificationManager.notify(NOTIFICATION_ID, builder.build());
}

public static void updateNotificationText(String inString)
{
    NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

    NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                                            .setContentText(inString)
                                            .setContentTitle("blablabla")
                                            .setSmallIcon(R.drawable.ic_launcher)
                                            .setOngoing(true)
                                            .setAutoCancel(false);

    Intent intent = new Intent(context, MyClass.class);
    intent.setAction(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    stackBuilder.addParentStack(MyClass.class);
    stackBuilder.addNextIntent(intent);

    PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,  PendingIntent.FLAG_UPDATE_CURRENT);

    builder.setContentIntent(pendingIntent);

    /*Notification noti = builder.build();
    noti.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;*/

    notificationManager.notify(NOTIFICATION_ID, builder.build());
}

public static void cancelNotification()
{
    NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

    notificationManager.cancel(NOTIFICATION_ID);
}
}

コメントで述べたように、loginActivityを開始した後、ここでonResume()がすぐに再び呼び出されます。 alarmManagerを起動した後も同じです。

また、alarmManagerがティックするたびに、アプリが前面に表示されるようです。それを回避する方法はありますか?

23
CeeRo

問題は通知コードのこれらの行によって引き起こされたようです(通知に関するAndroidのガイドから直接取られました:

TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    stackBuilder.addParentStack(FieldAgent.class);
    stackBuilder.addNextIntent(intent);

    PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,  PendingIntent.FLAG_UPDATE_CURRENT);

これらを通常の保留中の意図で置き換えることで解決しました:

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

完全な例

Intent intent = new Intent(getApplicationContext(), myactivity.class);
PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), intent, 0);

Notification myNotification  = new Notification.Builder(getApplicationContext())
                        .setContentTitle("Title")
                        .setContentText("Some text....")
                        .setSmallIcon(R.drawable.myicon)
                        .setContentIntent(pIntent)
                        .setAutoCancel(true).build();


NotificationManager notificationManager =
                        (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, myNotification);
35
CeeRo

私の場合、CeeRoによる回答は機能しましたが、奇妙な方法で行われました。電話を再起動する必要がありました! :)

7
zsolt világos

私のケースがあなたのようなものかどうかはわかりません。私はあなたのように意図を作成しようとしました。 pageviewer +フラグメントを使用しています。私の場合、通知メッセージをクリックしてアプリに移動してからメインに戻り、アプリをもう一度起動して、アクティビティを再作成します(このコードはクラスフラグメントにあります)

Intent intent = new Intent(getActivity(),MainActivity.class);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent =  PendingIntent.getActivity(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

これで問題ありません。次のコードに変更します。

Intent intent = getActivity().getIntent();
PendingIntent pendingIntent =  PendingIntent.getActivity(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

これが^ _ ^助けてくれることを願っています

5
Hung

複数回実行されている多くのコードがあります。

削除する:

saveVariables();
setVisible(false);

OnStop()およびonDestroy()から。このコードは、常に最初にonPause()ですでに実行されています。

loadVariables(); 2回実行することもできます。 onCreate()およびonResume()で1回。

ロードロジックを多少変更するのが賢明かもしれません。

Init()コードが2回実行されている可能性があります。 onCreate()およびonActivityResult()で1回。

TL; DR実際の問題は:

onActivityResult()は、奇妙な場合があります。つまり、必要なときに必ずしも起動するとは限りません。 onActivityResult()のinit()の前に変数をロードしてみてください。

これを試すには、onCreate()、onStart()、onResume()、onActivityResult()にログを配置し、それぞれがいつ開始されるかを確認します。

0
Knossos

アクティビティのonBackPressedをオーバーライドしましたか? onBackPressedをオーバーライドしない場合、Androidのデフォルトではアクティビティが破棄(finish())されます。したがって、おそらく前に戻った場合、通知はアクティビティが既に存在するかどうかを確認し、アクティビティが見つからない場合は新しいアクティビティを作成します。

0
unbekant