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

Android定时HTTP请求Service运行1小时崩溃问题修复建议

分析与修复方案:Android Service运行1小时后崩溃问题

你的Service初期运行正常但1小时后崩溃,是多个潜在问题叠加导致的,我来逐一拆解并给出可落地的修复方案:


1. 最致命问题:主线程被阻塞引发ANR/崩溃

你在Runnable的run()方法里调用了new RequestTask().execute(url).get(),get()是同步等待AsyncTask执行结果的方法,会直接卡死Handler所在的主线程。每次网络请求不管快慢,主线程都会被强制等待,多次累积后必然触发ANR(应用无响应),甚至直接崩溃。

修复方案:

彻底移除get(),改用AsyncTask的onPostExecute回调异步处理结果:

首先给RequestTask添加回调接口,让请求完成后主动通知结果:

// 定义回调接口
interface OnRequestCompleteListener {
    void onSuccess(String response);
    void onFailure();
}

class RequestTask extends AsyncTask<String, String, String> {
    private OnRequestCompleteListener listener;

    public RequestTask(OnRequestCompleteListener listener) {
        this.listener = listener;
    }

    @Override
    protected String doInBackground(String... uri) {
        HttpClient httpclient = new DefaultHttpClient();
        String responseString = null;
        try {
            // 原网络请求逻辑保留,注意添加资源关闭逻辑
            HttpResponse response = httpclient.execute(new HttpGet(uri[0]));
            StatusLine statusLine = response.getStatusLine();
            if(statusLine.getStatusCode() == HttpStatus.SC_OK){
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                response.getEntity().writeTo(out);
                responseString = out.toString();
                out.close();
            } else{
                response.getEntity().getContent().close();
                throw new IOException(statusLine.getReasonPhrase());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭HttpClient连接池,避免资源泄漏
            httpclient.getConnectionManager().shutdown();
        }
        return responseString;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        if (result != null && !result.isEmpty()) {
            listener.onSuccess(result);
        } else {
            listener.onFailure();
        }
    }
}

然后在Runnable里异步调用任务,不在主线程等待:

new RequestTask(new OnRequestCompleteListener() {
    @Override
    public void onSuccess(String response) {
        // 这里处理原有的字符串解析逻辑
        String withoutitlne = response.replaceAll("\\r|\\n", "");
        String input = withoutitlne.replace(" ", "");
        if (input != null && !input.isEmpty()) {
            String[] separated = response.split("\\*");
            // 先判断数组长度,避免越界崩溃
            if (separated.length >= 3) {
                String nb = separated[1];
                String am = separated[2];
                String oper = separated[0];
                String nnb = nb.replace(" ", "");
                if (oper.indexOf("GP") >= 0) {
                    // 注意:原代码中`rb`变量未定义,请替换为实际存在的变量
                    Intent newintent = new Intent(getApplicationContext(), Recharge.class);
                    newintent.putExtra("rcnumber", nnb);
                    newintent.putExtra("rcamount", am);
                    // newintent.putExtra("gpin", rb); // 修正此处未定义的变量
                    startService(newintent);
                }
            }
        }
        // 请求完成后再安排下一次任务
        handler.postDelayed(runnable, runTime);
    }

    @Override
    public void onFailure() {
        // 请求失败也继续循环任务
        handler.postDelayed(runnable, runTime);
    }
}).execute(url);

2. Handler内存泄漏导致内存耗尽

你使用的匿名内部类Runnable会持有Server Service的强引用,加上handler.postDelayed的循环机制,即使Service被销毁,这个引用也会让Service无法被GC回收,长期运行会导致内存泄漏累积,最终触发OOM崩溃。

修复方案:

  • 在onDestroy中移除Handler的所有回调,切断引用链:
@Override
public void onDestroy() {
    super.onDestroy();
    if (handler != null) {
        // 移除所有待执行的回调和消息
        handler.removeCallbacksAndMessages(null);
    }
    // 原AlarmManager重启代码保留
    // ...
}
  • 改用静态内部类+弱引用定义Runnable,避免持有Service的强引用:
private static class TaskRunnable implements Runnable {
    private WeakReference<Server> serviceRef;
    private Handler handler;
    private final int runTime = 5000;

