首页 > 解决方案 > 当后台堆栈中的 Activity 及其片段被调用到前台堆栈时,Android 应用程序崩溃

问题描述

我有一个活动,它创建一个片段,该片段使用网络连接来用数据更新 UI。它最初可以工作,但是当我开始一个新活动然后尝试返回实例化片段的活动时,应用程序崩溃了。logcat 声明它正在尝试引用一个空对象,这是因为我有一个临时修复来解析 JSON 字符串,不要注意这个哈哈,因为这不是问题的根源,只是主要问题的结果手头,我认为这是我对片段(及其父活动)生命周期缺乏了解,有人可以帮助我吗?

这是在启动时运行并创建片段的活动。

public class BotListActivity extends FragmentActivity implements DownloadCallback{
    // network variables
    private int mInterval = 5000; //5 seconds default
    private Handler mHandler;
    // Keep a reference to the NetworkFragment which owns the AsyncTask object
    // that is used to execute network ops.
    private NetworkFragment mNetworkFragment;
    // Boolean telling us whether a download is in progress, so we don't trigger overlapping
    // downloads with consecutive button clicks.
    private boolean mDownloading = false;
    private String mJSONPost = "{}"; 

    private String TAG = MainActivity.class.getSimpleName();

    //Object variables for list view implementation
    RecyclerView recyclerView;
    RecyclerAdapter adapter;
    //string array which holds list data
    ArrayList<String> botNames = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bot_list);
        //getSupportActionBar().hide();

        // This handler is to conduct network connections every 5 seconds
        mHandler = new Handler();
        startRepeatingTask();

        recyclerView = findViewById(R.id.RecyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter= new RecyclerAdapter(this, botNames);
        recyclerView.setAdapter(adapter);

        final Button createTradingBotButton = findViewById(R.id.createBotBtn);

        createTradingBotButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
             // this on click methods pulls up bot configuration activity
                //onDestroy();
                Intent i = new Intent( BotListActivity.this,BotConfiguration.class);
                startActivity(i);
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopRepeatingTask();
        Log.e(TAG, "did the on destroy happen for BotListActivity");
    }

    /**
     * Runnable class instance is meant to run once via the volatile boolean shutdown variable turning
     * into a value of true. A temporary while loop is conducted to allow runnable to run once then shut down.
     */
    Runnable mStatusChecker = new Runnable() {
        volatile boolean shutdown = false;
        @Override
        public void run() {
            while(!shutdown) {
                try {        //network connection to server instantiation
                    mNetworkFragment = NetworkFragment.getInstance(getSupportFragmentManager(), "localhost:8000/api/v1/Trading_Bots", mJSONPost);
                    startDownload();
                    Log.e(TAG, "network connection ran in botlistactivity");
                    ; //this function can change value of mInterval.
                } finally {
                    // 100% guarantee that this always happens, even if
                    // your update method throws an exception

                    Log.e(TAG, "Runnable network class has been stopped.....botlistactivity");
                    mHandler.postDelayed(mStatusChecker, mInterval);
                    stopRepeatingTask();
                    shutdown = true;
                }
            }
        }
    };

    void startRepeatingTask() {
        mStatusChecker.run();
    }
    void stopRepeatingTask() {
        mHandler.removeCallbacks(mStatusChecker);
        Log.e(TAG, "stopRepeatingTask  for BotListActivity has been completed");
    }
    private void startDownload() {
        if (!mDownloading && mNetworkFragment != null) {
            // Execute the async download.
            mNetworkFragment.startDownload();
            mDownloading = true;
        }
    }

    /// TODO's - parse the nested json objects for the json updates that took place
    @Override
    public void updateFromDownload(String result){
        Log.e(TAG, result);
        Log.e(TAG, "that was the result'");
        mJSONPost = result.replace("HTTP/1.0 200 OK", ""); // Probably want to figure this one out as well
        Log.e(TAG, mJSONPost);
        Log.e(TAG, "THAT WAS MJSONPOTST");
        if (result != null) {
            Log.e(TAG, mJSONPost.substring(mJSONPost.indexOf("{"), mJSONPost.lastIndexOf("}") + 1) );
            try {
                int insertIndex = 0;
                JSONObject JO = new JSONObject(mJSONPost.substring(mJSONPost.indexOf("{"), mJSONPost.lastIndexOf("}") + 1));
                JSONArray JA = JO.getJSONArray("Trading_Bots");
                // The for loop below retrieves the specefic elements of each json trading object
                // In this implementation we just need the name of the bot for now
                botNames.clear();
                for (int i = 0; i < JA.length(); i++) {
                    Log.e(TAG, "JSON ARRAY LENGTH = " + JA.length());
                    //JSONObject JO = (JSONObject) JA.get(i);  //only use when extracting specific data of json object other than bot names
                    JSONObject JO2 = new JSONObject(JA.getString(i));
                    String botName = JO2.keys().next();
                    botNames.add(insertIndex, botName);
                    Log.e(TAG, "botName = " + botName);
                }
                Log.e(TAG, "botNames = " + botNames);
                adapter.notifyItemInserted(insertIndex);
                adapter.notifyDataSetChanged();
            } catch (final JSONException e) {
                //mDataText.setText("JSON parsing error");
                Log.e(TAG, "Json parsing error:  " + e.getMessage());
                Log.e(TAG, "Json result = " + result);
                Log.e(TAG, "json mJSONPost = " + mJSONPost);
            }
        } else {
            Log.e(TAG, "Error... Bot List Activity updateFromDownload not working");
            // put error logging output here "no data available"
        }

    }

    @Override
    public NetworkInfo getActiveNetworkInfo () {
        ConnectivityManager connectivityManager =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        return networkInfo;
    }

    @Override
    public void finishDownloading () {
        mDownloading = false;
        if (mNetworkFragment != null) {
            mNetworkFragment.cancelDownload();
        }
    }

    @Override
    public void onProgressUpdate ( int progressCode, int percentComplete){
        switch (progressCode) {
            // You can add UI behavior for progress updates here.
            case DownloadCallback.Progress.ERROR:
                break;
            case DownloadCallback.Progress.CONNECT_SUCCESS:
                break;
            case DownloadCallback.Progress.GET_INPUT_STREAM_SUCCESS:
                break;
            case DownloadCallback.Progress.PROCESS_INPUT_STREAM_IN_PROGRESS:
                break;
            case DownloadCallback.Progress.PROCESS_INPUT_STREAM_SUCCESS:
                break;
        }
    }

