問題
JSON形式でWebサーバーからデータセットを取得し、ArrayListに含まれるオブジェクトに解析する単純なアプリを作成しています。この配列リストは、リスト ビューの形式で出力されます。そもそも美しく機能します。その後、しばらくアプリを離れて戻ってくると、ANR が発生し、強制終了する必要があります。ただし、アプリを離れてアプリに戻るだけでも問題ありません。(以下の完全な LogCat を参照してください) 以下に、関連するすべてのコード (Web サーバーの URL を削除したもの) を含めました。この問題を解決するための助けをいただければ幸いです。
アプリの再開と関係があると思います。または、これらのメソッドの両方をいくつかのメソッドとチェックでオーバーライドして無駄にしようとしたのonResume()
かもしれません。onRestart()
isEmpty()
!null
TL;DR: NullPointerException が原因でアプリが応答しません。ライフサイクル イベントが原因である可能性がありますが、理由がわかりません。
別の可能性のある混乱は、シングルトン クラスでの試みです。(これまで静的ベースのインスタンス参照を実際に使用したことはありません)
再起動に失敗したアプリの LogCat 出力
この 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
アクティビティが開始されます。重要なのは、ここにシングルトン インスタンスが設定されている場所です
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);
}
}
GetTickets.java
コードの要点 (目的):サーバーからデータを取得し、チケットと呼ばれる ArrayList 内のオブジェクトに解析します。これは魅力のように機能します。
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;
}
}
チケット.java
コードの要点 (目的):いくつかの便利なメソッドを備えた単なるデータ オブジェクトです。
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;
}
}
ListActivity.java
コードの要点 (目的):カスタム リスト アイテムを使用してリスト ビューを表示するアクティビティです。私の問題である可能性が高いこのアクティビティのライフサイクル
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;
}
}
Tickets.java
アクティビティ間で 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 標準 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;
}
}
それで、あなたは持っています。大小を問わず、どんな助けでも大歓迎です。もっとうまくできるコードをいくつか見て、私に知らせてください。問題を解決するだけでなく、できるだけ多くのことを学びたいと思っています。
ありがとう :)