Я посмотрел почти на каждый поток переполнения стека в отношении залпа и утечек, обработчиков и утечек, слабых ссылок и чуть не стал морально парализованным. Моя борьба заключалась в переводе техник из статей в мой код.
По сути, я пытаюсь «пинговать» локальный сервер внутри фрагмента в приложении для Android, чтобы узнать, есть ли он там или нет, используя базовый запрос получения залпа. Если это так, я загружаю страницу, а если нет, я загружаю пустую страницу и продолжаю вращать "ожидание соединения".
Моя проблема в том, что я получаю значительные утечки при повторных звонках на залп. Я надеюсь, что кто-то с реальным опытом борьбы с этими проблемами мог бы помочь. Я прошу, чтобы ответы были в контексте моего размещенного кода. Если бы статьи имели для меня смысл, я бы не стал публиковать здесь.
Вот лишь несколько заметных статей, которые я прочитал, но, вероятно, еще 100, которые я прочитал в блогах и переполнены стеками, пытаясь понять проблему.
Утечка активности при использовании залповых слушателей
https://www.smashingmagazine.com/2017/03/simplify-android-networking-volley-http-library/
http://blog.nimbledroid.com/2016/09/06/stop-memory-leaks.html
Я могу избавиться от утечки, если я не вызову isOnline (), но, очевидно, программа больше не делает то, что мне нужно, поэтому я знаю, что утечка имеет отношение как к залпу, так и к обработчику.
открытый класс DeviceFragment extends Fragment {
Handler handler;
boolean online;
boolean online_prev;
WebView webView;
private static final String TAG = "DeviceFragment";
ProgressBar spinner;
TextView spinnertext;
int toastCount;
boolean onlineBoolean;
boolean isDestroyed;
RequestQueue queue;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_device, container, false);
queue = Volley.newRequestQueue(getActivity()); // moved to help with leaks
spinner = view.findViewById(R.id.progressBar1);
spinnertext = view.findViewById(R.id.progressBar1text);
online = false;
online_prev = false;
toastCount = 1;
handler = new Handler();
isDestroyed = false;
spinner.setVisibility(View.VISIBLE);
spinnertext.setVisibility(View.VISIBLE);
webView = view.findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new myWebClient() {
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
view.loadUrl("about:blank");
Toast.makeText(getActivity().getApplicationContext(), "Waiting For Connection", Toast.LENGTH_LONG).show();
}
});
// New method to get to WiFi settings menu
Button wifisettings = view.findViewById(R.id.WiFiSettings);
wifisettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS));
}
});
Button connect = view.findViewById(R.id.connect);
connect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
webView.loadUrl("http://10.123.40.2");
}
});
handler.postDelayed(runnableCode, 1000);
return view;
}
@Override
public void onResume() {
super.onResume();
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
@Override
public void onPause() {
super.onPause();
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
private Runnable runnableCode = new Runnable() {
@Override
public void run() {
online = onlineBoolean;
if(isDestroyed == false){
isOnline();
}
if (online == true && online_prev == false) {
webView.loadUrl("http://10.123.40.2");
spinner.setVisibility(View.GONE);
spinnertext.setVisibility(View.GONE);
toastCount = 0;
} else if (online == true && online_prev == true) {
// do nothing
} else if (online == false && online_prev == true) {
// do nothing
} else {
webView.loadUrl("about:blank");
spinner.setVisibility(View.VISIBLE);
spinnertext.setVisibility(View.VISIBLE);
if (toastCount == 0) {
Toast.makeText(getActivity().getApplicationContext(), "Wi-Fi Connection Lost - Please Check Wi-Fi Settings", Toast.LENGTH_LONG).show();
toastCount = 1;
}
}
online_prev = online;
//handler = new Handler(); // leaks?
handler.removeCallbacksAndMessages(null); // Not sure if this helps with leaks?
handler.postDelayed(this, 5000);
}
};
public class myWebClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
public void isOnline() {
//RequestQueue queue = Volley.newRequestQueue(getActivity()); // Causes huge leak. Move to public
String url = "http://10.123.40.2";
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
onlineBoolean = true;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onlineBoolean = false;
}
});
queue.add(stringRequest);
}
public void onDestroy () {
handler.removeCallbacks(runnableCode);
isDestroyed = true;
super.onDestroy ();
}
}
UPDATE:
Я реализовал слабые ссылки для обработчика и запуска, и, похоже, это не улучшило ситуацию.
открытый класс DeviceFragment extends Fragment {
private final NimbleHandler nimblehander = new NimbleHandler(this);
private static class NimbleHandler extends Handler{
private WeakReference<DeviceFragment> weakReference;
public NimbleHandler(DeviceFragment activity) {
weakReference = new WeakReference<>(activity);
}
@Override public void handleMessage(Message message){
super.handleMessage(message);
}
}
private static class NimbleRunnable implements Runnable {
private WeakReference<DeviceFragment> weakReference;
public NimbleRunnable(DeviceFragment activity) {
weakReference = new WeakReference<>(activity);
}
@Override public void run(){
while(true);
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_device, container, false);
queue = Volley.newRequestQueue(getActivity());
spinner = view.findViewById(R.id.progressBar1);
spinnertext = view.findViewById(R.id.progressBar1text);
online = false;
online_prev = false;
toastCount = 1;
isDestroyed = false;
spinner.setVisibility(View.VISIBLE);
spinnertext.setVisibility(View.VISIBLE);
webView = view.findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new myWebClient() {
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
view.loadUrl("about:blank");
Toast.makeText(getActivity().getApplicationContext(), "Waiting For Connection", Toast.LENGTH_LONG).show();
}
});
nimblehander.post(runnableCode);
return view;
}
private final Runnable runnableCode = new NimbleRunnable(this) {
@Override
public void run() {
online = onlineBoolean;
if(isDestroyed == false){
isOnline();
}
// omitted rest of code to keep short
nimblehander.postDelayed(this, 5000);
}
};
}