java - 当后台堆栈中的 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;
}
}
}