【问题标题】:AsynTask scheduled in Android service executes properly only one time在 Android 服务中调度的 AsyncTask 只正确执行了一次
【发布时间】:2012-01-17 21:15:14
【问题描述】:

我正在尝试创建一个简单的 Android 程序,该程序每 30*1000 毫秒拉一次 Web 服务器。我的应用程序中有专门的服务 - PollerService

Android Manifest.xml:
<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".PullServerAndListenActivivty"
        android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".PollerService" />
</application>

该类创建调度执行器并注册其计时器任务以开始调用引擎,从而拉动服务器。

PollerService.java:

public class PollerService extends Service {
public static final int INTERVAL = 20*1000;
private ScheduledExecutorService executor;

public void onCreate() {
    super.onCreate();
    executor = Executors.newSingleThreadScheduledExecutor();
    executor.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            Engine.tick ();             
        }
    }, 0, INTERVAL, TimeUnit.MILLISECONDS);
}

public void onDestroy() {
    executor.shutdownNow();
}

public IBinder onBind(Intent arg0) {
    return null;
};
}

引擎只是在 GUI 线程中创建 AsyncTask 子类实例并执行它。 onPostExecute 通过 Activity 中分配的控制台写入一行并调用 UI 组件。

public class Engine {
private static Console console; //Assigned in Activity onCreate
private static Activity context;//Assigned in Activity onCreate 
    ...
public static void tick() {
    final String requestURL = String.format(urlFormat, serverURL);
    try {
        context.runOnUiThread(
                new Runnable () {
                    public void run() {
                        new RequestTask().execute(requestURL);                      
                    };
                }
        );
    }
    catch (Throwable e) {
        Log.e("SBOX", e.getMessage(), e);
    }
}

public static void processCommand(String result) {
    try {
        final Command command = fromJSONToCommand(result);
        context.runOnUiThread(new Runnable() {
            public void run() {
                console.writeLine("Receieved command: " + command);
            }
        });
    } catch (Exception e) {
        Log.e ("SBOX", e.getMessage(), e);      
    }
}

Yak,我从简单的 RequestTask 实例创建开始,我的印象是它会自行在 UI 线程上运行,但试图解决这个问题我终于找到了这种方法,仍然没用。只想说我在这里尝试了很多。

RequestTask 长这样,简单的http调用:

class RequestTask extends AsyncTask<String, Void, String>{

@Override
protected String doInBackground(String... uri) {
    HttpClient httpclient = new DefaultHttpClient();
    HttpResponse response;
    String responseString = null;
    try {
        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);
            out.close();
            responseString = out.toString();
        } else{
            response.getEntity().getContent().close();
            throw new IOException(statusLine.getReasonPhrase());
        }
    } catch (Exception e) {
        Log.e("SBOX", e.getMessage(), e);
    }
    return responseString;
}

@Override
protected void onPostExecute(String result) {
    super.onPostExecute(result);
    Engine.processCommand (result);
}
}

所以问题是它只正确运行一次,计时器调度,第一次调用引擎,它运行 RequestTask,它拉服务器,获取响应,在控制台中打印它。但是第二次它进入计时器方法,然后服务没有生命迹象。 LogCat 中没有异常,登录 RequestTask 没有打印出来。

当然是时候开始调试了,但也许有人可以让我摆脱这种痛苦并在代码中显示错误?如果对 Engine.tick() 调用进行注释,则执行程序本身可以完美地连续工作。

对于解决问题的线索非常有用。

【问题讨论】:

    标签: java android multithreading httpclient android-asynctask


    【解决方案1】:

    我正在尝试创建一个简单的 Android 程序,该程序每 30*1000 毫秒拉一次 Web 服务器。

    你肯定会走得很辛苦。

    第 1 步:在您的活动和服务之间建立一个真正的沟通渠道。不要使用静态数据成员,因为您正在创建可能的内存泄漏。有很多候选人(createPendingResult()Messenger 等)。为了这个答案,我假设您将使用广播Intent,因此选择一些独特的操作字符串并在您的活动中在onResume() 中设置一个BroadcastReceiver 来收听这样的广播(和删除onPause() 中的那个接收者)。

    第 2 步:在您的服务中创建一个 pollServer() 方法,其中包含来自您的 doInBackground() 方法的代码来自您的 RequestTask。但是,与其返回一个字符串,不如让它发送广播Intent,并将您的字符串作为额外的。

    第 3 步:从您的 ScheduledExecutorService 呼叫 pollServer(),而不是 Engine.tick()

    第 4 步:完全删除 EngineRequestTask

    【讨论】:

    • 谢谢,听起来很有道理,我会试试这个方法,让你知道它是否有帮助
    【解决方案2】:

    在 AsyncTask 的文档中,它给出了以下与线程相关的规则:

    该任务只能执行一次(如果尝试第二次执行将抛出异常。)

    这意味着每次你想使用它时都必须创建一个新的类实例。换句话说,必须这样做:

    new DownloadFilesTask().execute(url1, url2, url3);
    new DownloadFilesTask().execute(url4, url5, url6);
    

    或者相反,您不能执行以下操作:

    DownloadFilesTask dfTask = new DownloadFilesTask();
    dfTask().execute(url1, url2, url3);
    dfTask().execute(url4, url5, url6);
    

    【讨论】:

    • 这实际上就是我在 Engine.tick() 中所做的 - new RequestTask().execute(requestURL);
    • 你试过新的 RequestTask().execute(requestURL); requestURL 更改为 requestURL1,2 等。
    • 好吧,看不出这有什么帮助。查看@CommonsWare 的回复——他的方法看起来更像 Android。
    • 太好了,我认为你应该使用@CommonsWare 方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-02
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多