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.
Related
PlaylistFragment starts an adapter:
playlistsAdapter = new PlaylistRecyclerAdapter(playlistsListArray, addToPlaylist, mSong, getActivity(), this);
PlaylistRecyclerAdapter binds data to the PlaylistViewHolder, something like this:
((PlaylistViewHolder) viewHolder).bind(this, dataSet.get(position), addToPlaylist, mSong);
User clicks on an item in PlaylistViewHolder:
context.startActivity(PublicPlaylistActivity.createStartIntent(context, playlist));
Now here is the question, how can PublicPlaylistActivity talk back to the initial PlaylistFragment?
I suggest you'd better use Interface from fragment to adapter. So when user clicks anything in adapter, call function realization in fragment. If you need your activity to proceed some operation - ((YourActivity) getActivity()).someMethod() should be called from fragment.
Second trick is using broadcastreceiver to send events. A bit more complicated. You have to launch broadcast in view you need to recive message and send these messages from adapter. This approach is more complexible to debug and support if system is wide spread, so you'd better use interfaces.
There are several ways of doing that. The simplest way should be starting the PublicPlaylistActivity with startActivityForResult. In that way, then the activity finishes, you can set send some data to the caller fragment (which is PlaylistFragment in your case). Here is a nice tutorial about the implementation.
Another way of doing that is by using lifecycle methods. You might have a public static variable which can keep track of some status that you might observe in your onResume function of your PlaylistFragment when you are returning back from your PublicPlaylistActivity. You might consider a sample implementation as follows.
Define a public static variable in your PlaylistFragment. Then in your onResume function check the value of that variable and take actions accordingly.
public static boolean someIndicator = false; // Initialize with a default value
#Override
protected void onResume() {
super.onResume();
if(someIndicator == true) doSomething();
else doSomethingElse();
}
Now you can set the indicator variable from anywhere in your application actually which will have the effect on your PlaylistFragment. For example, from your PublicPlaylistActivity, you might consider doing something like this.
public void someFunctionInYourPublicPlaylistActivity() {
// ...
// Some code and then the following
PlaylistFragment.someIndicator = true;
}
Another way of achieving the same thing is by using a BroadcastReceiver. Here is a tutorial on how you can implement one.
It really depends on how you are structuring your whole activity-fragments communication. Hope that helps!
I would do a common "context" class (ComContext) with an interface. When you create your fragment, you also create this class. And from the activity you can check if it exists or not.
I assume that you already have a helper(AppHelper) class with static variables.
public class AppHelper {
public static ComContext comContext = null;
}
public class MainFragment {
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ConContext comContext = new ComContext();
comContext.listener = this;
AppHelper.comContext = comContext;
}
#Override
public void onDataChanged() {
}
#Override
public void onDestroyView() {
super.onDestroyView();
AppHelper.comContext = null;
}
}
public class MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppHelper.comContext != null) {
AppHelper.comContext.listener.onDataChanged();
}
}
}
public class ComContext {
public interface HelperListener {
void onDataChanged();
}
public HelperListener listener = null;
}
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
I was dealing recently with a question I'm not sure how to answer.
I wrote a code example for some AsyncTask that I want to perform. I read somewhere on the net that someone has implemented the AsyncTask and the Handler as inner classes and I wanted to scale that a little bit and make less coupling so I made separated class for those so I can reuse them with more than one Activity.
Because I had to do some different UI things on each Activity I decided to make those activities implement an interface so I can react to each event with same methods.
What I don't understand is why do I need the handler object that will handle the messaging for event occurrence? can't I just use the listeners observer pattern? and then the question that I asked my self and can't understand the answers around the web is what is the difference between my listener observer implementation and the handler object we get from Android.
Here is my code example:
Activity 1:
public class SomeActivity extends Activity implements MyListener{
MyAsyncTask myTask;
MyHandler handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new MyHandler();
myTask = new MyAsyncTask(handler);
// initilize the activity views etc...
}
#Override
public void do1(){
// DO UI THINGS FOR ACTIVITY 1 IN A CALLBACK TO DO1 EVENT
}
#Override
public void do2(){
// DO UI THINGS FOR ACTIVITY 1 IN A CALLBACK TO DO2 EVENT
}
}
Activity 2:
public class OtherActivity extends Activity implements MyListener{
MyAsyncTask myTask;
MyHandler handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new MyHandler();
myTask = new MyAsyncTask(handler);
// initilize the activity views etc...
}
#Override
public void do1(){
// DO UI THINGS FOR ACTIVITY 2 IN A CALLBACK TO DO1 EVENT
}
#Override
public void do2(){
// DO UI THINGS FOR ACTIVITY 2 IN A CALLBACK TO DO2 EVENT
}
}
Listener interface:
public interface MyListener{
void do1();
void do2();
}
AsyncTask implementation:
public class MyAsyncTask extends AsyncTask<Void,Void,String>{
private MyModel m;
public MyAsyncTask(Handler h){
m = new MyModel();
m.setHandler(h);
}
protected String doInBackground(Void... params) {
// do something in background with MyModel m
return null;
}
}
Handler implementation:
public class MyHandler extends Handler {
Vector<MyListener> listeners = new Vector<>();
#Override
public void handleMessage(Message msg) {
switch(msg.what){
case 1:
// do something for case 1
fireMethod1();
break;
case 2:
// do something for case 2
fireMethod2();
break;
}
}
public void registerListener(MyListener l){
listeners.add(l);
}
public void unregisterListener(MyListener l){
listeners.remove(l);
}
private void fireMethod1(){
for(MyListener l : listeners){
l.do1();
}
}
private void fireMethod2(){
for(MyListener l : listeners){
l.do2();
}
}
}
Some demo model I created:
public class MyModel{
private Handel h;
public MyModel(){
// at some point send message 1 or message 2 ...
}
public void setHandler(Handler h){
this.h = h;
}
private void sendMessage1(){
h.obtainMessage(1, null);
}
private void sendMessage2(){
h.obtainMessage(2, null);
}
}
if it is too hard to read the code let me know, and if you don't want to read the code please help me to answer what is the difference between Handler and listening to events with the observer pattern? are they pretty much different solutions for same problem? thanks!
what is the difference between Handler and listening to events with the observer pattern?
The difference is that when you use a listener you call a method synchronously on the same thread. When you use a Handler you synchronously add a message to the MessageQueue but it is handled only after those messages that are already in the queue.
For example, if you are using a UI handler and you already called finish() on the activity and then added your message, it will be inserted after onStop() and onDestroy(). You can't achieve this with a listener.
The advantage of handlers is that you just add messages to queues and you don't care about threading. You can easily add a message to the UI handler from the background thread. If you use a listener from the background thread, it will be called on a background thread synchronously.
are they pretty much different solutions for same problem?
No, they are not. Handlers help you to decouple android components which is critical for Android, I think. If you use listeners you will be relying on strong references only which in some cases is not possible because you might leak a memory.
Handler is UI-threaded component. Usage of simple listener may cause CalledFromWrongThreadException if you want to touch some UI.
AsyncTask although have onPreExecute, onPostExecute and onProgressUpdate, which are just methods, which are running on UI thread. doInBackground runs on separate thread
I have an adapter what uses the activity context to register and unregister a listener.
Activity mActivity;
MyBroadcastReceiver mReceiver;
#Override
public void onAttachedToRecyclerView (RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mActivity.registerReceiver(mReceiver, ...);
}
#Override
public void onDetachedFromRecyclerView (RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
mActivity.unregisterReceiver(mReceiver);
mActivity = null;
}
Although the onAttachedToRecyclerView always gets called, the detach method never, so the adapter leaks a lot of memory even after closing the activity.(running is only noticeable in the Settings app)
What do I have to do?
To be safe, you could register the receiver in your Activity. You shouldn't hold on to an Activity reference anywhere really...
If you really want to register the receiver from your adapter use an interface.
public interface Registerer {
void register();
void unregister();
}
In Activity:
mRecyclerView.setAdapter(new RecyclerAdapter(someDataSet,
new Registerer() {
public void register() {
registerReceiver(mReceiver, ...);
}
public void unRegister() {
unregisterReceiver(mReceiver);
}
});
Then you can call the interface methods from your adapter. I don't really see the point of cramming this into your view adapter though.
My application crashes after click the button, but the code executes properly.
public void makeLead(View v) throws Exception {
try {
RegisterTimer rt = new RegisterTimer();
rt.ma = this;
rt.execute(null);
} catch (Exception e) {
e.printStackTrace();
}
}
public void log(String msg)
{
final TextView tv = (TextView)findViewById(R.id.editText);
tv.append(msg);
}
private class RegisterTimer extends AsyncTask {
public MainActivity ma;
#Override
protected Object doInBackground(Object[] objects) {
ma.log("ausd");
return null;
}
}
makeLead is onClick event. Method ma.log generates an error but works properly (msg added to textEdit). When I delete ma.log, app doesn't crash. I have no logs in my AndroidStudio so I can't see error message. What's wrong ?
You can not touch the Views in a non UI Thread.
and you are appending text to TextView in a background Thread which is not allowed.
and I hope there is no problem with the initialization of MainActivity inside RegisterTimer as you are not creating the instance of Activity manually. You are in correct way with the initialization rt.ma = this. and why do you need AsyncTask just for changing the text of a TextView?
You cannot update ui from a doInbackground. Initializing ma is not required
Make AsyncTask an inner class of Activity and update ui in onPostExecute
or use interface as a callback to the activity
Edit:
To make it clear
Make asynctaks an inner class of activity. Declare textview as a instance variable. Return result in doInbackground
In Activity
TextView tv;
#Override
public void onCreate(Bundle savedInstancestate)
super.onCreate(savedInstancestate);
setContentView(R.layout.yourlayout);
tv = (TextView)findViewById(R.id.editText);
Then
#Override
protected String doInBackground(Void objects) {
// some background computation
return "ausd";
}
In onpostExecute
#Override
protected void onPostExecute(String result)
{
super.onPostExecute();
tv.append(result);
}
Also you need
private class RegisterTimer extends AsyncTask<Void,Void,String> { // args missing
As described by #Raghunandan you have not initialized ma.
next is you cannot access view in background thread.
if your thread class is inside of MainActivity class then you can use
runOnUiThread(new Runnable() {
#Override
public void run() {
ma.log("ausd");
}
});
inside doInBackground method to update view.
Your method log is public, you don't need to make an object of the MainActivity class to access it, instead you can call it directly. Also you need to add some template after your ASYNC task, if you want to pass some input to your background process, you are using ASYNC task in a wrong way.