1

이 예외가 나를 견디고 있습니다. 내 주요 활동 프로세스가 종료되고 내 확장 가능 목록 뷰의 널 포인터 예외로 인해 앱이 강제 종료 될 때까지. 이것은 내 첫 번째 안드로이드 애플 리케이션과 자바를 쓰는 처음이다 그래서 약간의 n00b입니다. 그것은 시간표 애플 리케이션이며 그것은 컨텐츠 제공자로부터 목록에 데이터를로드하기 위해 커서 로더를 사용하고 있습니다. 여기서 내가 뭘 잘못하고 있니? 미리 감사드립니다.ExpandableListAdapter getChildrenCount 활동이 복원되었을 때 null 포인터 예외가 발생했습니다.

로그 캣 :

12-03 00:21:42.235: E/AndroidRuntime(2341): FATAL EXCEPTION: main 
12-03 00:21:42.235: E/AndroidRuntime(2341): Process: com.crevitus.timetable, PID: 2341 
12-03 00:21:42.235: E/AndroidRuntime(2341): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.crevitus.timetable/com.crevitus.timetable.MainActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.ActivityThread.access$800(ActivityThread.java:144) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.os.Handler.dispatchMessage(Handler.java:102) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.os.Looper.loop(Looper.java:135) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.ActivityThread.main(ActivityThread.java:5221) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at java.lang.reflect.Method.invoke(Native Method) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at java.lang.reflect.Method.invoke(Method.java:372) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 
12-03 00:21:42.235: E/AndroidRuntime(2341): Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at com.crevitus.timetable.adapter.ExpandableListAdapter.getChildrenCount(ExpandableListAdapter.java:67) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.widget.ExpandableListConnector.refreshExpGroupMetadataList(ExpandableListConnector.java:563) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.widget.ExpandableListConnector.setExpandedGroupMetadataList(ExpandableListConnector.java:758) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.widget.ExpandableListView.onRestoreInstanceState(ExpandableListView.java:1340) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.view.View.dispatchRestoreInstanceState(View.java:13621) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.view.ViewGroup.dispatchThawSelfOnly(ViewGroup.java:2907) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.widget.AdapterView.dispatchRestoreInstanceState(AdapterView.java:795) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2893) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2893) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.view.View.restoreHierarchyState(View.java:13599) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:1980) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.Activity.onRestoreInstanceState(Activity.java:1022) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.Activity.performRestoreInstanceState(Activity.java:977) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1161) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2271) 
12-03 00:21:42.235: E/AndroidRuntime(2341):  ... 10 more 

MainActivity 번호 :

package com.crevitus.timetable; 

import java.util.ArrayList; 
import java.util.Calendar; 
import java.util.HashMap; 
import java.util.List; 

import android.app.AlertDialog; 
import android.app.LoaderManager; 
import android.content.ContentResolver; 
import android.content.CursorLoader; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.content.Loader; 
import android.database.Cursor; 
import android.os.Bundle; 
import android.preference.PreferenceManager; 
import android.view.View; 
import android.widget.AdapterView; 
import android.widget.AdapterView.OnItemLongClickListener; 
import android.widget.ExpandableListView; 
import android.widget.ExpandableListView.OnChildClickListener; 
import android.widget.ExpandableListView.OnGroupExpandListener; 
import android.widget.Toast; 

import com.crevitus.timetable.adapter.ExpandableListAdapter; 
import com.crevitus.timetable.provider.TimetableContentProvider; 
import com.crevitus.timetable.service.ReminderHandler; 

