I heard that the Anonymous Classes can leak memory.
Similarly, Anonymous Classes will also maintain a reference to the class that they were declared inside. Therefore a leak can occur if you declare and instantiate an AsyncTask anonymously inside your Activity. If it continues to perform background work after the Activity has been destroyed, the reference to the Activity will persist and it won’t be garbage collected until after the background task completes.
Should anonymous class object set to null onDestroy to prevent memory leaks? Here are some pieces of my code.
public class RegisterActivity extends AppCompatActivity {
private ApiHandler registerHandler = null;
private static final int SERVICE_REQUEST_REGISTER = 243;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
init();
}
private void init() {
useApiService();
initApiHandler();
}
protected void useApiService() {
apiService = ApiClient.getClient(getApplicationContext()).create(ApiInterface.class);
}
private void initApiHandler() {
registerHandler = new ApiHandler(this, SERVICE_REQUEST_REGISTER) {
#Override
protected String successStatusCode() {
return "802";
}
#Override
protected String secretKey() {
return getDefaultKey();
}
#Override
protected boolean isExchangeSecretKey() {
return false;
}
};
}
#Override
protected void onDestroy() {
super.onDestroy();
registerHandler = null;
}
}
Actually garbage colector do it for you.
What the piece of text you pasted says is if the Anonymous class you are creating start a new AsyncTask, the main class where you created it will never be destroyed...
in other words while there is one Anonymous class running tasks onDestroy is never called on main class
Related
My code is
// Asynctask class to handle load data from database in background
private class SyncGetLocations extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mPrgLoading.setVisibility(View.VISIBLE);
mScrollView.setVisibility(View.GONE);
mToolbarView.setVisibility(View.GONE);
}
#Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Get data from database
getLocationDataFromDatabase(mSelectedId);
return null;
}
… etc
Android studio report that this Asynctask class should be static else leaks might occur.
When change to static get a lots of errors like non-static methods and fields cannot be referenced from a static context.
I added:
MyActivity myactivity = new MyActivity();
myactivity.mPrgLoading.setVisibility(View.VISIBLE);
…
all look nice until I run application and get error because I try to access void fields.
Advices?
The problem is that when you make the inner class static you no longer have access to the Activity and its properties. One workaround would be to have a weak reference to your Activity inside the AsynkTask:
private static class YourTask extends AsyncTask<Void, Void, Void> {
private WeakReference<YourActivity> activityReference;
YourTask(YourActivity context) {
activityReference = new WeakReference<>(context);
}
#Override
protected String doInBackground(Void... params) {
return "task finished";
}
#Override
protected void onPostExecute(String result) {
YourActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
activity.myVariable = "example";
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
}
}
Anyway I think that creating the AsyncTask as a top level class instead of as an inner class would also solve your problem (as an inner class you have the risk of the class being alive more time than it needs to because of the AsyncTask).
I have a suspicion that there may be memory leaks. Since ServerManager will hold the callback link, and this callback will hold the activation link. Will there be memory leaks in this code?
Ativity {
TextView textView;
Handler h = new MyHandler();
interface Update{
void update(Data data);
}
private Update listener = new Update() {
#Override
public void update(Data data) {
textView.setText(data.getText());
handler.sendEmptyMessage(100);
}
}
#Override
public void onCreate() {
super.onCreate();
textView = findViewById(R.id.textView);
ServerManager.getInstance().addCallBack(listener);//Will keep a link to the implementation of the interface
}
private class MyHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case 100:
textView.setText("бла бла");
return;
}
}
}
}
Will CallBack keep a link to the fragment? How to do it better?
If the ServerManager is a singleton, there is definitely a context leak : it holds a reference to the Update instance which (as an non-static inner class) holds a reference to the activity declaring it.
Examples of solution could be :
Add an unregister method to the ServerManager and call it from the symetrical lifecycle callback (onDestroy for onCreate, onStop for onStart, ...)
Use a WeakReference, which doesn't prevent the referenced object to be collected.
I hate The application may be doing too much work on its main thread, skipped XXX frames.. warning, Also it degrades the users UI interaction experience. So trying it with proper way as android wants it to be...
MainActivity :
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener
{
public Button StartBg;
private static final String TAG = "TASK_FIRST";
private Handler mainHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StartBg = findViewById(R.id.StartBg);
StartBg.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
ExampleRunner ExampleRunnerObj = new ExampleRunner(50000);
new Thread(ExampleRunnerObj).start();
}
});
}
}
When i keeps below class as inner class of MainActivity, It is able to access UI Components.
ExampleRunner :
public class ExampleRunner implements Runnable
{
int count;
public ExampleRunner(int count)
{
this.count = count;
}
#Override
public void run()
{
Handler threadHandler = new Handler(Looper.getMainLooper());
for(int i = 0; i < count; i++)
{
Log.d(TAG,"PERFORMING : "+i+"\n");
if(i == 25000)
{
threadHandler.post(new Runnable()
{
#Override
public void run()
{
// StartBg.setText("50k");
// OR RETURN SOMETHING..
}
});
}
}
}
}
But when i makes ExampleRunner as separate external class, it says StartBg can not be resolved...
So, How should I :
Make external java class which implements Runnable, Access Main Threads UI components...?
Or At least return something to mainActivity where i am starting it, so that from mainActivity i can access it?
Thanks in advance.
You need to pass the external class the Button as a reference:
public class ExampleRunner implements Runnable
{
int count;
Button startBg;
public ExampleRunner(int count, Button startBg) {
this.count = count;
this.startBg = startBg;
}
and create it with it:
ExampleRunner ExampleRunnerObj = new ExampleRunner(50000, StartBg);
then it will be able to use it in run().
Right now, the ExampleRunner is an anonymous class accessing the StargBg variable declared locally.
If, I right understood you, for your solution, AsyncTask it is good solution.
Check docs https://developer.android.com/reference/android/os/AsyncTask
I have a class which uses AndroidAnnotations and #ViewById annotation. I've seen many crashes because of some of the fields with #ViewById annotations being null.
Attempt to invoke virtual method 'void com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout.setRefreshing(boolean)' on a null object reference
a problem with two of them which
Here's part of the class:
#EFragment(R.layout.fragment_categories_tab)
public class CategoriesTabFragment extends BaseFragment implements CategoryAdapter.OnItemSelected, SwipyRefreshLayout.OnRefreshListener {
#ViewById(R.id.swipeRefreshLayout)
protected SwipyRefreshLayout swipeRefreshLayout;
#ViewById(R.id.placeholder_textview)
protected CustomTextView statusLabel;
private void loadChildren() {
AndroidUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(true); //crash happens here, but not always
}
});
}
categoryService.getCategories(new CommListener<Pair<Category[], Boolean>>() {
#Override
public void onSuccess(NetworkResult result, Pair<Category[], Boolean> object) {
AndroidUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
if (getActivity() != null) {
statusLabel.setText(""); //another point that crash happens, but again sometime and only in early stage of the fragment's life
}
}
}
}
}
And this is the AndroidUtil class:
public class AndroidUtil {
private static Handler mainHandler = new Handler(Looper.getMainLooper());
public static void runOnUiThread(Runnable runnable) {
mainHandler.post(runnable);
}
}
I'm new to Android; and I don't seem to find the reason. I'd appreciate any help, in advance.
Change AndroidUtil.runOnUiThread to getActvity() and check null before using.
In your case, it's crashing because sometimes your fragment is not attached to the Activity so all you layout inside is null.
What i am trying to do is to pass a reference to the mainactivity to another class and use it.
when i first pass the reference it is not null. but later when i use it in an event handler it becomes null.
here is the code:
public class MainActivity extends MapActivity implements SmsReceivedListener {
SmsReceiver smsreceiver = new SmsReceiver();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
smsreceiver.setSmsReceivedListener(this);
}
public void drawme() {
// do smth
}
#Override
public void onSmsReceived(String id, String lon, String lat) {
Toast.makeText(this, "SMS delivered", Toast.LENGTH_LONG).show();
}
}
The other class where I obtain the reference:
public class SmsReceiver extends BroadcastReceiver {
private SmsReceivedListener listener;
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
#SuppressLint("NewApi")
#Override
public void onReceive(final Context context, final Intent intent) {
// Here it is NULL
if(listener!=null)
listener().onSmsReceived(carNumber, lon,lat);
}
public void setSmsReceivedListener(SmsReceivedListener mainActivity) {
//It is no null when I first set the object here
listener = (mainActivity);
}
}
It will only "become" null if you
set it to null
use it in a way which is not thread safe and you are reading in a different thread to setting
you are using the same field, but in a different object.
You are not registering the SmsReceiver, therefor i guess the following :
The SmsReceiver is registered in the Manifest. Therefore, an instance of the receiver is created by the system when the broadcast is detected, and it is not the same instance as the one you create.
Suggestion : register the receiver in the activity, not in the manifest.