这是片段。

public class NetworkFragment extends Fragment {
    public static final String TAG = "NetworkFragment";

    private static final String URL_KEY = "UrlKey";
    private static final String JSON_KEY = "JsonKey";
    private DownloadCallback mCallback;
    private DownloadTask mDownloadTask;
    private String mUrlString;
    private String mJSONString;

    /**
     * Static initializer for NetworkFragment that sets the URL of the host it will be downloading
     * from.
     */
    public static NetworkFragment getInstance(FragmentManager fragmentManager, String url, String json_post) {
        // Recover NetworkFragment in case we are re-creating the Activity due to a config change.
        // This is necessary because NetworkFragment might have a task that began running before
        // the config change and has not finished yet.
        // The NetworkFragment is recoverable via this method because it calls
        // setRetainInstance(true) upon creation.
        NetworkFragment networkFragment = (NetworkFragment) fragmentManager
                .findFragmentByTag(NetworkFragment.TAG);
        if (networkFragment == null) {
            networkFragment = new NetworkFragment();
            Bundle args = new Bundle();
            args.putString(URL_KEY, url);
            args.putString(JSON_KEY, json_post);
            networkFragment.setArguments(args);
            fragmentManager.beginTransaction().add(networkFragment, TAG).setReorderingAllowed(true).commit();
        }else{
            fragmentManager.beginTransaction().remove(networkFragment).commit();
        }
        return networkFragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Retain this Fragment across configuration changes in the host Activity.
        setRetainInstance(true);
        mUrlString = getArguments().getString(URL_KEY);
        mJSONString = getArguments().getString(JSON_KEY);

    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Host Activity will handle callbacks from task.
        mCallback = (DownloadCallback)context;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // Clear reference to host Activity.
        mCallback = null;
    }

    @Override
    public void onDestroy() {
        // Cancel task when Fragment is destroyed.
        Log.e(TAG, "onDestroy() in fragment");
        cancelDownload();
        super.onDestroy();

    }

    /**
     * Start non-blocking execution of DownloadTask.
     */
    public void startDownload() {
        Log.e(TAG, "startDownload in fragment");
        cancelDownload();
        mDownloadTask = new DownloadTask();
        mDownloadTask.execute("http://10.0.2.2:8000/api/v1/Trading_Bots");//mUrlString  USE THIS FOR A MORE DIVERSE SERVER
    }

    /**
     * Cancel (and interrupt if necessary) any ongoing DownloadTask execution.
     */
    public void cancelDownload() {
        if (mDownloadTask != null) {
            mDownloadTask.cancel(true);
            mDownloadTask = null;
        }
    }

    /**
     * Implementation of AsyncTask that runs a network operation on a background thread.
     */
    private class DownloadTask extends AsyncTask<String, Integer, DownloadTask.Result> {

        /**
         * Wrapper class that serves as a union of a result value and an exception. When the
         * download task has completed, either the result value or exception can be a non-null
         * value. This allows you to pass exceptions to the UI thread that were thrown during
         * doInBackground().
         */
        class Result {
            public String mResultValue;
            public Exception mException;
            public Result(String resultValue) {
                mResultValue = resultValue;
            }
            public Result(Exception exception) {
                mException = exception;
            }
        }