public class MainActivity extends BaseActivity implements 
    LoaderManager.LoaderCallbacks<Cursor> { 

    private ExpandableListAdapter listAdapter; 
    private ExpandableListView expListView; 
    private List<String> listDataHeader; 
    private HashMap<String, List<List<String>>> listDataChild; 
    private int lastExpandedPosition = -1; 
    private int gPosition, cPosition; 
    private static final int MONDAY = 0, TUESDAY = 1, WEDNESDAY = 2, THURSDAY = 3, FRIDAY = 4; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     PreferenceManager.setDefaultValues(this, R.xml.preferences, false); 

     //get data from content provider 
     getLoaderManager().initLoader(MONDAY, null, this); 
     getLoaderManager().initLoader(TUESDAY, null, this); 
     getLoaderManager().initLoader(WEDNESDAY, null, this); 
     getLoaderManager().initLoader(THURSDAY, null, this); 
     getLoaderManager().initLoader(FRIDAY, null, this); 

     // get the listview 
     expListView = (ExpandableListView) findViewById(R.id.lvExp); 
     expListView.setLongClickable(true); 

     //initialise data collections 
     listDataHeader = new ArrayList<String>(); 
     listDataChild = new HashMap<String, List<List<String>>>(); 

     // Add header data 
     listDataHeader.add("Monday"); 
     listDataHeader.add("Tuesday"); 
     listDataHeader.add("Wednesday"); 
     listDataHeader.add("Thursday"); 
     listDataHeader.add("Friday"); 

     //get adapter 
     listAdapter = new ExpandableListAdapter(this, listDataHeader, listDataChild); 

     // set list adapter 
     expListView.setAdapter(listAdapter); 

     // Listview on child click listener 
     expListView.setOnChildClickListener(new OnChildClickListener() { 

      @Override 
      public boolean onChildClick(ExpandableListView parent, View v, 
        int groupPosition, int childPosition, long id) { 
       //get data by position 
       String group = (String) listAdapter.getGroup(groupPosition); 
       List<String> childList = listDataChild.get(group).get(childPosition); 

       //launch view activity and pass data 
       Intent view = new Intent(getApplicationContext(), ViewActivity.class); 
       Bundle bundle = new Bundle(); 
       bundle.putStringArray("Class", new String[] {childList.get(0), childList.get(1), childList.get(2), childList.get(3), childList.get(4), childList.get(5)}); 
       view.putExtras(bundle); 
       startActivity(view); 
       return false; 
      } 
     }); 

     expListView.setOnItemLongClickListener(new OnItemLongClickListener() { 

      @Override 
      public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { 
       if (ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_CHILD) 
       { 
        int groupPosition = ExpandableListView.getPackedPositionGroup(id); 
        int childPosition = ExpandableListView.getPackedPositionChild(id); 
        longClick(expListView, groupPosition, childPosition); 

        return true; 
       } 
       return false; 
      } 

      private void longClick(ExpandableListView expListView, int groupPosition, int childPosition) { 
       gPosition = groupPosition; 
       cPosition = childPosition; 
       AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this); 
       // set title 
       alertDialogBuilder.setTitle("Delete Class?"); 

       // set dialog message 
       alertDialogBuilder 
        .setMessage("Are you sure you want to delete the class?") 
        .setCancelable(false) 
        .setPositiveButton("Yes",new DialogInterface.OnClickListener() { 
         public void onClick(DialogInterface dialog,int id) { 
          String group = (String) listAdapter.getGroup(gPosition); 
          List<String> childList = listDataChild.get(group).get(cPosition); 
          ContentResolver cr = getContentResolver(); 
          cr.delete(TimetableContentProvider.CONTENT_URI, TimetableContentProvider.KEY_ID + " = ?", new String[] {childList.get(0)}); 
          listAdapter.deleteChild(gPosition, cPosition); 
          listAdapter.notifyDataSetChanged(); 
          ReminderHandler.cancelAlarm(Integer.parseInt(childList.get(0)), getApplicationContext()); 
          Toast.makeText(getApplicationContext(), "Class Deleted", Toast.LENGTH_SHORT).show(); 
         } 
         }) 
        .setNegativeButton("No",new DialogInterface.OnClickListener() { 
         public void onClick(DialogInterface dialog,int id) { 
          // if this button is clicked, just close 
          // the dialog box and do nothing 
          dialog.cancel(); 
         } 
        }); 

        // create alert dialog 
        AlertDialog alertDialog = alertDialogBuilder.create(); 

        // show it 
        alertDialog.show(); 
        } 
       });//end longclick listener 


     expListView.setOnGroupExpandListener(new OnGroupExpandListener() { 
      @Override 
      public void onGroupExpand(int groupPosition) { 
        if (lastExpandedPosition != -1 && groupPosition != lastExpandedPosition) 
        { 
         expListView.collapseGroup(lastExpandedPosition); 
        } 
        lastExpandedPosition = groupPosition; 
      } 
     }); 

    } 
    @Override 
    public void onResume() 
    { 
     super.onResume(); 
     getLoaderManager().restartLoader(MONDAY, null, this); 
     getLoaderManager().restartLoader(TUESDAY, null, this); 
     getLoaderManager().restartLoader(WEDNESDAY, null, this); 
     getLoaderManager().restartLoader(THURSDAY, null, this); 
     getLoaderManager().restartLoader(FRIDAY, null, this); 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     CursorLoader loader = null; 
     switch(id) 
     { 
      case MONDAY: 
       loader = new CursorLoader(this, 
         TimetableContentProvider.CONTENT_URI, 
         null, 
         TimetableContentProvider.KEY_DAY_COLUMN + "=?", new String[] {"Monday"}, 
         null); 
       break; 
      case TUESDAY: 
       loader = new CursorLoader(this, 
         TimetableContentProvider.CONTENT_URI, 
         null, 
         TimetableContentProvider.KEY_DAY_COLUMN + "=?", new String[] {"Tuesday"}, 
         null);    
       break; 
      case WEDNESDAY: 
       loader = new CursorLoader(this, 
         TimetableContentProvider.CONTENT_URI, 
         null, 
         TimetableContentProvider.KEY_DAY_COLUMN + "=?", new String[] {"Wednesday"}, 
         null);    
       break; 
      case THURSDAY: 
       loader = new CursorLoader(this, 
         TimetableContentProvider.CONTENT_URI, 
         null, 
         TimetableContentProvider.KEY_DAY_COLUMN + "=?", new String[] {"Thursday"}, 
         null);    
       break; 
      case FRIDAY: 
       loader = new CursorLoader(this, 
         TimetableContentProvider.CONTENT_URI, 
         null, 
         TimetableContentProvider.KEY_DAY_COLUMN + "=?", new String[] {"Friday"}, 
         null);    
       break; 
     } 
     return loader; 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 

     List<String> splitMon; 
     List<String> splitTues; 
     List<String> splitWed; 
     List<String> splitThurs; 
     List<String> splitFri; 

     List<List<String>> monday = new ArrayList<List<String>>(); 
     List<List<String>> tuesday = new ArrayList<List<String>>(); 
     List<List<String>> wednesday = new ArrayList<List<String>>(); 
     List<List<String>> thursday = new ArrayList<List<String>>(); 
     List<List<String>> friday = new ArrayList<List<String>>(); 

     switch(loader.getId()) 
     { 
      case MONDAY: 
       while(cursor.moveToNext()) 
       { 
        splitMon = new ArrayList<String>(); 
        splitMon.add(cursor.getString(0)); 
        splitMon.add(cursor.getString(1)); 
        splitMon.add(cursor.getString(2)); 
        splitMon.add(cursor.getString(3)); 
        splitMon.add(cursor.getString(4)); 
        splitMon.add(cursor.getString(5)); 
        monday.add(splitMon); 
       } 
       listDataChild.put(listDataHeader.get(0), monday); 
       break; 
      case TUESDAY: 
       while(cursor.moveToNext()) 
       { 
        splitTues = new ArrayList<String>(); 
        splitTues.add(cursor.getString(0)); 
        splitTues.add(cursor.getString(1)); 
        splitTues.add(cursor.getString(2)); 
        splitTues.add(cursor.getString(3)); 
        splitTues.add(cursor.getString(4)); 
        splitTues.add(cursor.getString(5)); 
        tuesday.add(splitTues); 
       } 
       listDataChild.put(listDataHeader.get(1), tuesday); 
       break; 
      case WEDNESDAY: 
       while(cursor.moveToNext()) 
        { 
         splitWed = new ArrayList<String>(); 
         splitWed.add(cursor.getString(0)); 
         splitWed.add(cursor.getString(1)); 
         splitWed.add(cursor.getString(2)); 
         splitWed.add(cursor.getString(3)); 
         splitWed.add(cursor.getString(4)); 
         splitWed.add(cursor.getString(5)); 
         wednesday.add(splitWed); 
        } 
       listDataChild.put(listDataHeader.get(2), wednesday); 
       break; 
      case THURSDAY: 
       while(cursor.moveToNext()) 
       { 
        splitThurs = new ArrayList<String>(); 
        splitThurs.add(cursor.getString(0)); 
        splitThurs.add(cursor.getString(1)); 
        splitThurs.add(cursor.getString(2)); 
        splitThurs.add(cursor.getString(3)); 
        splitThurs.add(cursor.getString(4)); 
        splitThurs.add(cursor.getString(5)); 
        thursday.add(splitThurs); 
       } 
       listDataChild.put(listDataHeader.get(3), thursday); 
       break; 
      case FRIDAY: 
       while(cursor.moveToNext()) 
        { 
         splitFri = new ArrayList<String>(); 
         splitFri.add(cursor.getString(0)); 
         splitFri.add(cursor.getString(1)); 
         splitFri.add(cursor.getString(2)); 
         splitFri.add(cursor.getString(3)); 
         splitFri.add(cursor.getString(4)); 
         splitFri.add(cursor.getString(5)); 
         friday.add(splitFri); 

        } 
        listDataChild.put(listDataHeader.get(4), friday); 
       break; 
     } 
     listAdapter.notifyDataSetChanged(); 
     expListView.post(new Runnable() { 
      @Override 
      public void run() { 
       switch(Calendar.getInstance().get(Calendar.DAY_OF_WEEK)){ 
       case Calendar.MONDAY: 
        expListView.expandGroup(0); 
        break; 
       case Calendar.TUESDAY: 
        expListView.expandGroup(1); 
        break; 
       case Calendar.WEDNESDAY: 
        expListView.expandGroup(2); 
        break; 
       case Calendar.THURSDAY: 
        expListView.expandGroup(3); 
        break; 
       case Calendar.FRIDAY: 
        expListView.expandGroup(4); 
        break; 
       case Calendar.SATURDAY: 
        expListView.expandGroup(0); 
        break; 
       case Calendar.SUNDAY: 
        expListView.expandGroup(0); 
        break; 
       } 
      } 
      }); 


    } 
    @Override 
    public void onLoaderReset(Loader<Cursor> arg0) { 
     // TODO Auto-generated method stub 

    } 
} 

