1

問題

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;  
    }
}

それで、あなたは持っています。大小を問わず、どんな助けでも大歓迎です。もっとうまくできるコードをいくつか見て、私に知らせてください。問題を解決するだけでなく、できるだけ多くのことを学びたいと思っています。

ありがとう :)

4

2 に答える 2

1

tickets = Tickets.getInstance();あなたの犯罪者です。

シングルトン/静的変数にデータを保存しないでください。プロセスが強制終了されると、それらは消去されます。データを保持し、メモリ内で使用できない場合は読み取るようにしてください。また、起動時にonSaveInstanceState経由でデータを使用および/または送信します。IntentActivity

于 2013-05-14T17:24:39.760 に答える
1

問題の原因は、しばらくすると Android フレームワークがアプリケーション プロセスを強制終了することです。再起動すると、すべての静的データ (シングルトンTicketsオブジェクトを含む) がデフォルト値に設定されます。この状況を検出し、機能するために必要な状態に再初期化する必要があります。

これの結果の 1 つは、 がを返すListActivityことを発見したときに、 を開始した後に*を呼び出す必要があるということです。それ以外の場合は、引き続きリスト アクティビティの初期化を試みます。Tickets.getInstance()nullfinish()MainActivity

* David Wasser がコメントで指摘しているように、すぐに戻ります。

于 2013-05-14T17:24:40.253 に答える