    public TaskRunnable(Server service) {
        this.serviceRef = new WeakReference<>(service);
        this.handler = new Handler();
    }

    @Override
    public void run() {
        Server service = serviceRef.get();
        // Service已被回收则终止任务
        if (service == null) return;
        
        if (!service.isOnline(service)) {
            handler.postDelayed(this, runTime);
            return;
        }
        String url = "http://localhost:8080/feed/appfeed.php";
        new RequestTask(new OnRequestCompleteListener() {
            @Override
            public void onSuccess(String response) {
                // 处理结果逻辑...
                handler.postDelayed(TaskRunnable.this, runTime);
            }

            @Override
            public void onFailure() {
                handler.postDelayed(TaskRunnable.this, runTime);
            }
        }).execute(url);
    }
}

然后在onCreate中初始化:

@Override
public void onCreate() {
    super.onCreate();
    handler = new Handler();
    runnable = new TaskRunnable(this);
    handler.post(runnable);
}

3. 未处理的空指针与数组越界异常

原代码存在多个潜在崩溃点:

  • 网络请求失败时output为null,直接调用output.replaceAll会抛出NPE
  • split("\\*")后的数组长度可能不足3,直接访问separated[1]/separated[2]会触发数组越界
  • 代码中rb变量未定义,编译都无法通过(你说初期正常,大概率是笔误)

修复方案:

  • 对所有可能为null的变量做非空判断
  • 拆分字符串后先检查数组长度
  • 修正未定义的rb变量问题,替换为实际存在的参数

4. HttpClient过时与资源泄漏

Android 6.0(API 23)之后HttpClient被官方移除,且原代码未正确关闭连接池,长期运行会导致连接资源耗尽。

修复方案:

改用官方推荐的HttpURLConnection,示例如下:

@Override
protected String doInBackground(String... uri) {
    HttpURLConnection urlConnection = null;
    BufferedReader reader = null;
    String responseString = null;
    try {
        URL url = new URL(uri[0]);
        urlConnection = (HttpURLConnection) url.openConnection();
        // 设置User-Agent
        String userAgent = "Mozilla/5.0 (Linux; Android "+getAndroidVersion()+"; "+getDeviceName()+" Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36";
        urlConnection.setRequestProperty("User-Agent", userAgent);
        urlConnection.setRequestMethod("GET");
        urlConnection.connect();

        if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
            InputStream inputStream = urlConnection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder buffer = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            responseString = buffer.toString();
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 关闭所有资源
        if (urlConnection != null) urlConnection.disconnect();
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return responseString;
}

5. 定时任务稳定性优化

用Handler.postDelayed做长期定时任务不稳定,一旦Service被系统回收(后台内存紧张时很常见),任务就会中断。你已经用AlarmManager做重启,但原代码的AlarmManager调用存在适配问题。

修复方案:

适配不同Android版本的AlarmManager API,使用正确的PendingIntent flag:

@Override
public void onDestroy() {
    super.onDestroy();
    Toast.makeText(getApplicationContext(), "Service Task destroyed", Toast.LENGTH_LONG).show();
    Intent myIntent = new Intent(getApplicationContext(), Server.class);
    // 根据Android版本选择合适的flag
    int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? 
                PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_UPDATE_CURRENT;
    PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 0, myIntent, flags);
    AlarmManager alarmManager1 = (AlarmManager) getSystemService(ALARM_SERVICE);
    long triggerTime = System.currentTimeMillis() + 10 * 1000;
    
    // 适配不同版本的精确触发API
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager1.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        alarmManager1.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
    } else {
        alarmManager1.set(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
    }
}

onTaskRemoved中的AlarmManager代码做同样修改即可。


修复后关键总结

  • 绝对不要在主线程调用AsyncTask.get(),改用异步回调
  • 用静态内部类+弱引用避免Handler内存泄漏
  • 对所有对象做非空/边界检查,杜绝NPE和数组越界
  • 替换过时的HttpClient,用HttpURLConnection并正确关闭资源
  • 适配AlarmManager API,保证Service在后台能稳定重启

内容的提问来源于stack exchange,提问作者Osman Gani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:22:12