ExpandableListAdapter : 그래서

package com.crevitus.timetable.adapter; 

import java.util.HashMap; 
import java.util.List; 

import android.content.Context; 
import android.graphics.Typeface; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.BaseExpandableListAdapter; 
import android.widget.TextView; 

import com.crevitus.timetable.R; 

public class ExpandableListAdapter extends BaseExpandableListAdapter { 

    private Context _context; 
    private List<String> listGroupData; 
    private HashMap<String, List<List<String>>> listItemData; 

    public ExpandableListAdapter(Context context, List<String> listGroupData, 
      HashMap<String, List<List<String>>> listDataChild) { 
     this._context = context; 
     this.listGroupData = listGroupData; 
     this.listItemData = listDataChild; 
    } 

    @Override 
    public Object getChild(int groupPosition, int childPosition) { 
     return listItemData 
       .get(listGroupData 
         .get(groupPosition)) 
         .get(childPosition); 
    } 

    @Override 
    public long getChildId(int groupPosition, int childPosition) { 
     return childPosition; 
    } 

    @Override 
    public View getChildView(int groupPosition, final int childPosition, 
      boolean isLastChild, View convertView, ViewGroup parent) { 

     List<String> childText = (List<String>) getChild(groupPosition, childPosition); 

     if (convertView == null) { 
      LayoutInflater infalInflater = (LayoutInflater) this._context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      convertView = infalInflater.inflate(R.layout.exp_list_item, null); 
     } 

     TextView sTime = (TextView) convertView.findViewById(R.id.sTime); 
     TextView modName = (TextView) convertView.findViewById(R.id.modName); 

     if(childText != null) 
     { 
      modName.setText(childText.get(1) + " - Room: " + childText.get(2)); 
      sTime.setText(childText.get(4)); 
     } 
     return convertView; 
    } 

