连续的异步任务,每个后续任务使用前一个任务的结果。

huangapple 未分类评论47阅读模式
英文:

Sequential AsyncTasks where each following use result from previous

问题

我稍微思考了一下,我想知道哪种模式可能是实现我的请求的最佳模式。

真实案例:

我有许多必须连续进行的网络调用,每个调用都必须使用前一个调用的结果。这就是我所说的概念:

假设有这个数据库结构:

  • 用户 [id、服务器端创建的信息、信息]
  • 房屋 [id、服务器端创建的信息、用户引用、信息]
  • 房间 [id、服务器端创建的信息、房屋引用、信息]

假设在我的应用程序(离线)中,用户可以创建他的用户实体,N 个房屋实体,并且对于每个房屋,可以创建 N 个房间实体。

然后,完成后,用户正在与服务器同步数据。

应该执行以下操作:

  1. 对于每个用户,将用户发送到 webapi 并读取服务器端信息。
  2. 对于已发送用户的每个房屋,读取服务器端信息,将房屋发送到 webapi 并读取服务器端信息。
  3. 对于已发送房屋的每个房间,读取服务器端信息,将房间发送到 webapi 并读取服务器端信息。

可能是这样完成的:

  • 创建一个 AtomicInteger,它是要执行的所有同步的计数。

使用用户、房屋和房间的总和初始化这个值。

void syncDone() {
  if(totalSyncCounter.decrementAndGet() == 0){
    //同步完成,提示用户
  }
}

创建 AsyncTasks:

class SendUserToDbAsyncTask extends AsyncTask<User, Void, Integer> {
    // ...(此处省略一些代码,与之类似)
}

class SendHouseToDbAsyncTask extends AsyncTask<House, Void, Integer> {
    // ...(此处省略一些代码,与之类似)
}

class SendRoomToDbAsyncTask extends AsyncTask<Room, Void, Integer> {
    // ...(此处省略一些代码,与之类似)
}

逻辑:

void DoStuffs() {
  mySyncCounter = [users + houses + rooms];
  for (User user : users) {
    new SendUserToDbAsyncTask(new UserSavedCallback() {
      void onResult(){
        syncDone();
        for (House house : user.houses) {
          new SendHouseToDbAsyncTask(new HouseSavedCallback() {
            void onResult(){
              syncDone();
              for (Room room : house.rooms) {
                new SendRoomToDbAsyncTask(new RoomSavedCallback() {
                  void onResult(){
                    syncDone();
                }).execute(room);
              }
          }).execute(house);
        }
      }).execute(user);
    }
  }
}

显然,这只是一个在 Stack Overflow 手写的示例。我只是想让您理解要点。我知道我可以为所有操作创建一个单独的回调,将其在方法外部初始化,只需为一切创建一个单独的异步任务,等等... 但我不寻求有关优化此特定代码的任何建议,我只想知道如何执行多个连续的异步操作,其中每个后续操作使用前一个操作的返回。

在执行这种类型的操作时,什么是最佳方法?

是否有更“清晰”的方法来做到这一点?

谢谢

英文:

I thought a bit about this and I was wondering which could be the best pattern to accomplish my request.

Real case:

I have many web calls which must be sequential and where every call must use the result of the previous. This is the concept of what I mean:

Suppose to have this db structure:

  • User [id, server-side created info, infos]
  • House [id, server-side created info, user ref, infos]
  • Room [id, server-side created info, house ref, infos]

Suppose that with my app (offline) an user can create his user entity, N house entities and N room entities for each house.

Then, once done, the user is syncing data with server.

This is what should be done:

  1. For each user, send user to webapi and read the server-side info.
  2. For each house of the sent user, read the server-side info, send the house to webapi and read the server-side info.
  3. For each room of the sent house, read the server-side info, send the room to webapi and read the server-side info.

This might be done this way:

  • Create an AtomicInteger that is the count of all sync to be done.

Init this value with the sum of users, houses, rooms.

void syncDone() {
  if(totalSyncCounter.decrementAndGet() == 0){
    //sync finished, prompt user
  }
}

Create AsyncTasks

class SendUserToDbAsyncTask extends AsyncTask&lt;User, Void, Integer&gt; {

    UserSavedCallback _callback;

    public SendUserToDbAsyncTask(UserSavedCallback _callback) {
        this._callback = _callback;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Integer doInBackground(User... users) {
        //save user and return the server-side value
        return serverSideInfo;
    }

    @Override
    protected void onPostExecute(Integer res) {
        super.onPostExecute(res);
        _callback.onResult(res);
    }
}

class SendHouseToDbAsyncTask extends AsyncTask&lt;House, Void, Integer&gt; {

    HouseSavedCallback _callback;

    public SendHouseToDbAsyncTask(HouseSavedCallback _callback) {
        this._callback = _callback;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Integer doInBackground(House... houses) {
        //save user and return the server-side value
        return serverSideInfo;
    }

    @Override
    protected void onPostExecute(Integer res) {
        super.onPostExecute(res);
        _callback.onResult(res);
    }
}

class SendRoomToDbAsyncTask extends AsyncTask&lt;User, Void, Integer&gt; {

    RoomSavedCallback _callback;

    public SendRoomToDbAsyncTask(RoomSavedCallback _callback) {
        this._callback = _callback;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Integer doInBackground(Room... rooms) {
        //save user and return the server-side value
        return serverSideInfo;
    }

    @Override
    protected void onPostExecute(Integer res) {
        super.onPostExecute(res);
        _callback.onResult(res);
    }
}

Logic:

void DoStuffs() {

  mySyncCounter = [users + houses + rooms];
  for (User user : users) {
    new SendUserToDbAsyncTask(new UserSavedCallback() {
      void onResult(){
        syncDone();
        for (House house : user.houses) {
          new SendHouseToDbAsyncTask(new HouseSavedCallback() {
            void onResult(){
              syncDone();
              for (Room room : house.rooms) {
                new SendRoomToDbAsyncTask(new RoomSavedCallback() {
                  void onResult(){
                    syncDone();
                }).execute(room);
              }
          }).execute(house);
        }
    }).execute(user);
  }
}

Obiouvsly it is just an example hand-wrote here on SO. I just want to make you get the point. I know I can make a single callback for all, init it outside the method, just create a single asynctask for everything, etc... but I'm not looking for any suggestion about optimizing this specific code, I just want to know how to perform multiple sequential async operations where every following operation use the return of the previous.

What is the best way for performing this kind of operations?

Is there a "cleaner" way of doing it?

Thanks

答案1

得分: 0

如果我理解正确。
您可以使用ExecutorService来调用同步服务器,并使用Semaphore来按您喜欢的方式停止线程。
但无论如何,您仍然需要使用AsyncTask,因为使用ExecutorService会导致调用失败。

英文:

If I understand correctly.<br>
You could use ExecutorService to make calls to the sync server, and Semaphore to stop the thread as you like.<br>
Only you will have to use AsyncTask anyway, because the calls would only fail with ExecutorService

huangapple
  • 本文由 发表于 2020年4月3日 21:22:12
  • 转载请务必保留本文链接:https://java.coder-hub.com/61012885.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定