        /**
         * Cancel background network operation if we do not have network connectivity.
         */
        @Override
        protected void onPreExecute() {
            if (mCallback != null) {
                NetworkInfo networkInfo = mCallback.getActiveNetworkInfo();
                if (networkInfo == null || !networkInfo.isConnected() ||
                        (networkInfo.getType() != ConnectivityManager.TYPE_WIFI
                                && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) {
                    // If no connectivity, cancel task and update Callback with null data.
                    mCallback.updateFromDownload("");
                    cancel(true);
                }
            }
        }

        /**
         * Defines work to perform on the background thread.
         */
        @Override
        protected Result doInBackground(String... urls) {
            Result result = null;
            //String result = "";
            if (!isCancelled() && urls != null && urls.length > 0) {
                String urlString = urls[0];

                try {
                    URL url = new URL(urlString);
                    String resultString = downloadUrl(url);
                    if (resultString != null) {
                        result = new Result(resultString);
                    } else {
                        throw new IOException("No response received.");
                    }
                } catch(Exception e) {
                    result = new Result(e);
                }
            }
            return result;
        }

        /**
         * Send DownloadCallback a progress update.
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            if (values.length >= 2) {
                mCallback.onProgressUpdate(values[0], values[1]);
            }
        }

        /**
         * Updates the DownloadCallback with the result.
         */
        @Override
        protected void onPostExecute(Result result) {
            if (result != null && mCallback != null) {
               if (result.mException != null) {
                   mCallback.updateFromDownload(result.mException.getMessage());
                }
                if (result.mResultValue != null) {
                    mCallback.updateFromDownload(result.mResultValue);
                }
                mCallback.finishDownloading();
            }
        }

        /**
         * Override to add special behavior for cancelled AsyncTask.
         */
        @Override
        protected void onCancelled(Result result) {
        }

        /**
         * Given a URL, sets up a connection and gets the HTTP response body from the server.
         * If the network request is successful, it returns the response body in String form. Otherwise,
         * it will throw an IOException.
         */
        // might have to pass a json object  through here
        private String downloadUrl(URL url) throws IOException {

            InputStream stream = null;
            HttpURLConnection connection = null;
            String result = null;
            try {
                connection = (HttpURLConnection) url.openConnection();
                // For this use case, set HTTP method to POST.
                connection.setRequestMethod("POST");
                //Set the Request Content-Type Header Parameter
                connection.setRequestProperty("content-type", "application/json; utf-8");
                //Set Response Format Type
                connection.setRequestProperty("Accept", "application/json");
                // Already true by default but setting just in case; needs to be true since this request
                // is carrying an input (response) body.  noooooo
                JSONObject json = new JSONObject(mJSONString);
                try{
                    //json.put("bot_activated", "true");
                    //json.put("is_sim_on", "True");
                  //  json.put("profit_reinvest", "100");
                  //  json.put("is_main_on", "Maybe");
                } catch(Exception e){
                    e.printStackTrace();
                }
                // ensure the connection will be used to SEND content
                connection.setDoOutput(true);
                DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                out.writeBytes(json.toString());
                out.flush();
                out.close();

                int status = connection.getResponseCode();
                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder content = new StringBuilder();
                while ((inputLine = in.readLine()) != null) {
                    Log.e("appendage", content.toString());
                    content.append(inputLine);
                }
                in.close();
                    result = content.toString();
            }
                /*Ths is for GET requests
                // Open communications link (network traffic occurs here).
                connection.connect();
                publishProgress(DownloadCallback.Progress.CONNECT_SUCCESS);
                int responseCode = connection.getResponseCode();
                if (responseCode != HttpsURLConnection.HTTP_OK) {
                    throw new IOException("HTTP error code: " + responseCode);
                }
                // Retrieve the response body as an InputStream.
                stream = connection.getInputStream();
                publishProgress(DownloadCallback.Progress.GET_INPUT_STREAM_SUCCESS, 0);
                if (stream != null) {
                    // Converts Stream to String with max length of 500.
                    result = readStream(stream, 500);
                    publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_SUCCESS, 0);
                }

              /*catch ( MalformedURLException e){
                e.printStackTrace();;
            } catch (IOException e){
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }*/ catch (JSONException e) {
                e.printStackTrace();
            } finally {

                // Close Stream and disconnect HTTPS connection.
                if (stream != null) {
                    stream.close();
                }
                if (connection != null) {
                    connection.disconnect();
                }
            }
            Log.e("finally", "what");
            Log.e("HEY", result);
            return result;
        }

        /**
         * Converts the contents of an InputStream to a String.
         */
        private String readStream(InputStream stream, int maxLength) throws IOException {
            String result = null;
            // Read InputStream using the UTF-8 charset.
            InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
            // Create temporary buffer to hold Stream data with specified max length.
            char[] buffer = new char[maxLength];
            // Populate temporary buffer with Stream data.
            int numChars = 0;
            int readSize = 0;
            while (numChars < maxLength && readSize != -1) {
                numChars += readSize;
                int pct = (100 * numChars) / maxLength;
                publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_IN_PROGRESS, pct);
                readSize = reader.read(buffer, numChars, buffer.length - numChars);
            }
            if (numChars != -1) {
                // The stream was not empty.
                // Create String that is actual length of response body if actual length was less than
                // max length.
                numChars = Math.min(numChars, maxLength);
                result = new String(buffer, 0, numChars);
            }
            return result;
        }
    }
}

标签: javaandroidxmlandroid-fragments

解决方案


推荐阅读