Android 即使屏幕被锁定,我如何在后台定期运行服务/线程?

Android 即使屏幕被锁定,我如何在后台定期运行服务/线程?,android,multithreading,service,wakelock,Android,Multithreading,Service,Wakelock,我正在开发一款可以每半小时检查一次网络数据的应用程序,我需要确保它在通电的情况下一直运行。 目前,我的应用程序的结构如下: 主要活动: onCreate()中的AlarmManager 报警接收器: 启动服务 获取服务的部分内容 服务: 使用StrictMode获取网络数据 如果需要数据,则弹出活动2 活动2: 振动 按钮退出(活动2.this.finish()) 但在测试中,我发现服务将在前30分钟后停止(被终止)。此外,如果我在服务中启动一个用于联网的线程而不是使用StrictMode,它将

我正在开发一款可以每半小时检查一次网络数据的应用程序,我需要确保它在通电的情况下一直运行。 目前,我的应用程序的结构如下:

  • 主要活动:
    onCreate()中的AlarmManager
  • 报警接收器:
    启动服务 获取服务的部分内容
  • 服务:
    使用StrictMode获取网络数据 如果需要数据,则弹出活动2
  • 活动2:
    振动 按钮退出
    (活动2.this.finish())
  • 但在测试中,我发现服务将在前30分钟后停止(被终止)。此外,如果我在服务中启动一个用于联网的线程而不是使用StrictMode,它将在屏幕锁定后5分钟内被终止

    希望有人能对此提出建议。真令人不安。
    非常感谢。

    普通使用寿命不管活动发生了什么。如果您希望定期启动,请查看矿山服务:

    /** *定期从网络中加载汇率 *返回广播消息中的结果。 *由koss于2016年2月19日创建。 * */ 公共类EcbEuropeService扩展服务{

    public static final String ECB_URL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
    public static final int UPDATE_PERIOD = 30000;
    public static final int UPDATE_TICK = 1000;
    
    public static final String NOTIFICATION = "koss.ru.oneclickrate.receiver";
    public static final String EXTRA_CURRENCIES_MAP = "extra_currencies_map";
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        getUrlData();
        return Service.START_NOT_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    public Cubes getUrlData() {
        (new AsyncTask<Object, Object, Cubes>() {
            Map<CurrencyType, BigDecimal> result = new EnumMap<CurrencyType, BigDecimal>(CurrencyType.class);
    
            @Override
            protected Cubes doInBackground(Object... params) {
                Cubes cubes = new Cubes();
                InputStream is = null;
                HttpURLConnection urlConnection = null;
                try {
                    URL url = new URL(ECB_URL);
                    urlConnection = (HttpURLConnection) url.openConnection();
                    is = urlConnection.getInputStream();
                    cubes = EcbEuropeResponseParser.parse(is);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(urlConnection!=null) IOUtils.close(urlConnection);
                    if(is!=null) IOUtils.closeQuietly(is);
                    return cubes;
                }
            }
    
            @Override
            protected void onPostExecute(Cubes map) {
                super.onPostExecute(map);
                sendBroadcastMessage(map);
                startTimer();
            }
        }).execute();
        return null;
    }
    
    /**
     * Restarts timer
     * */
    public void startTimer() {
        cdt.cancel();
        cdt.start();
    }
    
    CountDownTimer cdt = new CountDownTimer(UPDATE_PERIOD, UPDATE_TICK) {
        @Override
        public void onTick(long millisUntilFinished) {
    
        }
    
        public void onFinish() {
            getUrlData();
        }
    };
    
    private void sendBroadcastMessage(Cubes currenciesMap) {
        Intent intent = new Intent(NOTIFICATION);
        intent.putExtra(EXTRA_CURRENCIES_MAP, currenciesMap);
        sendBroadcast(intent);
    }
    
    public静态最终字符串ECB\u URL=”http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
    公共静态最终整数更新周期=30000;
    公共静态最终整数更新_TICK=1000;
    公共静态最终字符串通知=“koss.ru.oneclickrate.receiver”;
    公共静态最终字符串EXTRA\u currences\u MAP=“EXTRA\u currences\u MAP”;
    @凌驾
    公共int onStartCommand(Intent Intent、int标志、int startId){
    getUrlData();
    退货服务。开始时不粘;
    }
    @凌驾
    公共IBinder onBind(意向){
    返回null;
    }
    公共多维数据集getUrlData(){
    (新任务(){
    映射结果=新的EnumMap(CurrencyType.class);
    @凌驾
    受保护的多维数据集doInBackground(对象…参数){
    立方体=新立方体();
    InputStream=null;
    HttpURLConnection-urlConnection=null;
    试一试{
    URL URL=新URL(ECB_URL);
    urlConnection=(HttpURLConnection)url.openConnection();
    is=urlConnection.getInputStream();
    cubes=ecbeuropresponseparser.parse(is);
    }捕获(例外e){
    e、 printStackTrace();
    }最后{
    if(urlConnection!=null)IOUtils.close(urlConnection);
    if(is!=null)IOUtils.closes(is);
    返回立方体;
    }
    }
    @凌驾
    受保护的void onPostExecute(多维数据集映射){
    super.onPostExecute(map);
    发送广播信息(map);
    startTimer();
    }
    }).execute();
    返回null;
    }
    /**
    *重新启动计时器
    * */
    公共无效startTimer(){
    cdt.cancel();
    cdt.start();
    }
    倒计时cdt=新倒计时(更新周期,更新勾号){
    @凌驾
    公共void onTick(长毫秒未完成){
    }
    公共无效onFinish(){
    getUrlData();
    }
    };
    私有void发送广播消息(多维数据集当前映射){
    意向=新意向(通知);
    意向。额外支付(额外货币地图、货币地图);
    发送广播(意图);
    }
    
    我做了一些改动,现在效果很好

    1.由于我的手机是4.4.2(api=19),alarmmanager.setrepeating不准确。因此我转而使用.setExact(新方法.set())并在服务中的AsyncTask(网络)结束时重新安排闹钟

    2.将wakelock实例设置为全局,在AlarmReceiver中获取它,并在AsyncTask结束时释放。我曾经在onDestroy()中放置.release(),它在任务完成之前释放锁


    3.我的手机中有一个关于受保护的后台应用程序的设置,我没有打开它。这会允许系统关闭应用程序并禁用报警管理器。

    运行后台服务只是为了检查是否有新数据可用不是一个好主意。试着使用GCM实现同样的设置。您不需要创建服务。AlarmManager每半小时重复一次不精确的警报就可以了。事实上,它就是为了这个目的而设计的。我还建议在有数据时发出通知,而不是自己显示活动。这样,用户就可以收到通知,并可以在方便的时候打开已有数据的活动。