本周完成将闹钟代码实现模块化,该程序不需要UI,只需要在Mainactivity中给予一个实例化闹钟即可设定闹钟。
AlarmManager是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent。简单的说就是我们设定一个时间,然后在该时间到来时,AlarmManager为我们广播一个我们设定的Intent,通常我们使用 PendingIntent,PendingIntent可以理解为Intent的封装包,简单的说就是在Intent上在加个指定的动作。在使用Intent的时候,我们还需要在执行startActivity、startService或sendBroadcast才能使Intent有用。而PendingIntent的话就是将这个动作包含在内了。
入口类
Mainactivity.class
package com.example.alarmmodule;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
Button setTime1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitSetTime1();
}
private void InitSetTime1(){
setTime1 = (Button)findViewById(R.id.settime1);
setTime1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
MyAlarmManager alarm = new MyAlarmManager();
alarm.setNoRepeatAlarmUsingTimePicker(MainActivity.this,16,39,1);
}
});
}
}
MyAlarmManager.class
AlarmManager使用:
1 获得ALarmManager实例 ALarmManager am=(ALarmManager)getSystemService(ALARM_SERVICE);
2 定义一个PendingIntent发出广播
3 调用ALarmManager方法,设置定时或重复提醒
4 取消提醒
set(int type,long startTime,PendingIntent pi);
该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。
PendingIntent sender:是闹钟的执行动作,比如发送一个广播、给出提示等等。PendingIntent是Intent的封装类。需要注意的是,如果是通过启动服务来实现闹钟提示的话,PendingIntent对象的获取就应该采用Pending.getService( Context c,int i,Intent intent,int j)方法;如果是通过广播来实现闹钟提示的话PendingIntent对象的获取就应该采用PendingIntent.getBroadcast(Contex
c,int i,Intent intent,int j)方法;如果是采用Activity的方式来实现闹钟提示的话,PendingIntent对象的获取就应该采用PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。如果这三种方法错用了的话,虽然不会报错,但是看不到闹钟提示效果。本程序采用广播来实现闹钟提示。
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
通过上述方法,可在指定时间使pendingIntnet发送广播,然后程序实现BrodcastReceiver,接收此广播。这样就可在指定时间,作出动作。getBroadcast方法发出广播,这样在service中可以接收到所发出的广播来获取其中intent中携带的数据,然后再执行其他的逻辑。
取消闹钟
noRepeatAlarm.cancel(pi);
package com.example.alarmmodule;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import java.util.Calendar;
public class MyAlarmManager{
//需要知道用户想要执行闹钟响应的方式
public static final int TYPE_ALARM_RING = 1;//响铃
public static final int TYPE_ALARM_TOAST = 2;//弹框
public static final int TYPE_ALARM_VIBRATE = 3;//震动
/**
* 设置当日一次性闹钟
* @param hourOfDay,minute 用户设置时间
* @param responseType 反馈模式
*/
public void setNoRepeatAlarmUsingTimePicker(Context context,int hourOfDay,int minute,int responseType){
Calendar c = Calendar.getInstance();
// 根据用户选择的时间来设置Calendar对象
c.setTimeInMillis(System.currentTimeMillis());
c.set(Calendar.HOUR_OF_DAY,hourOfDay);
c.set(Calendar.MINUTE,minute);
c.set(Calendar.SECOND,0);
c.set(Calendar.MILLISECOND,0);
//设置AlarmManager在Calendar对应的时间启动Activity
AlarmManager noRepeatAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context,AlarmReceiver.class);
intent.putExtra("responseType", responseType);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
int type = AlarmManager.RTC_WAKEUP;
noRepeatAlarm.set(type,c.getTimeInMillis(),pi);
//获取当前毫秒值
long systemTime = System.currentTimeMillis();
long selectTime = c.getTimeInMillis();
// 如果当前时间大于设置的时间,那么就从第二天的设定时间开始
if(systemTime > selectTime) {
c.add(Calendar.DAY_OF_MONTH, 1);
}
}
/**
* 设置按周定制闹钟
* @param hourOfDay 小时
* @param minute 分钟
* @param weekDayArray 周几
* @param responseType 反馈类型
*/
public void setWeekAlarmUsingTimePicker(Context context,int hourOfDay,int minute,int weekDayArray[],int responseType){
AlarmManager weekAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent weekIntent = new Intent(context, AlarmReceiver.class);
weekIntent.putExtra("responseType", responseType);
PendingIntent weekPi=PendingIntent.getBroadcast(
context,0, weekIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar weekc = Calendar.getInstance();
weekc.setTimeInMillis(System.currentTimeMillis());
// 根据用户选择的时间来设置Calendar对象
weekc.set(Calendar.HOUR, hourOfDay);
weekc.set(Calendar.MINUTE, minute);
int nowDay = Contants.getNowWeek();
int setDay = 0;
setDay = Contants.getResultDifferDay1(weekDayArray, nowDay);
int differDay = Contants.compareDayNowToNext(nowDay, setDay);
if(differDay == 0){
weekAlarm.set(AlarmManager.RTC_WAKEUP,weekc.getTimeInMillis() + Contants.getDifferMillis(7),weekPi);
}else{
weekAlarm.set(AlarmManager.RTC_WAKEUP,weekc.getTimeInMillis() + Contants.getDifferMillis(differDay),weekPi);
}
}
/**
* 设置按日期定制闹钟
* @param Month 月份
* @param Day 日期
* @param hourOfDay 小时
* @param Minute 分钟
* @param responseType 反馈类型
*/
public void setDayAlarmUsingTimePicker(Context context,int Month, int Day, int hourOfDay, int Minute, int responseType){
Calendar calendar = Calendar.getInstance();//获取系统的日期
int year = calendar.get(Calendar.YEAR);//年
// 设置闹钟时间
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(year,Month,Day,hourOfDay,Minute);
Intent DayIntent = new Intent(context,AlarmReceiver.class);
DayIntent.putExtra("responseType", responseType);
AlarmManager dayAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent Daypi = PendingIntent.getBroadcast(context, 0, DayIntent, PendingIntent.FLAG_CANCEL_CURRENT);
dayAlarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), Daypi);
}
public String setToastContent(String content){
content = "闹钟响了";
return content;
}
}
主要是要继承 BroadcastReceiver 这个类,然后重写onRecive方法。onRecive方法在闹钟的时间达到之后会执行,在这里我们可以做自己的事情,比如启动某个程序,或者播放铃声,该类定义一个Receiver类通过广播来接收并处理闹钟响应事件。
注意:要将该广播接收器添加到清单文件当中。
<receiver android:name=".AlarmReceiver" android:process=":remote" />
在该程序中,参数responseType代表的是闹钟响应方式,要通过Intent从MyAlarmManager传递整型到AlarmReceiver,使之判断响应方式。需要用到的方法(通过数据类型转换)如下:
发送端:
int id = 0;
Intent intent = new Intent(this,EditActivity.class);
intent.putExtra("id",id+"")//id+""这样是把int型转化为string
startActivity(intent);
接收端:
String sID = gerStringExtra("id");
int id = Interger.parseInt(sID); //String转int
AlarmReceiver.class
package com.example.alarmmodule;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.view.Gravity;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver {//接受广播BroadcastReceiver的类:
@Override
public void onReceive(Context context, Intent intent) {
//当系统到我们设定的时间点的时候会发送广播,执行这里
if ("android.alarm.demo.action".equals(intent.getAction())) {
System.out.println("设置成功");
String getresponseType = intent.getStringExtra("responseType");
int responseType = Integer.parseInt(getresponseType);
System.out.println(responseType);
switch (responseType) {
case MyAlarmManager.TYPE_ALARM_RING:
System.out.println("闹钟响了");
Intent intent1 = new Intent(context, AlarmAlert.class);
context.startActivity(intent1);
MediaUtil.playRing(context);//开启
MediaUtil.stopRing();//关闭
Contants.cancelAlarmTimer(context,"android.alarm.demo.action");//取消闹钟
break;
case MyAlarmManager.TYPE_ALARM_TOAST:
Intent intent2 = new Intent(context, AlarmAlert.class);
context.startActivity(intent2);
Toast toast = Toast.makeText(context, "Toast提示消息", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Contants.cancelAlarmTimer(context,"android.alarm.demo.action");
break;
case MyAlarmManager.TYPE_ALARM_VIBRATE:
Intent intent3 = new Intent(context, AlarmAlert.class);
context.startActivity(intent3);
// 开启震动
boolean isVirating = true;
VibrateUtil.vibrate(context, 1000);//震动一次,1000ms
//关闭震动
if (isVirating) {//防止多次关闭抛出异常,这里加个参数判断一下
isVirating = false;
VibrateUtil.virateCancle(context);
Contants.cancelAlarmTimer(context,"android.alarm.demo.action");
break;
}
default:
System.out.println("error\n");
}
}
}
}
播放音乐模块
MediaUtil.class
package com.example.alarmmodule;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
public class MediaUtil {
private static MediaPlayer mMediaPlayer;
//开始播放
public static void playRing(Context context){
try {
//用于获取手机默认铃声的Uri
Uri alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(context, alert);
//告诉mediaPlayer播放的是铃声流
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
mMediaPlayer.setLooping(true);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
//停止播放
public static void stopRing(){
if (mMediaPlayer!=null){
if (mMediaPlayer.isPlaying()){
mMediaPlayer.stop();
mMediaPlayer.release();
}
}
}
}
震动模块
VibrateUtil.class
package com.example.alarmmodule;
import android.app.Service;
import android.content.Context;
import android.os.Vibrator;
public class VibrateUtil {
/**
* 让手机振动milliseconds毫秒
*/
public static void vibrate(Context context, long milliseconds) {
Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
if(vib.hasVibrator()){ //判断手机硬件是否有振动器
vib.vibrate(milliseconds);
}
}
/**
* 让手机以我们自己设定的pattern[]模式振动
* long pattern[] = {1000, 20000, 10000, 10000, 30000};
*/
public static void vibrate(Context context, long[] pattern,int repeat){
Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
if(vib.hasVibrator()){
vib.vibrate(pattern,repeat);
}
}
/**
* 取消震动
*/
public static void virateCancle(Context context){
//关闭震动
Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
vib.cancel();
}
}
工具类
Contants.class
package com.example.alarmmodule;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Contants {
//repeated day of week define
public static Map<String, Integer> mapWeek = new HashMap<String,Integer>();
public static void addMapWeek(){
mapWeek.put("周一", 1);
mapWeek.put("周二", 2);
mapWeek.put("周三", 3);
mapWeek.put("周四", 4);
mapWeek.put("周五", 5);
mapWeek.put("周六", 6);
mapWeek.put("周日", 7);
mapWeek.put("无重复", 0);
}
public static int getMapWeek(String str){
Contants.addMapWeek();
int dayOfMapWeek = 0;
if(str != null){
dayOfMapWeek = mapWeek.get(str);
}
return dayOfMapWeek;
}
public static String[] getDatetimeString(){
Date date = new Date();
String[] tempStr = new String[2];
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = format.format(date);
tempStr[0] = str.substring(0, 10);
tempStr[1] = str.substring(11, str.length());
return tempStr;
}
public static long getNowTimeMinuties()
{
return System.currentTimeMillis();
}
public static boolean differSetTimeAndNowTime(long setTime)
{
if(setTime >= getNowTimeMinuties()){
return true;
}else{
return false;
}
}
public static long getDifferMillis(int differDays)
{
return differDays * 24 * 60 * 60 * 1000;
}
//compare nowDay to nextDay
public static int compareDayNowToNext(int nowDay,int nextDay){
if(nextDay > nowDay){
return (nextDay-nowDay);
}else if(nextDay == nowDay){
return 0;
}else{
return (7-(nowDay-nextDay));
}
}
//turn the nowday to China`s day of Week
public static Map<String, Integer> nowWeek = new HashMap<String,Integer>();
public static void addNowWeek()
{
nowWeek.put("1", 7);
nowWeek.put("2", 1);
nowWeek.put("3", 2);
nowWeek.put("4", 3);
nowWeek.put("5", 4);
nowWeek.put("6", 5);
nowWeek.put("7", 6);
}
/**
* @return 返回星期几
*/
public static int getNowWeek()
{
Calendar nowCal = Calendar.getInstance();
Date nowDate = new Date(System.currentTimeMillis());
nowCal.setTime(nowDate);
int nowNum = nowCal.get(nowCal.DAY_OF_WEEK);
String nowNumStr = String.valueOf(nowNum);
Contants.addNowWeek();
int nowDayOfWeek = 0;
if(nowNumStr != null){
nowDayOfWeek = nowWeek.get(nowNumStr);
}
return nowDayOfWeek;
}
public static int getSetDay(String str)
{
if(str.equals("周一")){
return 1;
}
if(str.equals("周二")){
return 2;
}
if(str.equals("周三")){
return 3;
}
if(str.equals("周四")){
return 4;
}
if(str.equals("周五")){
return 5;
}
if(str.equals("周六")){
return 6;
}
if(str.equals("周日")){
return 7;
}
return 0;
}
public static int[] getDayOfNum(String[] str)
{
int[] dayOfInt = new int[str.length];
for(int i=0;i<str.length;i++){
dayOfInt[i] = getSetDay(str[i]);
}
return dayOfInt;
}
public static int getResultDifferDay(int[] in,int nowDay)
{
int result = 0;
for(int i=0;i<in.length;i++){
if(in[i] >= nowDay){
result = in[i];
break;
}
}
if(result == 0){
result = in[0];
}
return result;
}
public static int getResultDifferDay1(int[] in,int nowDay)
{
int result = 0;
for(int i=0;i<in.length;i++){
if(in[i] > nowDay){
result = in[i];
break;
}
}
if(result == 0){
result = in[0];
}
return result;
}
/**
* 取消闹钟
* @param context
* @param action
*/
public static void cancelAlarmTimer(Context context, String action) {
Intent myIntent = new Intent();
myIntent.setAction(action);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, myIntent,0);
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(sender);
}
}
AndroidManifest.xml
核心的配置为<receiver android:name=".AlarmReceiver" android:process=":remote" />
,这也是闹钟程序的关键,如果不做这个配置,那么时间到了之后,闹钟将不会提示。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alarmmodule">
<uses-permission android:name="android.permission.VIBRATE" />
<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">
<!--闹钟接收广播-->
<receiver android:name=".AlarmReceiver" android:process=":remote" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AlarmAlert"
android:label="@string/app_name">
</activity>
</application>
</manifest>
如何解决AndroidStudio logcat显示时间与当前系统时间不一致
今天在用模拟器调试程序的时候,突然发现AndroidStudio的logcat显示的调试信息时间跟当前系统的时间不一致,这对于我们查看调试信息无疑带来了很大的不便,所以就在此将解决方法稍微记录下。
出现这个问题的原因是模拟器所默认的时区是美国太平洋时区,导致模拟器的时间与我们系统的时间不一致,而logcat显示的是模拟器的当前时间。解决方法很简单,只要将模拟器的时区改变下就OK了。步骤如下:
1.打开模拟器,打开Settings,打开Date&time
2.选择Select time zone
3.选择Shanghai