    @Override 
    public int getChildrenCount(int groupPosition) { 
     return this.listItemData.get(this.listGroupData.get(groupPosition)) 
       .size(); 
    } 

    @Override 
    public Object getGroup(int groupPosition) { 
     return this.listGroupData.get(groupPosition); 
    } 

    @Override 
    public int getGroupCount() { 
     return this.listGroupData.size(); 
    } 

    @Override 
    public long getGroupId(int groupPosition) { 
     return groupPosition; 
    } 

    @Override 
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { 
     String headerTitle = (String) getGroup(groupPosition); 
     if (convertView == null) { 
      LayoutInflater infalInflater = (LayoutInflater) this._context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      convertView = infalInflater.inflate(R.layout.list_group, null); 
     } 

     TextView lblListHeader = (TextView) convertView.findViewById(R.id.listGroup); 
     lblListHeader.setTypeface(null, Typeface.BOLD); 
     lblListHeader.setText(headerTitle); 

     return convertView; 
    } 

    @Override 
    public boolean hasStableIds() { 
     return true; 
    } 

    @Override 
    public boolean isChildSelectable(int groupPosition, int childPosition) { 
     return true; 
    } 

    public void deleteChild(int groupPosition, int childPosition) 
    { 
     listItemData.get(listGroupData.get(groupPosition)).remove(childPosition); 
    } 
} 

