解析 Fragment 中的 JSON 数组

Parse JSON array in Fragment

我一直在使用 this guide 解析 JSON 数组并遇到奇怪的错误:

01-20 14:56:40.511  28118-28118/com.cbsystematic.mobile.itvdn E/WindowManager﹕ Activity com.cbsystematic.mobile.itvdn.NavigationActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4151cdd0 that was originally added here
android.view.WindowLeaked: Activity com.cbsystematic.mobile.itvdn.NavigationActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4151cdd0 that was originally added here
        at android.view.ViewRootImpl.<init>(ViewRootImpl.java:464)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:419)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:351)
        at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:171)
        at android.view.Window$LocalWindowManager.addView(Window.java:558)
        at android.app.Dialog.show(Dialog.java:282)
        at com.cbsystematic.mobile.itvdn.CatalogFragment$GetContacts.onPreExecute(CatalogFragment.java:178)
        at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
        at android.os.AsyncTask.execute(AsyncTask.java:534)
        at com.cbsystematic.mobile.itvdn.CatalogFragment.onCreate(CatalogFragment.java:89)
        at android.support.v4.app.Fragment.performCreate(Fragment.java:1763)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:913)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
        at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:739)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1489)
        at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:548)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1225)
        at android.app.Activity.performStart(Activity.java:5076)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2328)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
        at android.app.ActivityThread.access0(ActivityThread.java:151)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1335)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:155)
        at android.app.ActivityThread.main(ActivityThread.java:5520)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796)
        at dalvik.system.NativeStart.main(Native Method)

程序结构为导航抽屉Activity,内容为3个片段。我正在使用扩展 AbsListView.OnItemClickListener:

CatalogFragment

JSON数组如下:

[
{
    "Url":"windows-store-app-html5-and-vascript",
    "LessonsCount":1,
    "Name":"Разработка Windows Store приложений с JavaScript и HTML5.",
    "IsFree":true,
    "Price":0,
    "Language":"ru",
    "Difficulty":2,
    "Author":{
        "Id":"963163db-eee6-4e73-81b3-71b58a602f78","Name":"Дмитрий Охрименко",
        "PhotoUrl":"https://itvdn.blob.core.windows.net/authors/authors-okhrimenko.jpg",
        "Description":"Сертифицированный специалист Microsoft (MCTS, MCPD, MCT).Тренер-консультант, эксперт по построению распределенных и веб-ориентированных приложений.","Url":null}
},
.
.
]

GetCatalog class(与指南几乎相同):

  private class GetCatalog extends AsyncTask<Void, Void, Void> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // Showing progress dialog
        pDialog = new ProgressDialog(getActivity()); 
        pDialog.setMessage("Please wait...");
        pDialog.setCancelable(false);
        pDialog.show();
    }

    @Override
    protected Void doInBackground(Void... arg0) {
        // Creating service handler class instance
        ServiceHandler sh = new ServiceHandler();

        // Making a request to url and getting response
        String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);

        Log.d("Response: ", "> " + jsonStr);

        if (jsonStr != null) {
            try {
                // Getting JSON Array node
                catalog = new JSONArray(jsonStr);

                // looping through All Contacts
                for (int i = 0; i < catalog.length(); i++) {
                    JSONObject c = catalog.getJSONObject(i);

                    String name = c.getString(TAG_NAME);

                    // tmp hashmap for single contact
                    HashMap<String, String> contact = new HashMap<String, String>();

                    // adding each child node to HashMap key => value

                    contact.put(TAG_NAME, name);

                    // adding contact to contact list
                    catalogList.add(contact);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } else {
            Log.e("ServiceHandler", "Couldn't get any data from the url");
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        // Dismiss the progress dialog
        if (pDialog.isShowing())
            pDialog.dismiss();

        /**
         * Updating parsed JSON data into ListView
         * */

        ListAdapter adapter = new SimpleAdapter(
                getActivity(), catalogList,
                R.layout.catalog_list_item, new String[] {TAG_NAME}, new int[] {
                R.id.name });

        mListView.setAdapter(adapter);
    }

}

我正在尝试传递 OnCreate()...

中的内容
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    new GetCatalog().execute();
}

整个CatalogFragmentclass(如果需要的话):

