如何在每日凌晨12点自动创建TextView(不受App生命周期限制)
实现每日凌晨12点自动创建TextView(跨应用生命周期)
核心思路
要实现不受应用生命周期约束的定时任务,需结合AlarmManager和BroadcastReceiver:
AlarmManager负责在指定时间触发定时事件,即便应用处于后台或未启动状态BroadcastReceiver接收闹钟广播,处理笔记创建逻辑:若Activity未前台运行,先将待创建的笔记信息存入SharedPreferences;若Activity在前台,直接切换到主线程更新UI
步骤1:创建BroadcastReceiver
新建DailyNoteReceiver类,用于接收闹钟广播并处理笔记创建:
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.example.app1.NotesActivity; public class DailyNoteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { SharedPreferences sharedPreferences = context.getSharedPreferences("Notes Preferences", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); // 生成带日期的新笔记标题 String newNoteTitle = "Daily Log - " + java.text.DateFormat.getDateInstance().format(new java.util.Date()); // 先保存到SharedPreferences,避免Activity未启动时数据丢失 int noteCount = sharedPreferences.getInt("Number of notes", 0); editor.putString("Note " + noteCount, newNoteTitle); editor.putInt("Number of notes", noteCount + 1); editor.apply(); // 若NotesActivity处于前台,直接更新UI(必须在主线程操作) if (NotesActivity.isActivityVisible()) { new Handler(Looper.getMainLooper()).post(() -> { NotesActivity activity = (NotesActivity) context; TextView newNote = activity.getStyledNote(newNoteTitle); newNote.setOnClickListener(activity); activity.notesList.add(newNote); activity.linearLayout.addView(newNote); }); } } }
步骤2:修改NotesActivity添加闹钟与状态管理
更新NotesActivity,添加Activity可见性标记,在onCreate中设置每日闹钟,并同步加载保存的笔记:
public class NotesActivity extends AppCompatActivity implements View.OnClickListener { // 静态变量标记Activity是否处于前台 public static boolean isActivityVisible = false; LinearLayout linearLayout; ImageButton addNoteButton, infoButton; TextView titleTextNotes; ArrayList<TextView> notesList; SharedPreferences sharedPreferences; Calendar calendar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notes); sharedPreferences = this.getSharedPreferences("Notes Preferences", Context.MODE_PRIVATE); linearLayout = findViewById(R.id.linearLayout); addNoteButton = findViewById(R.id.addNoteButton); infoButton = findViewById(R.id.infoButton); titleTextNotes = findViewById(R.id.titleTextNotes); notesList = new ArrayList<>(); // 初始化每日闹钟 setupDailyAlarm(); // 加载所有保存的笔记(包括广播触发时存入的) loadSavedNotes(); // 原有初始化逻辑 Bundle extras = getIntent().getExtras(); String noteTitle = extras.getString("noteTitle"); if(notesList.size() == 0) { notesList.add(getStyledNote(noteTitle)); linearLayout.addView(notesList.get(notesList.size() - 1)); } for(int i = 0; i < notesList.size(); i++) { notesList.get(i).setOnClickListener(this); } titleTextNotes.setOnClickListener(this); infoButton.setOnClickListener(view -> callInfoActivity()); } // 加载SharedPreferences中保存的所有笔记 private void loadSavedNotes() { int noteCount = sharedPreferences.getInt("Number of notes", 0); for(int i = 0; i < noteCount; i++) { String noteTitle = sharedPreferences.getString("Note " + i, "My Log"); notesList.add(getStyledNote(noteTitle)); linearLayout.addView(notesList.get(notesList.size() - 1)); } } // 设置每日凌晨12点的闹钟 private void setupDailyAlarm() { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, DailyNoteReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // 设置首次触发时间为次日凌晨12点 Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); // 如果当前时间已过凌晨12点,自动延后到次日 if (calendar.getTimeInMillis() <= System.currentTimeMillis()) { calendar.add(Calendar.DAY_OF_MONTH, 1); } // 适配Android 6+的Doze模式,使用精确触发并允许空闲时执行 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent ); } else { alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent ); } } // 更新Activity可见状态 @Override protected void onResume() { super.onResume(); isActivityVisible = true; } @Override protected void onPause() { super.onPause(); isActivityVisible = false; } // 原有方法保持不变 @Override public void onClick(View view) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putInt("Number of notes", notesList.size()); for (int i = 0; i < notesList.size(); i++) { editor.putString("Note " + i, notesList.get(i).getText().toString()); } editor.apply(); for (int i = 0; i < notesList.size(); i++) { if (view.getId() == notesList.get(i).getId()) callMainActivity(i); } } public void callMainActivity(int pos) { Intent sendInfoBack = new Intent(); sendInfoBack.putExtra("noteTitleSentBack", notesList.get(pos).getText()); setResult(RESULT_OK, sendInfoBack); overridePendingTransition(R.anim.leave, R.anim.enter); finish(); } public void callInfoActivity() { Intent intentToLoad = new Intent(NotesActivity.this, InfoActivity.class); startActivity(intentToLoad); overridePendingTransition(R.anim.enter, R.anim.leave); } public TextView getStyledNote(String noteTitle) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 175); params.gravity = Gravity.CENTER; params.setMargins(0, 10, 0, 10); TextView note = new TextView(NotesActivity.this); note.setId(View.generateViewId()); note.setLayoutParams(params); note.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.note_style, null)); note.setTextSize(20); note.setTextColor(Color.BLACK); note.setGravity(Gravity.CENTER); note.setTypeface(getResources().getFont(R.font.sfprodisplaybold)); if(noteTitle == null || noteTitle.equals("")) note.setText("My Log"); else note.setText(noteTitle); return note; } }
步骤3:在AndroidManifest.xml中注册Receiver
在<application>标签内添加广播接收器注册:
<receiver android:name=".DailyNoteReceiver" />
关键注意事项
- Doze模式适配:Android 6.0及以上的Doze模式会限制普通闹钟触发,因此使用
setExactAndAllowWhileIdle确保闹钟能在空闲状态下执行,该方法不支持重复触发,需在广播接收后重新设置下一次闹钟(若需长期重复) - UI线程限制:BroadcastReceiver中不能直接更新UI,必须通过
Handler切换到主线程执行 - 数据持久化:所有笔记信息都存入SharedPreferences,确保应用重启或未启动时数据不丢失
内容的提问来源于stack exchange,提问作者infernicfuse
相关产品推荐
相关产品推荐

