【发布时间】:2019-02-25 18:49:26
【问题描述】:
我很难理解如何创建一些在主线程中声明和实例化的 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;
}
}
此服务有效;美好的。然后,从另一个类(其目的是处理将接收到的数据插入本地 Room 数据库的持久性),我尝试执行以下操作(这就是我大吃一惊的地方):
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 问题...
我在那里找到的大多数示例都提供了关于如何实现多线程和并发执行的过于冗长的代码示例;当我阅读它们并试图弄清楚如何在我的代码中实现这些结构时,我就卡住了;而且,有这么多的课程可供选择,我更加困惑......
由于需要异步执行 HTTP 调用(并且响应时间并不总是相同),我试图弄清楚如何制作为 MyService 抛出 NullReferenceException“等待”的代码在开始执行之前完成它的工作; while 循环将无法工作,因为它会破坏主线程的生命周期。 “知道”服务是否完成了它的任务就像使用布尔方法responseReceived 一样简单。重要的想法是,每次通过 HTTP 获取新数据时,都会用它更新 RoomDatabase,同时 MainActivity 会显示当前的本地数据(如果有的话,或者如果还没有的话,则显示一个空列表)。
所以,当我得到它时,我将了解如何正确重构整个代码结构以开始将更多 Service 子实例添加到我的 RemoteDataSource 类中,我创建该类的想法是让所有服务子代使用 OkHttp 执行 HTTP 通信,将它们封装在一个类中以便更好地组织。
什么是实现我正在寻找的正确方法?有人能提供一些简短的例子来解释我需要这样的代码结构吗?包含 cmets 的空块的示例,例如“在此处准备好时执行的代码”会很棒,所以我可以弄清楚。
这里暴露的问题与几周前让我发布this other question 的同一个项目有关;从那时起,我一直在这里和那里阅读,进行一些试验和错误并在这里和那里纠正一些代码问题;但是,我在这里提出了一个不同的问题;找到这个问题的答案可能也是找出另一个问题的答案的第一步。
对我一直在阅读的文档的 URL 引用
我一直在阅读的一些文档(但不限于):
【问题讨论】:
-
当我有时间的时候,我会尝试编译我一直在阅读的不同网站以了解如何处理这个问题,并将它们的链接发布在问题更新中作为参考。
-
据我了解,您真正尝试的是使用异步线程实现同步工作流。这是基本问题。是吗?如果我错了,请纠正我
-
也许我的代码实现比我想象的更错误;我使用的一些代码块在阅读了它们之后是复制粘贴适应的,所以我尝试实现它们以查看它们是否按预期工作;正如我之前所说,我对 AsyncTasks、多线程和其他东西感到非常困惑。我将查看浏览器的历史记录,以编译我阅读的网站、标记和其他用于保存 URL 引用的方法;当我拥有它时,我会在问题更新中提供它们,为问题提供更多背景信息。
标签: java android multithreading okhttp android-room