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

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>

问题分析
  1. Launcher类错误继承AppCompatActivity:Activity不能通过new Launcher()手动实例化,这种方式创建的Activity未经过系统初始化,上下文Launcher.this无效,直接引发NullPointerException。
  2. TimerTask运行在非UI线程:UpdateTimeTask是TimerTask,运行在后台线程,而CountDownTimer依赖UI线程的Looper,直接在后台线程创建CountDownTimer会抛出Looper.prepare()错误。
  3. makeRepeatTask中的Runnable未执行:定义了Runnable但未启动,导致定时任务根本未运行。
  4. 静态变量状态管理混乱:使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:14:51