Android API27后台重复启动两个应用的问题求助
问题描述
开发环境:最低Android 8.1(API 27)
需求:实现后台每隔一段时间重复启动另外两个应用的功能
现状:单独从MainActivity启动应用正常,但调用makeRepeatTask()时出现NullPointerException(上下文相关)和Looper.prepare相关错误。
现有代码
MainActivity类
import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.text.DecimalFormat; import java.text.NumberFormat; public class MainActivity extends AppCompatActivity { public static Boolean weather =false; public static Boolean kiosk =false; private Launcher launchObj = new Launcher(); private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initialize the TextView textView = findViewById(R.id.textView); //checkRunTimePermission(); //new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); weather=true; //launchWeather(); launchObj.makeRepeatTask(); } }
Launcher类
public class Launcher extends AppCompatActivity { public Launcher(){} public void makeRepeatTask() { Runnable task = () -> { Timer timer = new Timer(); timer.schedule(new UpdateTimeTask(), 1, 10000); }; } class UpdateTimeTask extends TimerTask { public void run() { if (MainActivity.weather){ //Toast.makeText(Launcher.this, "weather .......", Toast.LENGTH_SHORT).show(); System.out.println("-----On TICK------"); launchWeather(); } if (MainActivity.kiosk) { //Toast.makeText(Launcher.this, "kiosk .......", Toast.LENGTH_SHORT).show(); launchKiosk(); } } } public void launchKiosk() { new CountDownTimer(5000, 1000) { @Override public void onTick(long millisUntilFinished) { NumberFormat f = new DecimalFormat("00"); long hour = (millisUntilFinished / 3600000) % 24; long min = (millisUntilFinished / 60000) % 60; long sec = (millisUntilFinished / 1000) % 60; System.out.println(f.format(hour) + ":" + f.format(min) + ":" + f.format(sec)); } @Override public void onFinish() { startNewActivity( Launcher.this, "com.fullykiosk.videokiosk"); MainActivity.kiosk=true; MainActivity.weather=false; } }.start(); } public void launchWeather() { new CountDownTimer(5000, 1000) { @Override public void onTick(long millisUntilFinished) { NumberFormat f = new DecimalFormat("00"); long hour = (millisUntilFinished / 3600000) % 24; long min = (millisUntilFinished / 60000) % 60; long sec = (millisUntilFinished / 1000) % 60; System.out.println(f.format(hour) + ":" + f.format(min) + ":" + f.format(sec)); } @Override public void onFinish() { startNewActivity( Launcher.this, "com.apalon.weatherlive.free"); MainActivity.weather=true; MainActivity.kiosk=false; } }.start(); } public void startNewActivity(Context context, String packageName) { Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName); if (intent == null) { intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("market://details?id=" + packageName)); } intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); runOnUiThread(new Runnable() { public void run() { final Toast toast = Toast.makeText(Launcher.this, "App not Found", Toast.LENGTH_SHORT); toast.show(); } }); } }
Manifest声明
<queries> <package android:name="com.arlosoft.macrodroid" /> <package android:name="com.apalon.weatherlive.free" /> <package android:name="com.fullykiosk.videokiosk" /> </queries>
问题分析
- Launcher类错误继承AppCompatActivity:Activity不能通过
new Launcher()手动实例化,这种方式创建的Activity未经过系统初始化,上下文Launcher.this无效,直接引发NullPointerException。 - TimerTask运行在非UI线程:
UpdateTimeTask是TimerTask,运行在后台线程,而CountDownTimer依赖UI线程的Looper,直接在后台线程创建CountDownTimer会抛出Looper.prepare()错误。 - makeRepeatTask中的Runnable未执行:定义了Runnable但未启动,导致定时任务根本未运行。
- 静态变量状态管理混乱:使用
MainActivity.weather/kiosk静态变量控制切换,逻辑上会导致重复触发,且静态变量在Activity销毁后状态不可控。
修复方案
1. 重构Launcher为普通工具类
去掉AppCompatActivity继承,通过构造方法传入有效上下文:
import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.CountDownTimer; import android.os.Handler; import android.os.Looper; import android.widget.Toast; import java.text.DecimalFormat; import java.text.NumberFormat; public class Launcher { private final Context mContext; private final Handler mHandler; private boolean isWeatherActive = true; // 用成员变量替代静态变量 private Runnable mRepeatRunnable; public Launcher(Context context) { mContext = context; mHandler = new Handler(Looper.getMainLooper()); // 确保在UI线程执行任务 } public void startRepeatTask() { mRepeatRunnable = new Runnable() { @Override public void run() { if (isWeatherActive) { launchWeather(); } else { launchKiosk(); } // 每隔10秒重复执行(可按需调整间隔) mHandler.postDelayed(this, 10000); } }; mHandler.postDelayed(mRepeatRunnable, 1000); // 延迟1秒启动第一个任务 } private void launchKiosk() { new CountDownTimer(5000, 1000) { @Override public void onTick(long millisUntilFinished) { NumberFormat f = new DecimalFormat("00"); long hour = (millisUntilFinished / 3600000) % 24; long min = (millisUntilFinished / 60000) % 60; long sec = (millisUntilFinished / 1000) % 60; System.out.println(f.format(hour) + ":" + f.format(min) + ":" + f.format(sec)); } @Override public void onFinish() { startNewActivity("com.fullykiosk.videokiosk"); isWeatherActive = true; // 切换状态 } }.start(); } private void launchWeather() { new CountDownTimer(5000, 1000) { @Override public void onTick(long millisUntilFinished) { NumberFormat f = new DecimalFormat("00"); long hour = (millisUntilFinished / 3600000) % 24; long min = (millisUntilFinished / 60000) % 60; long sec = (millisUntilFinished / 1000) % 60; System.out.println(f.format(hour) + ":" + f.format(min) + ":" + f.format(sec)); } @Override public void onFinish() { startNewActivity("com.apalon.weatherlive.free"); isWeatherActive = false; // 切换状态 } }.start(); } private void startNewActivity(String packageName) { Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(packageName); if (intent == null) { intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("market://details?id=" + packageName)); } intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); // 在UI线程显示Toast mHandler.post(() -> Toast.makeText(mContext, "启动应用: " + packageName, Toast.LENGTH_SHORT).show()); } public void stopRepeatTask() { if (mRepeatRunnable != null) { mHandler.removeCallbacks(mRepeatRunnable); } } }
2. 修改MainActivity调用逻辑
import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private Launcher launchObj; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textView); launchObj = new Launcher(this); // 传入当前Activity上下文 launchObj.startRepeatTask(); // 启动重复任务 } @Override protected void onDestroy() { super.onDestroy(); // 页面销毁时停止定时任务,避免内存泄漏 launchObj.stopRepeatTask(); } }
3. 后台运行优化(可选)
如果需要应用在后台持续执行定时任务,API 27及以上建议使用AlarmManager配合BroadcastReceiver,因为Handler在应用进入后台后可能被系统暂停:
- 注册BroadcastReceiver接收闹钟广播
- 在广播中触发应用切换逻辑
- 用AlarmManager设置重复闹钟
内容的提问来源于stack exchange,提问作者ETola
相关产品推荐
相关产品推荐