public class CatalogFragment extends android.support.v4.app.Fragment implements AbsListView.OnItemClickListener {

private static final String ARG_SECTION_NUMBER = "section_number";

private ProgressDialog pDialog;

// URL to get contacts JSON
private static String url = "http://api.androidhive.info/contacts/";

// JSON Node names
private static final String TAG_NAME = "Name";

// contacts JSONArray
JSONArray catalog = null;

// Hashmap for ListView
ArrayList<HashMap<String, String>> catalogList;

private OnFragmentInteractionListener mListener;

/**
 * The fragment's ListView/GridView.
 */
private AbsListView mListView;

/**
 * The Adapter which will be used to populate the ListView/GridView with
 * Views.
 */
private ListAdapter mAdapter;

// TODO: Rename and change types of parameters
public static CatalogFragment newInstance( int sectionNumber) {
    CatalogFragment fragment = new CatalogFragment();
    Bundle args = new Bundle();
    args.putInt(ARG_SECTION_NUMBER, sectionNumber);
    fragment.setArguments(args);
    return fragment;
}

/**
 * Mandatory empty constructor for the fragment manager to instantiate the
 * fragment (e.g. upon screen orientation changes).
 */
public CatalogFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    new GetCatalog().execute();

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_catalog, container, false);

    // Set the adapter
    mListView = (AbsListView) view.findViewById(android.R.id.list);
    ((AdapterView<ListAdapter>) mListView).setAdapter(mAdapter);

    // Set OnItemClickListener so we can be notified on item clicks
    mListView.setOnItemClickListener(this);

    return view;
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentInteractionListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }

    ((NavigationActivity) activity).onSectionAttached(
                getArguments().getInt(ARG_SECTION_NUMBER));
}

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}


@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  
}

/**
 * The default content for this Fragment has a TextView that is shown when
 * the list is empty. If you would like to change the text, call this method
 * to supply the text it should use.
 */
public void setEmptyText(CharSequence emptyText) {
    View emptyView = mListView.getEmptyView();

    if (emptyView instanceof TextView) {
        ((TextView) emptyView).setText(emptyText);
    }
}

/**
 * This interface must be implemented by activities that contain this
 * fragment to allow an interaction in this fragment to be communicated
 * to the activity and potentially other fragments contained in that
 * activity.
 * <p/>
 * See the Android Training lesson <a href=
 * "http://developer.android.com/training/basics/fragments/communicating.html"
 * >Communicating with Other Fragments</a> for more information.
 */
public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    public void onCatalogFragmentInteraction(String id);
}

private class GetCatalog extends AsyncTask<Void, Void, Void> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // Showing progress dialog
        pDialog = new ProgressDialog(getActivity());
        pDialog.setMessage("Please wait...");
        pDialog.setCancelable(false);
        pDialog.show();
    }

    @Override
    protected Void doInBackground(Void... arg0) {
        // Creating service handler class instance
        ServiceHandler sh = new ServiceHandler();

        // Making a request to url and getting response
        String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);

        Log.d("Response: ", "> " + jsonStr);

        if (jsonStr != null) {
            try {
                // Getting JSON Array node
                catalog = new JSONArray(jsonStr);

                // looping through All Contacts
                for (int i = 0; i < catalog.length(); i++) {
                    JSONObject c = catalog.getJSONObject(i);

                    String name = c.getString(TAG_NAME);

                    // tmp hashmap for single contact
                    HashMap<String, String> contact = new HashMap<String, String>();

                    // adding each child node to HashMap key => value

                    contact.put(TAG_NAME, name);

                    // adding contact to contact list
                    catalogList.add(contact);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } else {
            Log.e("ServiceHandler", "Couldn't get any data from the url");
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        // Dismiss the progress dialog
        if (pDialog.isShowing())
            pDialog.dismiss();

        /**
         * Updating parsed JSON data into ListView
         * */

        ListAdapter adapter = new SimpleAdapter(
                getActivity(), catalogList,
                R.layout.catalog_list_item, new String[] {TAG_NAME}, new int[] {
                R.id.name });

        mListView.setAdapter(adapter);
    }

}
}

P.S。我一直在尝试从指南中下载完整的代码,这很有效。但是当在我的代码中使用相同的代码时,它不会。

感谢您的帮助


编辑: 在评论与 ProgressDialog 有关的操作后:

