我制作一个简单的应用程序,以JSON格式从Web服务器检索数据集,将其解析为ArrayList中包含的对象。然后以列表视图的形式输出该数组列表。它开始时工作得非常好。然后,当我离开应用程序一段时间并回到它时,我得到一个ANR并且必须强制关闭。然而,只需离开应用程序并返回它就完全没问题了。 (请参阅下面的完整LogCat)我已在下面包含了所有相关代码(删除了我的网络服务器网址)。任何帮助解决这个问题都将不胜感激。
我认为它与应用程序正在恢复onResume()
或onRestart()
有关,所以我尝试用一些isEmpty()
和!null
检查覆盖这两种方法无济于事
TL; DR:应用由于NullPointerException而无法响应,可能是因为某些生命周期事件但无法找出原因。
另一个可能的混乱是我尝试单身人士课程。 (之前从未真正使用过基于静态的实例引用)
此LogCat是在进入主屏幕后打开应用程序失败,打开许多其他应用程序,然后返回到我的应用程序。只需离开并返回应用程序就不会发生这种情况。
05-14 17:21:45.140: W/dalvikvm(22985): threadid=1: thread exiting with uncaught exception (group=0x4103a2a0)
05-14 17:21:45.150: E/AndroidRuntime(22985): FATAL EXCEPTION: main
05-14 17:21:45.150: E/AndroidRuntime(22985): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.liamjpeters.retrievedata/com.liamjpeters.activities.ListActivity}: java.lang.NullPointerException
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2100)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2125)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.access$600(ActivityThread.java:140)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1227)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.os.Handler.dispatchMessage(Handler.java:99)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.os.Looper.loop(Looper.java:137)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.main(ActivityThread.java:4898)
05-14 17:21:45.150: E/AndroidRuntime(22985): at java.lang.reflect.Method.invokeNative(Native Method)
05-14 17:21:45.150: E/AndroidRuntime(22985): at java.lang.reflect.Method.invoke(Method.java:511)
05-14 17:21:45.150: E/AndroidRuntime(22985): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
05-14 17:21:45.150: E/AndroidRuntime(22985): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
05-14 17:21:45.150: E/AndroidRuntime(22985): at dalvik.system.NativeStart.main(Native Method)
05-14 17:21:45.150: E/AndroidRuntime(22985): Caused by: java.lang.NullPointerException
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:330)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.widget.ListView.setAdapter(ListView.java:466)
05-14 17:21:45.150: E/AndroidRuntime(22985): at com.liamjpeters.activities.ListActivity.onCreate(ListActivity.java:48)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.Activity.performCreate(Activity.java:5206)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1083)
05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2064)
05-14 17:21:45.150: E/AndroidRuntime(22985): ... 11 more
代码的要点(它意味着什么): 只是你的沼泽标准主要活动。有一个按钮,按下时执行从Web服务器检索数据的异步任务。完成后,它会启动ListActivity
活动。重要的是这里是我的Singletons实例的设置
package com.liamjpeters.activities;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import com.liamjpeters.content.Ticket;
import com.liamjpeters.content.Tickets;
import com.liamjpeters.getData.GetTickets;
import com.liamjpeters.retrievedata.R;
public class MainActivity extends Activity {
Button btn;
ProgressBar pb;
ArrayList<Ticket> tickets = new ArrayList<Ticket>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btnConnect);
pb = (ProgressBar) findViewById(R.id.pbconnect);
pb.setVisibility(View.INVISIBLE);
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
getServerData();
pb.setVisibility(View.VISIBLE);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void getServerData(){
GetTickets gsd = new GetTickets(this);
gsd.execute();
}
public void gotServerData(ArrayList<Ticket> tickets){
pb.setVisibility(View.INVISIBLE);
this.tickets = tickets;
Tickets.setInstance(tickets);
Intent intent = new Intent(this, ListActivity.class);
startActivity(intent);
}
}
代码的要点(它意味着什么): 从服务器获取数据并将其解析为名为
package com.liamjpeters.getData;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.AsyncTask;
import android.util.Log;
import com.liamjpeters.activities.MainActivity;
import com.liamjpeters.content.Ticket;
public class GetTickets extends AsyncTask<String, Void, String> {
MainActivity mAct;
ArrayList<Ticket> tickets = new ArrayList<Ticket>();
public GetTickets(Activity activ){
mAct = (MainActivity) activ;
}
protected void onPostExecute(String result) {
mAct.gotServerData(tickets);
}
protected String doInBackground(String... params) {
InputStream is = null;
String result = "";
// Connect to the server and obtain the data from the Server via the PHP Script.
// This script returns the MYSQL results encoded in JSON format
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("REMOVED BUT WORKS, TRUST ME");
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
// Converts the returned HTTPEntity into a string that can be parsed
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result = sb.toString();
} catch (Exception e) {
Log.e("log_tag", "Error converting result " + e.toString());
}
// Parses the JSON data
try {
JSONArray jArray = new JSONArray(result);
result = "";
for (int i = 0; i < jArray.length(); i++) {
JSONObject json_data = jArray.getJSONObject(i);
tickets.add(new Ticket(json_data.getInt("ticket_id"),json_data.getString("ticket_name"),json_data.getString("ticket_location"), json_data.getString("ticket_problem_area"),json_data.getString("ticket_description"),json_data.getInt("ticket_priority"),json_data.getInt("ticket_time")));
}
} catch (JSONException e) {
Log.e("log_tag", "Error parsing data " + e.toString());
}
return null;
}
}
代码的要点(它意味着什么): 只是一个带有一些便利方法的数据对象。
package com.liamjpeters.content;
public class Ticket {
private String name;
private String location;
private String problemArea;
private String description;
private int priority;
private int timeInt;
private int id;
private String timeString;
private boolean isNew = false;;
public Ticket(int id,String name, String location, String problemArea, String description, int priority, int time){
this.id = id;
this.name = name;
this.location = location;
this.problemArea = problemArea;
this.description = description;
this.priority = priority;
this.timeInt = time;
if (((System.currentTimeMillis() / 1000L)-time) <84600){
isNew = true;
}
timeString = new java.util.Date((long) time * 1000).toString();
}
public Ticket clone(){
return new Ticket(Integer.valueOf(id), new String(name), new String(location), new String(problemArea), new String(description), Integer.valueOf(priority), Integer.valueOf(timeInt));
}
public int getID(){
return id;
}
public String getName(){
return name;
}
public String getLocation(){
return location;
}
public String getProblemArea(){
return problemArea;
}
public String getDescription(){
return description;
}
public int getPriority(){
return priority;
}
public int getTimeInt(){
return timeInt;
}
public String getTimeString(){
return timeString;
}
public Boolean getIsNew(){
return isNew;
}
}
代码的要点(它的意图): 使用我的自定义列表项显示列表视图的活动。可能这个活动的生命周期是我的问题
package com.liamjpeters.activities;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.widget.ListView;
import com.liamjpeters.content.Ticket;
import com.liamjpeters.content.TicketListAdapter;
import com.liamjpeters.content.Tickets;
import com.liamjpeters.retrievedata.R;
public class ListActivity extends Activity {
ListView lstTest;
TicketListAdapter listAdapter;
ArrayList<Ticket> tickets;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tickets = Tickets.getInstance();
//Ony of my attempts at a fix :S
if (tickets == null || tickets.isEmpty()){
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
setContentView(R.layout.listlayout);
lstTest = (ListView)findViewById(R.id.lstText);
listAdapter = new TicketListAdapter(ListActivity.this,R.layout.listitemrel , tickets);
lstTest.setAdapter(listAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
有助于在活动之间传递我的ArrayList的单例类。它最初只是存储了它收到的引用。然后我有一个概念,即原始的ArrayList是在一个必须在某个时候处理掉的线程上创建的,因此引用将变为null。这没有解决问题
package com.liamjpeters.content;
import java.util.ArrayList;
public class Tickets {
private static ArrayList<Ticket> instance;
public static void setInstance(ArrayList<Ticket> instance){
Tickets.instance = new ArrayList<Ticket>(instance.size());
for (int i = 0; i<instance.size(); i++){
Tickets.instance.add(instance.get(i).clone());
}
}
public static ArrayList<Ticket> getInstance(){
return instance;
}
}
Bog Standard ArrayAdapter。这些在线百万个例子
package com.liamjpeters.content;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.liamjpeters.retrievedata.R;
public class TicketListAdapter extends ArrayAdapter<Ticket> {
int textViewResourceId;
public TicketListAdapter(Context context, int textViewResourceId, List<Ticket> objects) {
super(context, textViewResourceId, objects);
this.textViewResourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//The linear layout that the listview is contained within
LinearLayout ticketView;
//Get the current ticket object
Ticket ticket = getItem(position);
//Inflate the view
if(convertView==null){
ticketView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater vi;
vi = (LayoutInflater)getContext().getSystemService(inflater);
vi.inflate(textViewResourceId, ticketView, true);
}else{
ticketView = (LinearLayout) convertView;
}
//Get the text boxes from the list item XML file
TextView isNew = (TextView)ticketView.findViewById(R.id.tvIsNew);
TextView idText =(TextView)ticketView.findViewById(R.id.tvTicketID);
TextView nameText =(TextView)ticketView.findViewById(R.id.tvName);
TextView locationText =(TextView)ticketView.findViewById(R.id.tvLocation);
TextView problemText =(TextView)ticketView.findViewById(R.id.tvProblem);
TextView dateText =(TextView)ticketView.findViewById(R.id.tvDate);
ImageView priorityImage = (ImageView)ticketView.findViewById(R.id.ivPriority);
//Assign the appropriate data from our alert object above
if (ticket.getIsNew()){
isNew.setText("NEW!");
}
idText.setText(""+ticket.getID());
nameText.setText("Who: " + ticket.getName());
locationText.setText("Where: "+ ticket.getLocation());
problemText.setText("What: "+ticket.getProblemArea());
dateText.setText(ticket.getTimeString());
switch (ticket.getPriority()){
case 0:
priorityImage.setImageResource(R.drawable.low);
break;
case 1:
priorityImage.setImageResource(R.drawable.med);
break;
case 2:
priorityImage.setImageResource(R.drawable.high);
break;
default:
priorityImage.setImageResource(R.drawable.low);
break;
}
return ticketView;
}
}
所以你有。任何帮助,无论大小,都值得赞赏。看一些可以做得更好的代码然后请告诉我。我不仅要解决我的问题,还要尽可能多地学习。
谢谢:)
答案 0 :(得分:1)
tickets = Tickets.getInstance();
是你的罪犯。
您不应将数据存储在单例/静态变量中。当过程被杀死时,它们会被擦除。确保在内存中没有数据时保留数据并读取数据。在onSaveInstanceState
启动时,也可以使用Intent
和/或通过Activity
发送数据。
答案 1 :(得分:1)
问题的根源在于,经过一段时间后,Android框架会终止您的申请流程。重新启动时,所有静态数据(包括单个Tickets
对象)都将设置为默认值。您需要检测这种情况并将事物重新初始化为您需要运行的状态。
这样做的一个结果是,在ListActivity
中,当您发现Tickets.getInstance()
返回null
时,您应该在调用finish()
* 之后从MainActivity
开始。否则,它会继续尝试初始化列表活动。
* 并立即返回,正如David Wasser在评论中指出的那样。