Android startService()需要很长时间才能返回UI线程

发布于 2024-12-19 14:47:24 字数 2122 浏览 3 评论 0原文

第一次启动时,我的用例(大致)如下:

  1. 活动启动服务
  2. 服务获取数据并将其保存在数据库中
  3. 服务通知具有意图的活动
  4. 活动显示数据

现在我想在服务繁忙时显示进度条。问题是:

startService(new Intent(getApplicationContext(), UpdateDataService.class));

需要很长时间才能“返回”UI 线程。它似乎是一个同步函数(或者不是?)。如果服务类为空,则 startService 命令几乎会立即处理。看起来 UI 线程等待 Serice 来处理它的工作,这根本没有意义。我尝试启动(尽管看起来很愚蠢)启动异步任务服务,同时在 UI 线程中显示进度条。奇怪的是,有时这会起作用。有时,当我的服务正在运行时,我只会看到白屏,然后是毫秒级的进度条,然后是我的用户界面。

现在我的问题是:如何在不阻塞用户界面的情况下启动服务?

public class MyClass extends TabActivity {
private ProgressDialog pd;

@Override
public void onCreate(final Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent intent = null;

    //building some tabs here, setting some text views....

    // starting service if does not exist yet
    boolean serviceRunning = false;

    final ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (final RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if ("aegir.mobile.UpdateDataService".equals(service.service.getClassName())) {
            serviceRunning = true;
            Log.i(MY_APP_TAG, "Service found.");
        }
    }
    if (!serviceRunning) {
        pd = ProgressDialog.show(this, "Loading...", "Setting up data.", true, false);
        new StartServiceAsync().execute("");
    }

}

private final Handler handler = new Handler() {
    @Override
    public void handleMessage(final Message msg) {
        pd.dismiss();

    }
};

public class StartServiceAsync extends AsyncTask<String, Void, String> {

    @Override
    protected String doInBackground(final String... params) {
        // starting service
        startService(new Intent(getApplicationContext(), UpdateDataService.class));
        return null;
    }

    @Override
    protected void onPostExecute(final String result) {
        handler.sendEmptyMessage(0);
        super.onPostExecute(result);
    }
}

my usecase is (roughly) the following on first startup:

  1. activity starts a service
  2. service gets and saves data in a database
  3. service notifies activity with an intent
  4. activity displays data

Now I want to display a progress bar while the service is busy. The problem is:

startService(new Intent(getApplicationContext(), UpdateDataService.class));

takes a very long time to "come back" to the UI thread. It seems to be a synchronized function (or not?). If a empty the service class the startService command is processed almost instant. It seems the UI thread waits for the Serice to handle its work, which does not make sense at all. I tried to start (as stupid as it may seem) to start the service a async task while displaying a progress bar in my UI thread. Weird enough this works sometimes. Othertimes I just get a white screen while my service is working and afterwards for a millisecond ma progressbar and then my UI.

Now my question: How do I start the service without blocking my UI?

public class MyClass extends TabActivity {
private ProgressDialog pd;

@Override
public void onCreate(final Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent intent = null;

    //building some tabs here, setting some text views....

    // starting service if does not exist yet
    boolean serviceRunning = false;

    final ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (final RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if ("aegir.mobile.UpdateDataService".equals(service.service.getClassName())) {
            serviceRunning = true;
            Log.i(MY_APP_TAG, "Service found.");
        }
    }
    if (!serviceRunning) {
        pd = ProgressDialog.show(this, "Loading...", "Setting up data.", true, false);
        new StartServiceAsync().execute("");
    }

}

private final Handler handler = new Handler() {
    @Override
    public void handleMessage(final Message msg) {
        pd.dismiss();

    }
};

public class StartServiceAsync extends AsyncTask<String, Void, String> {

    @Override
    protected String doInBackground(final String... params) {
        // starting service
        startService(new Intent(getApplicationContext(), UpdateDataService.class));
        return null;
    }