01-20 16:11:33.641    4969-5281/com.cbsystematic.mobile.itvdn E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
    java.lang.RuntimeException: An error occured while executing doInBackground()
            at android.os.AsyncTask.done(AsyncTask.java:299)
            at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
            at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:230)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:864)
     Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)
            at java.net.InetAddress.lookupHostByName(InetAddress.java:428)
            at java.net.InetAddress.getAllByNameImpl(InetAddress.java:239)
            at java.net.InetAddress.getAllByName(InetAddress.java:214)
            at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:137)
            at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
            at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
            at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:367)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:593)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:510)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:488)
            at com.cbsystematic.mobile.itvdn.ServiceHandler.makeServiceCall(ServiceHandler.java:73)
            at com.cbsystematic.mobile.itvdn.ServiceHandler.makeServiceCall(ServiceHandler.java:37)
            at com.cbsystematic.mobile.itvdn.CatalogFragment$GetCatalog.doInBackground(CatalogFragment.java:187)
            at com.cbsystematic.mobile.itvdn.CatalogFragment$GetCatalog.doInBackground(CatalogFragment.java:169)
            at android.os.AsyncTask.call(AsyncTask.java:287)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:230)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:864)
     Caused by: libcore.io.GaiException: getaddrinfo failed: EAI_NODATA (No address associated with hostname)
            at libcore.io.Posix.getaddrinfo(Native Method)
            at libcore.io.ForwardingOs.getaddrinfo(ForwardingOs.java:55)
            at java.net.InetAddress.lookupHostByName(InetAddress.java:415)
            at java.net.InetAddress.getAllByNameImpl(InetAddress.java:239)
            at java.net.InetAddress.getAllByName(InetAddress.java:214)
            at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:137)
            at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
            at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
            at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:367)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:593)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:510)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:488)
            at com.cbsystematic.mobile.itvdn.ServiceHandler.makeServiceCall(ServiceHandler.java:73)
            at com.cbsystematic.mobile.itvdn.ServiceHandler.makeServiceCall(ServiceHandler.java:37)
            at com.cbsystematic.mobile.itvdn.CatalogFragment$GetCatalog.doInBackground(CatalogFragment.java:187)
            at com.cbsystematic.mobile.itvdn.CatalogFragment$GetCatalog.doInBackground(CatalogFragment.java:169)
            at android.os.AsyncTask.call(AsyncTask.java:287)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:230)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:864)
     Caused by: libcore.io.ErrnoException: getaddrinfo failed: EACCES (Permission denied)
            at libcore.io.Posix.getaddrinfo(Native Method)
            at libcore.io.ForwardingOs.getaddrinfo(ForwardingOs.java:55)
            at java.net.InetAddress.lookupHostByName(InetAddress.java:415)
            at java.net.InetAddress.getAllByNameImpl(InetAddress.java:239)
            at java.net.InetAddress.getAllByName(InetAddress.java:214)
            at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:137)
            at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
            at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
            at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:367)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:593)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:510)
            at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:488)
            at com.cbsystematic.mobile.itvdn.ServiceHandler.makeServiceCall(ServiceHandler.java:73)
            at com.cbsystematic.mobile.itvdn.ServiceHandler.makeServiceCall(ServiceHandler.java:37)
            at com.cbsystematic.mobile.itvdn.CatalogFragment$GetCatalog.doInBackground(CatalogFragment.java:187)
            at com.cbsystematic.mobile.itvdn.CatalogFragment$GetCatalog.doInBackground(CatalogFragment.java:169)
            at android.os.AsyncTask.call(AsyncTask.java:287)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:230)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:864)

DoInBackground方法仔细看看:

@Override
protected Void doInBackground(Void... arg0) {
    // Creating service handler class instance
    ServiceHandler sh = new ServiceHandler();

    // Making a request to url and getting response
    String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);

    Log.d("Response: ", "> " + jsonStr);

    if (jsonStr != null) {
        try {
            // Getting JSON Array node
            catalog = new JSONArray(jsonStr);

            // looping through All Courses
            for (int i = 0; i < catalog.length(); i++) {
                JSONObject c = catalog.getJSONObject(i);

                String name = c.getString(TAG_NAME);

                // tmp hashmap for single course
                HashMap<String, String> contact = new HashMap<String, String>();

                // adding each child node to HashMap key => value

                contact.put(TAG_NAME, name);

                // adding course name to contact list
                catalogList.add(contact);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    } else {
        Log.e("ServiceHandler", "Couldn't get any data from the url");
    }

    return null;
}

作为例外明确指出:

java.lang.SecurityException: Permission denied (missing INTERNET permission?)

您必须添加行

<uses-permission android:name="android.permission.INTERNET" />

进入你的 manifest file.