답변

1

아니에요 너무 식구들 LoaderManager에 익숙하지만 로더를 다시 시작하는 onResume() 메서드는 비웃음이라고 말할 수 있습니다. 네가 완전히 제거 할 수 있다고 생각하고있어. 나는이 SO answer을 읽고, initLoader와 restartLoader의 차이점과 Activity가 다시 생성 될 때 그것들을 사용할시기를 설명 할 것을 강력하게 제안한다. 거기에서 작은 발췌 :

활동/조각의 수명주기는을 위해 ...이 결정은 전적으로 은 "필요"를 기반으로 만든 하나를 사용하기로 결정 또는 다른 방법을 함께 할 수 없다 새 로더. initLoader를 사용하는 동일한 쿼리를 실행하려면 다른 쿼리를 실행하려면 restartLoader를 사용합니다. 우리는 항상 restartLoader를 사용할 수 있지만 비효율적입니다. 화면을 회전하거나 사용자가 앱에서 다른 곳으로 이동하여 이 나중에 동일한 활동으로 돌아 오면 보통 쿼리 결과가 표시되므로 restartLoader가 불필요하게 로더를 다시 만들고 잠재적으로 기본 비싼) 쿼리 결과.

타이밍 문제와 같이 실제로 충돌하는 이유가 여기에 추가 될 수 있습니다. onCreate()에 채워지는 그룹 수는 listGroupData입니다. 그러나 커서가 완료 될 때까지로드되지 않은 listItemData에서 자녀 수를 검색합니다. 즉, onCreate()와 onLoadFinished() 사이에는 데이터가 포함되지 않지만 어댑터는 사용 가능한 항목이 있다고 생각하므로 NPE로 중단됩니다.

어댑터를 작성할 때 listGroupDatalistItemData을 반영한다는 것이 절대적으로 중요합니다. 하나를 수정하면 다른 하나를 수정해야합니다. 또한 맵의 List 값이 null이 아닌 것을 보장하면 장소 전체에서 Null 검사를 수행 할 필요가 없습니다. 예,에서 onCreate에서 대신이 작업을 수행 :이 listDataChild와 로더가 완료 될 때까지 listDataChild의 모든 값이 빈 목록을 반환 것을 발견 listDataHeader의 모든 항목을 보장하고 실제로 데이터를 채 웁니다

listDataChild = new HashMap<String, List<List<String>>>(); 
    listDataChild.put("Monday", new ArrayList<List<String>>()); 
    listDataChild.put("Tuesday", new ArrayList<List<String>>()); 
    listDataChild.put("Wednesday", new ArrayList<List<String>>()); 
    listDataChild.put("Thursday", new ArrayList<List<String>>()); 
    listDataChild.put("Friday", new ArrayList<List<String>>()); 

    listDataHeader = new ArrayList<String>(listDataChild.keySet()); 

.

+0

우수 답변 감사합니다. Java 지식이 없으므로 한 달 안에 다른 교과서와 함께이 앱으로 갔기 때문에 내 안드로이드 이론은 부족합니다./ 어쨌든, 몇 가지 추가해야합니다.어떤 이유로 인해 오리엔테이션 변경시 데이터를 다시로드하기 위해 onResume()에서 restartLoader를 호출해야합니다. 이 기사에 대한 나의 이해에도 불구하고. 마지막으로, listDataHeader는 해시 맵이 정렬되지 않기 때문에 여전히 하드 코딩 된 일 목록을 필요로합니다. –

+0

Gotcha. 참고로 LinkedHashMap 대신 항상 LinkedHashMap을 사용할 수 있습니다. –