You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在每日凌晨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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:57:09