Currently, I'm working on an app that shows contents of an JSON-based API in a listview. Fetching the data and using the adapter to fill the listview works fine the first time. But when I try to refresh the contents using Swipe to refresh, the first time I try to refresh I get an IllegalStateException Error:
W/System.err: java.lang.IllegalStateException: JsonReader is closed
W/System.err: at android.util.JsonReader.peek(JsonReader.java:361)
W/System.err: at android.util.JsonReader.expect(JsonReader.java:308)
W/System.err: at android.util.JsonReader.beginArray(JsonReader.java:277)
W/System.err: at com.cologne_international.cologneinternationalapp.notamActivity.readNotamArray(notamActivity.java:71)
W/System.err: at com.cologne_international.cologneinternationalapp.notamActivity.readJsonStream(notamActivity.java:63)
W/System.err: at com.cologne_international.cologneinternationalapp.notamActivity.updateNOTAMS(notamActivity.java:122)
W/System.err: at com.cologne_international.cologneinternationalapp.notamActivity$1.onRefresh(notamActivity.java:48)
W/System.err: at android.support.v4.widget.SwipeRefreshLayout$1.onAnimationEnd(SwipeRefreshLayout.java:187)
W/System.err: at android.support.v4.widget.CircleImageView.onAnimationEnd(CircleImageView.java:106)
W/System.err: at android.view.ViewGroup.finishAnimatingView(ViewGroup.java:6237)
W/System.err: at android.view.View.draw(View.java:17129)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.draw(View.java:17188)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16167)
W/System.err: at android.view.View.draw(View.java:16951)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16162)
W/System.err: at android.view.View.draw(View.java:16951)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16162)
W/System.err: at android.view.View.draw(View.java:16951)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16162)
W/System.err: at android.view.View.draw(View.java:16951)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16162)
W/System.err: at android.view.View.draw(View.java:16951)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16162)
W/System.err: at android.view.View.draw(View.java:16951)
W/System.err: at android.view.ViewGroup.drawChild(ViewGroup.java:3727)
W/System.err: at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3513)
W/System.err: at android.view.View.draw(View.java:17188)
W/System.err: at com.android.internal.policy.DecorView.draw(DecorView.java:753)
W/System.err: at android.view.View.updateDisplayListIfDirty(View.java:16167)
W/System.err: at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:648)
W/System.err: at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:654)
W/System.err: at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:762)
W/System.err: at android.view.ViewRootImpl.draw(ViewRootImpl.java:2800)
W/System.err: at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2608)
W/System.err: at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2215)
W/System.err: at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
W/System.err: at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6337)
W/System.err: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
W/System.err: at android.view.Choreographer.doCallbacks(Choreographer.java:686)
W/System.err: at android.view.Choreographer.doFrame(Choreographer.java:621)
W/System.err: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
W/System.err: at android.os.Handler.handleCallback(Handler.java:751)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err: at android.os.Looper.loop(Looper.java:154)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6119)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
However, when I try to reload for a second time, everything is working as it should.
notamActivity Class:
public class notamActivity extends AppCompatActivity {
private static ListView lv;
private static AdapterNotam adbNotam;
private static SwipeRefreshLayout swipeContainer;
private static JsonReader reader;
private static Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notam);
context = getApplicationContext();
lv = (ListView) findViewById(R.id.list);
try {
adbNotam = new AdapterNotam(this, readJsonStream());
} catch (Exception e) {
e.printStackTrace();
}
lv.setAdapter(adbNotam);
swipeContainer = (SwipeRefreshLayout) findViewById(R.id.swiperefresh);
swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Log.i("Called onRefresh", "onRefresh called from SwipeRefreshLayout");
notamActivity.updateNOTAMS();
}
});
}
public static List<Notam> readJsonStream() throws IOException {
try {
return readNotamArray(reader);
} finally {
reader.close();
}
}
public static List<Notam> readNotamArray(JsonReader reader) throws IOException {
List<Notam> notams = new ArrayList<Notam>();
reader.beginArray();
while (reader.hasNext()) {
notams.add(readNotam(reader));
}
reader.endArray();
return notams;
}
public static Notam readNotam(JsonReader reader) throws IOException {
int id = 0;
String datestart = null;
String dateend = null;
String text = null;
reader.beginObject();
while (reader.hasNext()) {
String jname = reader.nextName();
if (jname.equals("id")) {
id = Integer.parseInt(reader.nextString());
} else if (jname.equals("datestart")) {
datestart = reader.nextString();
} else if (jname.equals("dateend")) {
dateend = reader.nextString();
} else if (jname.equals("textde")) {
text = reader.nextString();
} else {
reader.skipValue();
}
}
reader.endObject();
return new Notam(id, text, datestart, dateend);
}
public static void setReader(JsonReader pReader){
reader = pReader;
}
public static Context getContext(){
return context;
}
public static void updateNOTAMS(){
new downloadNOTAMTask().execute("http://cologne-international.com/notam/json.php");
try {
adbNotam = new AdapterNotam(getContext(), readJsonStream());
} catch (Exception e) {
e.printStackTrace();
}
lv.setAdapter(adbNotam);
swipeContainer.setRefreshing(false);
}
}
downloadNOTAMTask Class:
class downloadNOTAMTask extends AsyncTask<String,Void,JsonReader>{
protected JsonReader doInBackground(String... url){
JsonReader reader = null;
try {
InputStream ls = new URL(url[0]).openStream();
reader = new JsonReader(new InputStreamReader(ls, "UTF-8"));
}catch(Exception e){
e.printStackTrace();
}
return reader;
}
protected void onPostExecute(JsonReader result){
notamActivity.setReader(result);
}
}
Layout xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_event"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.cologne_international.cologneinternationalapp.eventActivity">
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swiperefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bt_back">
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="match_parent"
/>
</android.support.v4.widget.SwipeRefreshLayout>
<Button
android:text="@string/button_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:id="@+id/bt_back"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:onClick="back"
style="@android:style/Widget.Holo.Button" />
</RelativeLayout>
There is a problem with the activity that you have published in the question:-
However, when I try to reload for a second time, everything is working as it should.
It is happening because you are not doing things in synchronous manner. (ie. You are not waiting for AsynTask to get finished)
Below I have given you the reference to write more cleaner and less error prone code:-
notamActivity Class:
public class notamActivity extends AppCompatActivity {
private ListView lv;
private AdapterNotam adbNotam;
private SwipeRefreshLayout swipeContainer;
private Context context;
private DownloadNOTAMTask downloadNOTAMTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notam);
context = getApplicationContext();
lv = (ListView) findViewById(R.id.list);
//to initially get the list of Notams
updateNOTAMS();
swipeContainer = (SwipeRefreshLayout) findViewById(R.id.swiperefresh);
swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Log.i("Called onRefresh", "onRefresh called from SwipeRefreshLayout");
updateNOTAMS();
}
});
}
public void cancelTask(DownloadNOTAMTask downloadNOTAMTask) {
if (downloadNOTAMTask != null && downloadNOTAMTask.getStatus() == AsyncTask.Status.RUNNING)
downloadNOTAMTask.cancel(true);
}
//you should create a reference to cancel the task when the activity is closing
public void updateNOTAMS() {
cancelTask(downloadNOTAMTask);
downloadNOTAMTask = new DownloadNOTAMTask(new GenericCallBack() {
@Override
public void success(List<Notam> notamList) {
//you don't need to setup adapter again and again
//just use setDataSetChanged
adbNotam = new AdapterNotam(notamActivity.this, notamList);
lv.setAdapter(adbNotam);
swipeContainer.setRefreshing(false);
}
@Override
public void failure() {
swipeContainer.setRefreshing(false);
}
}).execute("http://cologne-international.com/notam/json.php");
}
//interface help you to get callback from the downloadNOTAMTask
interface GenericCallBack {
void success(List<Notam> notamList);
void failure();
}
}
DownloadNOTAMTask Class:
public class DownloadNOTAMTask extends AsyncTask<String, Void, List<Notam>> {
private final notamActivity.GenericCallBack callBack;
public DownloadNOTAMTask(notamActivity.GenericCallBack genericInterface) {
this.callBack = genericInterface;
}
protected List<Notam> doInBackground(String... url) {
JsonReader reader = null;
List<Notam> notamList = null;
try {
InputStream ls = new URL(url[0]).openStream();
reader = new JsonReader(new InputStreamReader(ls, "UTF-8"));
notamList = readNotamArray(reader);
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return notamList;
}
@Override
protected void onPostExecute(List<Notam> notams) {
if (callBack != null) {
if (notams != null)
callBack.success(notams);
else
callBack.failure();
}
}
private List<Notam> readNotamArray(JsonReader reader) throws IOException {
List<Notam> notams = new ArrayList<>();
reader.beginArray();
while (reader.hasNext()) {
notams.add(readNotam(reader));
}
reader.endArray();
return notams;
}
private Notam readNotam(JsonReader reader) throws IOException {
int id = 0;
String datestart = null;
String dateend = null;
String text = null;
reader.beginObject();
while (reader.hasNext()) {
String jname = reader.nextName();
switch (jname) {
case "id":
id = Integer.parseInt(reader.nextString());
break;
case "datestart":
datestart = reader.nextString();
break;
case "dateend":
dateend = reader.nextString();
break;
case "textde":
text = reader.nextString();
break;
default:
reader.skipValue();
break;
}
}
reader.endObject();
return new Notam(id, text, datestart, dateend);
}
}
Layout xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_event"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.cologne_international.cologneinternationalapp.eventActivity">
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swiperefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bt_back">
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="match_parent"
/>
</android.support.v4.widget.SwipeRefreshLayout>
<Button
android:text="@string/button_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:id="@+id/bt_back"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:onClick="back"
style="@android:style/Widget.Holo.Button" />
</RelativeLayout>