Android:即使应用最小化,如何设置通知您的计时器?

时间:2017-07-25 16:29:13

标签: android time timer alarmmanager job-scheduling

我希望能够设置可重复使用的计时器(例如20秒),然后让它开始倒计时,但我也可以最小化应用程序,做其他事情,并让计时器仍然通知我。计时器也应该是启动/停止/暂停/重置。

我已经看过AlarmManager ,但我读到它似乎在几台设备上被破坏了。有更强大的解决方案吗?

编辑:尝试服务

The Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.packagename.timertest"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

        <service android:name=".TimerService"
                 android:exported="false"/>
    </application>

</manifest>

启动片段的MainActivity:

public class MainActivity extends AppCompatActivity {
    private Button launchTimerPanelButton;
    private FragmentManager fragmentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragmentManager = getSupportFragmentManager();

        launchTimerPanelButton = (Button) findViewById(R.id.launch_timer_button);
        launchTimerPanelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TimerDialogFragment dialogFragment = TimerDialogFragment.newInstance(10);
                dialogFragment.setStyle(DialogFragment.STYLE_NORMAL, R.style.CustomDialog);
                dialogFragment.show(fragmentManager,"");
            }
        });
    }
}

MainActivity的XML:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    tools:context="com.packagename.timertest.MainActivity">

    <Button
        android:id="@+id/launch_timer_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Launch Timer Panel"/>

</android.support.constraint.ConstraintLayout>

DialogFragment:

public class TimerDialogFragment extends DialogFragment {
    private static final String ARGUMENT_NUM_SECONDS = "state_num_seconds";
    private static final String STATE_NUM_SECONDS = "state_num_seconds";
    public static final String STATE_IS_BROADCAST_RECEIVER_REGISTERED = "state_is_broadcast_receiver_registered";
    private int numSecondsInitial;
    private int numSeconds;
    private TextView secondsRemainingTextView;
    private Button startButton;
    private Button pauseButton;
    private Button resetButton;
    private Button closeButton;
    private BroadcastReceiver restTimerReceiver;
    private boolean isBroadcastReceiverRegistered;

    public static TimerDialogFragment newInstance(int numSeconds) {
        Bundle args = new Bundle();
        TimerDialogFragment fragment = new TimerDialogFragment();
        args.putInt(ARGUMENT_NUM_SECONDS, numSeconds);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        numSecondsInitial = getArguments().getInt(ARGUMENT_NUM_SECONDS);
        if (savedInstanceState == null) {
            numSeconds = numSecondsInitial;
        }
        else {
            numSeconds = savedInstanceState.getInt(STATE_NUM_SECONDS);
        }

        isBroadcastReceiverRegistered = false;
        if (savedInstanceState != null) {
            isBroadcastReceiverRegistered = savedInstanceState.getBoolean(STATE_IS_BROADCAST_RECEIVER_REGISTERED);
        }

        restTimerReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                //???????
            }
        };
        registerBroadcastReceiver();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.dialogfragment_timer, container, false);
        getDialog().setTitle("Timer");

        secondsRemainingTextView = (TextView) contentView.findViewById(R.id.seconds_remaining_textview);
        secondsRemainingTextView.setText(numSeconds + "");

        startButton = (Button) contentView.findViewById(R.id.start_button);
        pauseButton = (Button) contentView.findViewById(R.id.pause_button);
        resetButton = (Button) contentView.findViewById(R.id.reset_button);
        closeButton = (Button) contentView.findViewById(R.id.close_button);


        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        pauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        resetButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        closeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //end the timer first?
                unregisterBroadcastReceiver();
                dismiss();
            }
        });

        return contentView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(STATE_NUM_SECONDS, numSeconds);
        super.onSaveInstanceState(outState);
    }

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

    @Override
    public void onPause() {
        unregisterBroadcastReceiver();
        super.onPause();
    }

    @Override
    public void onDestroy() {
        unregisterBroadcastReceiver();
        super.onDestroy();
    }


    private void registerBroadcastReceiver() {
        if (!isBroadcastReceiverRegistered) {
            LocalBroadcastManager.getInstance(getActivity()).registerReceiver(restTimerReceiver, new IntentFilter(TimerService.TIMER_SERVICE));
            isBroadcastReceiverRegistered = true;
        }
    }

    private void unregisterBroadcastReceiver() {
        if (isBroadcastReceiverRegistered) {
            isBroadcastReceiverRegistered = false;
            LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(restTimerReceiver);
        }
    }

}

其布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:padding="10dp"
    android:gravity="center">


    <TextView
        android:id="@+id/seconds_remaining_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:gravity="center"
        android:text="10"/>

    <Button
        android:id="@+id/start_button"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:gravity="center"
        android:text="Start Timer"/>


    <Button
        android:id="@+id/pause_button"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:gravity="center"
        android:text="Pause Timer"/>


    <Button
        android:id="@+id/reset_button"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:gravity="center"
        android:text="Reset Timer"/>

    <Button
        android:id="@+id/close_button"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:gravity="center"
        android:text="Close / End Timer"/>

</LinearLayout>

服务:

public class TimerService extends Service {
    private String LOG_TAG = TimerService.class.getSimpleName();
    public static final String TIMER_SERVICE = "timer_service";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(LOG_TAG, "OnCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOG_TAG, "In onStartCommand");
        new Thread(new Runnable() {
            public void run() {
                //something
            }
        }).start();
        return START_REDELIVER_INTENT;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(LOG_TAG, "OnBind");
        return null;
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.i(LOG_TAG, "In onTaskRemoved");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "In onDestroy");
    }
}

2 个答案:

答案 0 :(得分:0)

如果这是一次性过程,我认为Service是最佳选择。你可以在那里做任何你想做的事情,当过程结束时,发送Broadcast通知它已经完成。

答案 1 :(得分:0)

另一种选择是使用FirebaseJobDispatcher

以下是一个例子:

// Create a new dispatcher using the Google Play driver.
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");

Job myJob = dispatcher.newJobBuilder()
    // the JobService that will be called
    .setService(MyJobService.class)
    // uniquely identifies the job
    .setTag("my-unique-tag")
    // one-off job
    .setRecurring(false)
    // don't persist past a device reboot
    .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
    // start between 0 and 60 seconds from now
    .setTrigger(Trigger.executionWindow(0, 60))
    // don't overwrite an existing job with the same tag
    .setReplaceCurrent(false)
    // retry with exponential backoff
    .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
    // constraints that need to be satisfied for the job to run
    .setConstraints(
        // only run on an unmetered network
        Constraint.ON_UNMETERED_NETWORK,
        // only run when the device is charging
        Constraint.DEVICE_CHARGING
    )
    .setExtras(myExtrasBundle)
    .build();

dispatcher.mustSchedule(myJob);

取消该职位:

dispatcher.cancel("my-unique-tag");

如果要替换上一个计划的作业,该标记很有用。请记住,它有一个时间窗口,因此它不如AlarmManager准确。

这里有一张表格,可以比较您拥有的不同选项:

https://github.com/firebase/firebase-jobdispatcher-android#comparison-to-other-libraries

在该表格中,您会找到Evernote's library。它不依赖于Google Play,而是一种自定义实现,它根据您运行的用户操作系统使用不同的策略。