gpt4 book ai didi

java - 如何使在主线程中运行的某个类同时等待其他类达到某种特定状态?

转载 作者:行者123 更新时间:2023-12-02 10:47:36 25 4
gpt4 key购买 nike

我很难理解如何使一些 AsyncTask 子级在主线程中声明和实例化,以等待 Service 子级实例达到某些特定状态。

这里的代码示例是服务的相关部分;此代码执行预期操作:接收 JSON 响应并保存它。

public class MyService extends Service {
private boolean received = false;
private string url = "http://someserver.mine/get-data-in-json-format";

// [...]
@Override
public void onCreate() {
doHttpJsonQuery();
}

public boolean responseReceived() {
return this.received;
}

public List<MyModel> getResponseAsObject() {
if (!this.received) return new ArrayList<MyModel>;

// Many code lines that convert the data into a list.
// [...]

return the_list;
}

// [...]
private void doHttpJsonQuery() {

OkHttpClient client = new OkHttpClient.Builder()
.build();

Request request = new Request.Builder()
.url(url)
.build();

client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
call.cancel();
}

@Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
//...and some code to hold data as JSONArray
//[...]
}
});
this.received = true;
}
}

该服务有效;美好的。然后,从另一个类(其目的是处理将接收到的数据插入本地房间数据库的持久性),我尝试执行以下操作(这就是我的想法):

public class DataRepository {
private MyRoomDatabase db;
private MyModelDao mModelDao;
// I'm skipping irrelevant code on purpose
// [...]

public DataRepository(Application application) {
db = MyRoomDatabase.getDatabase(application);
mModelDao = db.myModelDao();
// [...]

// Here I instance a subclass of ContextWrapper(i named it RemoteDataSource) which
// responsability will be handling different Services for making HTTP operations
mRemoteDataSource = new RemoteDataSource(application.getApplicationContext());
// It holds a reference to MyService. It has some public methods, like this one, to
// control the referenced Service from outside with some encaspsulation
mRemoteDataSource.startMyService();

// Instantiating a private nested class...
PopulateDbAsync mPopulateDbAsync = new PopulateDbAsync(db);
mPopulateDbAsync.doInBackground();
}
// [...]

// Here is the failing code
private class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
PopulateDbAsync(MyRoomDatabase db) {}

@Override
protected Void doInBackground(final Void... params) {
MyService mService = mRemoteDataSource.getMyService();
if (mService == null) {
// This doesn't happen at all right now...
Log.e("MY_ERROR","DataRepository.PopulateDbAsync --> MyService from RemoteDataSource is NULL!!!!");
}
List<MyModel> the_list = mService.getResponseAsObject();
if (the_list == null) {
// HERE! I obtain the NullReferenceException here.
// I am confused about how would I avoid this flaw in my code
Log.e("MY_ERROR", "DataRepository.PopulateDbAsync --> error: response isn't ready yet.");
}
for (MyModel i_model : the_list) {
Log.d("MY_LOG", "DataRepository.PopulateDbAsync --> Inserting data in local DB...");
mModelDao.insert(i_model);
}
return null;
}
}
}

总结:我的问题是我总是会在这一行中得到 NullReferenceException :

for (MyModel i_model : the_list) {

我对多线程、异步操作和并发执行不熟悉。两周以来,我一直在互联网上阅读 Android 官方网站和其他网站上的许多不同文档,试图弄清楚......“AsyncTask 不好执行这种操作”...... .那么,我应该实现什么基础设施,我一直在想......我应该使用处理程序、线程、信使还是什么?我读得越多,就越感到困惑。就像我有一个 Analysis Paralysis问题...

我发现的大多数示例都提供了关于如何实现多线程和并发执行的过于冗长的代码示例;当我阅读它们并尝试弄清楚如何在我的代码中实现这些结构时,我只是陷入困境;而且,有这么多类(class)可供选择,我变得更加困惑......

由于 HTTP 调用需要异步执行(并且响应时间并不总是相同),我试图弄清楚如何编写抛出 NullReferenceException 的代码段在开始执行之前“等待”MyService 完成其工作; while 循环将不起作用,因为它会破坏主线程的生命周期。 “了解”服务是否完成其任务就像使用 boolean 方法 responseReceived 一样简单。最重要的想法是,每次通过 HTTP 获取新数据时,都会用它更新 RoomDatabase,同时 MainActivity 将显示当前的本地数据(如果有的话,如果还没有任何数据,则显示一个空列表)。

因此,当我得到它时,我将了解如何正确重构整个代码结构,以开始将更多 Service 子实例添加到我创建的 RemoteDataSource 类中其想法是让所有使用 OkHttp 执行 HTTP 通信的 Service 子级都包装在一个类中,以便更好地组织。

实现我正在寻找的目标的正确方法是什么?有人能够提供一些简短的示例来解释我需要这样的代码结构吗?包含诸如“此处准备好时要执行的代码”之类的注释的空 block 的示例会很棒,这样我就可以弄清楚。

这里暴露的问题与我发布的同一个项目有关 this other question几周前;从那时起,我一直在这里和那里阅读,进行一些反复试验并纠正一些代码问题;然而,我在这里提出一个不同的问题;找到这个问题的答案可能也是找到另一个问题答案的第一步。

URL 引用我一直在阅读的文档

我读过的一些文档(但不限于):

最佳答案

问题出在您的应用程序逻辑上,如下所示,

如果您使用AsyncTask,那显然是与主线程分开的线程。但是通过 HTTP 调用检索数据后同步到数据库是一个有顺序的过程(通过 HTTP 调用并检索 -> 然后持久化到数据库),它不能异步执行。所以当你打电话时,

List<MyModel> the_list = mService.getResponseAsObject();

此调用发生在特定线程中,并且程序流位于不同的线程中。由于这些是异步任务,因此它们异步工作。这意味着你永远不知道哪个会先执行,哪个是下一个。但按照你的逻辑,

if (the_list == null) {

这部分本质上需要初始化the_list才能运行。但问题是,此时服务线程还没有完成他的工作来执行你的下一个逻辑。所以它的明显断裂。

如果您可以重新设计它,以便等待 HTTP 请求完成,然后保存到数据库,那就更好了。因为假设如果您的 HTTP 请求首先完成,但它仍然返回您 null 或任何不需要的输出。所以在这种情况下你需要用你的逻辑来处理它。

好的,让我告诉您一个快速解决方法。让我们只使用一个线程而不是不同的线程。因此考虑更改以下行

private class PopulateDbAsync extends AsyncTask<Void, Void, Void> {

private class PopulateDbAsync

然后你会得到一个错误

@Override
protected Void doInBackground(final Void... params) {

因为我们不再扩展 AsyncTask 类。

因此,通过删除 @Override

对其进行如下更改
public Void doInBackground(final Void... params) {

这应该可以解决此处所述的问题。

关于java - 如何使在主线程中运行的某个类同时等待其他类达到某种特定状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52442384/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com