    @Override
    protected void onPostExecute(final String result) {
        handler.sendEmptyMessage(0);
        super.onPostExecute(result);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

飘逸的'云 2024-12-26 14:47:24

从指南主题服务

警告:默认情况下,服务与声明它的应用程序在同一进程中运行,并且在该应用程序的主线程中运行。因此,如果您的服务在用户与同一应用程序中的活动交互时执行密集或阻塞操作,则该服务将降低活动性能。为了避免影响应用程序性能,您应该在服务内启动一个新线程。

这不会仅仅因为您从 AsyncTask 调用 startService 而改变。

From the guide topic Services:

Caution: A services runs in the same process as the application in which it is declared and in the main thread of that application, by default. So, if your service performs intensive or blocking operations while the user interacts with an activity from the same application, the service will slow down activity performance. To avoid impacting application performance, you should start a new thread inside the service.

This doesn't change just because you call startService from an AsyncTask.

薄荷梦 2024-12-26 14:47:24

我认为您应该将逻辑从服务移动到异步任务中的 doInBackground 方法。这就是异步任务的用途,执行艰苦的工作,为您提供一种与 UI 交互的简单方法。在异步任务中调用服务很奇怪,你这样做有什么原因吗?

I think you should just move the logic from the service to the doInBackground method in the async task. That's what async task are for, executing hard work giving you a simple way to interact with the UI. It's weird to call a service in the async task, is there any reason why you did that?

风和你 2024-12-26 14:47:24

要么在 AsyncTask 本身中完成工作,使用 IntentService 而不是普通的旧 Service,或者从您的 Service< /code> 来完成你的工作。 Android 中的 Service 本身并不使用不同的线程(IntentService 确实可以完成其工作)。

尽管从 AsyncTask 启动您的 Service,但现在看来实际工作是在您的 UI 线程中执行的。

Either do your work in the AsyncTask itself, use an IntentService instead of a plain old Service or start a thread from your Service to do your work. A Service in Android does not inherently use a different thread (IntentService does to do its work).

Despite starting your Service from an AsyncTask, right now it seems the actual work is being performed in your UI thread.

浅暮の光 2024-12-26 14:47:24

请使用此代码以简单的方式执行您所提出的问题。

@Override
public void onCreate(final Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent intent = null;
    registerReceiver(dataUpdated, new IntentFilter("<FLAG>"));
    //building some tabs here, setting some text views....

    // starting service if does not exist yet
    boolean serviceRunning = false;

    final ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (final RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if ("aegir.mobile.UpdateDataService".equals(service.service.getClassName())) {
            serviceRunning = true;
            Log.i(MY_APP_TAG, "Service found.");
        }
    }
    if (!serviceRunning) {
        pd = ProgressDialog.show(this, "Loading...", "Setting up data.", true, false);
        startService(new Intent(getApplicationContext(), UpdateDataService.class));
    }

}


private BroadcastReceiver dataUpdated= new BroadcastReceiver() {
    @SuppressLint("ShowToast")
    @Override
    public void onReceive(Context context, Intent intent) {
        //<Your Process which you want when Service finishes a task >
              pd.dismiss();
    }
};

@Override
protected void onDestroy() {

    unregisterReceiver(dataUpdated);
}

在服务中,当您的任务完成时调用此方法

sendBroadcast(new Intent("<FLAG>"));

please Use this code the easy way to do what you asked in question.

@Override
public void onCreate(final Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent intent = null;
    registerReceiver(dataUpdated, new IntentFilter("<FLAG>"));
    //building some tabs here, setting some text views....

    // starting service if does not exist yet
    boolean serviceRunning = false;

    final ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (final RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if ("aegir.mobile.UpdateDataService".equals(service.service.getClassName())) {
            serviceRunning = true;
            Log.i(MY_APP_TAG, "Service found.");
        }
    }
    if (!serviceRunning) {
        pd = ProgressDialog.show(this, "Loading...", "Setting up data.", true, false);
        startService(new Intent(getApplicationContext(), UpdateDataService.class));
    }

}


private BroadcastReceiver dataUpdated= new BroadcastReceiver() {
    @SuppressLint("ShowToast")
    @Override
    public void onReceive(Context context, Intent intent) {
        //<Your Process which you want when Service finishes a task >
              pd.dismiss();
    }
};

@Override
protected void onDestroy() {

    unregisterReceiver(dataUpdated);
}

In Service when your Task will Complete call this Method

sendBroadcast(new Intent("<FLAG>"));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文