第五天
今日目標:
1.背景定時提醒處理 !!注意這裡的SharePreference會移到第六天講
1.背景定時提醒功能
在onCreate方法下添加 這個是要判斷是否定時提醒通知的Button有沒有按
先預設status是false,當他按第一次的時候因為flase預設是false就會跳”是”接下來把status改成true,因為status為true當他按第二次的時候就會跳否再把status改成false如此循環,很多UI要按按鈕改字也可以使用這樣的技巧
private boolean status=false;
activityAddBinding.test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (status){
activityAddBinding.test.setText("否");
status=false;
}else {
activityAddBinding.test.setText("是");
status=true;
}
}
});
接下來來講如何啟動定時提醒,我們先寫一個function,命名為startAlarm
接下來設定一個Alarmmanger
這時候先來介紹Alarmmanger主要是處理時間跟接受鬧鐘的,他的架構是
跟系統註冊鬧鐘在你設定的時間→ 時間到系統會發送一個鬧鐘→ 在AlarmReciever接收鬧鐘加邏輯處理→回傳回activity接收那個鬧鈴後執行
/小提醒:如果你設定的時間比現在時間早,鬧鐘會立刻響/
1.Alarmmanger的對象獲取
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
因為鬧鐘是系統服務所以我們要getSystemService去拿他的context
想知道底層原理可以看這篇https://blog.csdn.net/xyh269/article/details/52789737
2.發送intent給Reciever接收
因為可能同時有很多鬧鐘所以要給他們一個特殊的id,讓他在pendingIntent註冊廣播
2.1創一個新的class當作reciever幫助他接收
Intent intent = new Intent(this, AlertReceiver.class);
2.2 BROADCAST
BroadcastReceiver,顧名思義就是“廣播接收者”的意思,它是Android四大基本元件之一,這種元件本質上是一種全域的監聽器,用於監聽系統全域的廣播消息。它可以接收來自系統和應用的的廣播。
Broadcast 是廣播接收器 可以被動接收其他應用程式/狀態等等
主要需要 IntentFilter 過濾/增加intent 跟一個Reciever接收
分為靜態註冊跟動態註冊。我們這裡使用的是靜態註冊,因為靜態註冊才可以程序沒打開時也可以使用,動態註冊必須程式打開才可以使用,好處是可以自由控制跟取消。
動態註冊examlple
另一種是在activity註冊 activity死去的時候記得也要unregister
MyBroadcastReceiver receiver=new MyBroadcastReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction("android.intent.action.MyBroadcastReceiver");
registerReceiver(receiver, filter);
裡面還有一些其他的東西 ex有序廣播阿(就是廣播可以照順序) 無序(不用)等等,這裡先不說明
步驟
- 發intent > 2.創reciever收
我們使用的是靜態註冊記得reciver要在manifest裡面註冊 <<這個是靜態註冊就是程序關了有事件觸發還是有 背景處理就是靠這個 = =
打開manifest.xml
確認是否有
<receiver android:name=".AlertReceiver" >
</receiver>
PendingIntent,他是一個可以讓你推延發送的intent,他是一種特殊的可以延遲收到的intent(原理是他有包裝好的intent跟你的conext)即使你傳的activity死去了也可以用(因為context存好了) 就是生命週期是自己的,廣播/鬧鐘/通知都是用這個
獲取一個PendingIntent物件,而且該物件日後激發時所做的事情是啟動一個新activity
public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
第一個參數代表前後文 第二個是你的請求碼這裡我們設為你的alarmId因為他也是獨一無二的,第三個是你要傳的intent,第四個是flag
先在最外面宣告 int alarmId;
alarmId = SharedPreUtils.getInt(this, "alarm_id", 0);
SharedPreUtils.setInt(this, "alarm_id", ++alarmId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmId, intent, 0);
!!這裡我們使用了sharePreference
存,但是因為我今天主要是想說BroadCast跟Notification所以sharePreference內容將會移到第六天介紹。
所以請先複製貼上吧!
public class SharedPreUtils {
private static final String PREFERENCES = "live";
private static SharedPreferences preferencesSharedPreferences;
private static SharedPreferences getPreferences(Context context) {
if (preferencesSharedPreferences == null) {
preferencesSharedPreferences = context.getSharedPreferences(PREFERENCES, 0);
}
return preferencesSharedPreferences;
}
public static int getInt(Context context, String key, int defaultVal) {
return getPreferences(context).getInt(key, defaultVal);
}
public static void setInt(Context context, String key, int value) {
preferencesSharedPreferences.edit().putInt(key, value).commit();
}
}
}
2.3 設置時間
如果設置時間比現在時間早 (設置的是過去時間),將這個鬧鐘取消,否則會變成當下就會響
c.before()是比現在還早,Calendar.getInstance是獲取現在時間
if (c.before(Calendar.getInstance())) {
Toast.makeText(this, "你設置的時間是過去時間,不可能實現的QQ", Toast.LENGTH_SHORT).show();
cancelAlarm();
}
2.4 啟動鬧鐘
alarmManager.setExact(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent);
Toast.makeText(this, "您已設置定時提醒", Toast.LENGTH_SHORT).show();
接下來介紹參數
AlarmManager.RTC
: 表示鬧鐘在睡眠狀態下不可用,該狀態下鬧鐘使用絕對時間,即當前系統時間。
AlarmManager.RTC_WAKEUP
: 鬧鐘在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鐘使用絕對時間。
AlarmManager.ELAPSED_REALTIME
:真實時間,不喚醒休眠設備,休眠不可用。
AlarmManager.ELAPSED_REALTIME_WAKEUP
:真實時間,可喚醒手機休眠。
RTC鬧鐘和ELAPSED_REALTIME
最大的差別就是前者可以通過修改手機時間觸發鬧鐘事件, 後者要通過真實時間的流逝,即使在休眠狀態,時間也會被計算。
long triggerAtMillis
: 鬧鐘第一次的執行時間,單位是毫秒。
如果是RTC類型(他是現在多少時間就是多少也是絕對時間),triggerAtMillis 則一般使用System.currentTimeMillis();
如果是ELAPSED類型(他是相對於系統啟動開始算的時間),triggerAtMillis 則一般使用SystemClock.elapsedRealtime();
/這裡要注意 如果你手機api是android 4.4以上 因為google為了追求系統省電所以你如果只用alarmManger.set()會略有不準,請使用setExact()/
alarmManager.setExact(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent);
Toast.makeText(this, "您已設置定時提醒", Toast.LENGTH_SHORT).show();
這裡的參考資料: https://chendongqi.me/2017/02/14/AlarmManager-guide/
2.5取消鬧鐘
接下來我們要創一個新的function為cancelAlarm(),用alarmManger.cancel就可以取消了,然後記得intent也要有alarmId
private void cancelAlarm() {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlertReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmId, intent, 0);
alarmManager.cancel(pendingIntent);
Toast.makeText(this, "您尚未設置定時提醒", Toast.LENGTH_SHORT).show();
}
2.6 按下submit按鈕時判斷
還記得我們前面設的status嗎? Status是flase時是開啟鬧鐘,true為關上鬧鐘
在onSumbit()方法下添加,先判斷status是否為true,再確認是不是要發送通知 startAlarm方法,year_x month_x為之前在onDataSet按下時就設定好的值哦
沒印象的話可以去複習第四天
if (!status){
if (!TextUtils.isEmpty(activityAddBinding.timePicker.getText())) {
store=Calendar.getInstance();
store.set(year_x,month_x,day,hour_x,minute_x,second_x);
startAlarm(store);
}
}else {
cancelAlarm();
2.7 AlarmReciever.class
接收傳來的pendingIntent,還記得我們前面發出的是broadcast嗎?所以我們在接收的時候要extends BroadCastReceiver
public class AlertReceiver extends BroadcastReceiver {
…
}
2.8 傳送通知
這個我們要在onRecieve做,代表當你在特定時間收到這個pendingIntent時,我們就要開啟通知!! 下面有通知的詳細介紹
notification
雷點 :
- notification 如果Android 8.0(API級別26)以上記得要設channel
= = (可以視為id的一種) 不然會出大事
步驟 : 在depencies加入
dependencies { implementation "com.android.support:support-compat:28.0.0" }
然後如果要傳東西要用pendingintent 他是一種特殊的可以延遲收到的intent(原理是他有包裝好的intent跟你的conext)即使你傳的activity死去了也可以用(因為context存好了) 就是生命週期是自己的
廣播 鬧鐘 通知都是用這個
1.設channel>用notificantion manager建(這個是要傳達給用戶的)在這裡可以設燈會不會閃 震動 顏色 跟優先度等等
builder建實例(設通知內容跟channel) 注意icon一定要設不然會出大事
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.notification_icon) .setContentTitle(textTitle) .setContentText(textContent) . setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_DEFAULT); setStyle
- notificantion manager建用notify提醒
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); notificationManager.notify(notificationId, builder.build());
然後這是我們正式的,你也可以用上面的建法!! 我用NotificationHelper只是為了好整理而已!
@Override
public void onReceive(Context context, Intent intent) {
NotificationHelper notificationHelper = new NotificationHelper(context);
NotificationCompat.Builder nb = notificationHelper.getChannelNotification();
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
new Intent(context, HomeActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
nb.setContentIntent(contentIntent);
notificationHelper.getManager().notify(1, nb.build());
NotificationHelper.class
public class NotificationHelper extends ContextWrapper {
public static final String channelID = "channelID";
public static final String channelName = "Channel Name";
private NotificationCompat.Builder mBuilder;
private NotificationManager mManager;
public NotificationHelper(Context base) {
super(base);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
}
@TargetApi(Build.VERSION_CODES.O)
private void createChannel() {
NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
getManager().createNotificationChannel(channel);
}
public NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
}
public NotificationCompat.Builder getChannelNotification() {
return mBuilder = new NotificationCompat.Builder(getApplicationContext(), channelID)
.setContentTitle("Alarm!")
.setContentText("Your AlarmManager is working.")
.setSmallIcon(R.drawable.ic_launcher_background)
.setOngoing(true);
}
}
2.service在onrecive那邊不要超過十秒 不然有可能會anrS
Service
0.1 記得去看manifest.xml有沒有註冊 = =
- startservice / stopservice
- 他只是背景執行但是線程還是一樣的 所以如果你寫太複雜的東西一樣會ANR
如果要寫複雜的東西要在多開一個線程比較好
啊在service開線程比activity的好處是service可以很多地方綁定
他執行是第一次oncreate 之後都是startcommand- 想綁東西可以用ibind 他可以綁很多個activity 但是注意 你解開service也要解開綁定
Ex: start -> bind -> unbind -> stop 如果你沒有unbind他會一直在喔 = =- 如果只是一次性的東西沒必要用到service
- 有很潮的東西是前台service(就是持續更新的) 跟 remote service
- android:process=":remote" 但是你要綁activity就要用aidl綁 好處是可以跨程序服務 就是有A程序 可以傳到B程序 = =
- AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
/加remote就可以多很多秒XD/
int anHour = 10 1000;
/這個是開機的時間*/
```
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);- AlarmReceiver extends BroadcastReceiver
- context.startService(i); //重複開
```