Volley static callback listeners context GCed - java

After using LeakCanary I found that there were many leaks in my app, most of them due to Volley's anonymous callback listeners. So I wrote a Util (below) class which uses static callbacks and WeakReference to keep reference to Context and an anonymous callback. But when I open the app for the first time, i.e. a cold start, the context is GCed soon after the request is made but during a warm start all works fine. Also this happens only for the first activity in the app.
Any alternative way of handling memory leaks with volley which works properly are also welcome.
public abstract class VUtil {
public static final String TAG = VUtil.class.getSimpleName();
public interface JsonCallback {
void onSuccess(JSONObject response);
}
public interface StringCallback {
void onSuccess(String response);
}
public interface ErrorCallback {
void onError(VolleyError error);
}
public static class JsonResponseListener implements Response.Listener<JSONObject> {
private final WeakReference<Context> mContextWeakReference;
private final WeakReference<JsonCallback> mCallbackWeakReference;
public JsonResponseListener(Context context, JsonCallback callback) {
mContextWeakReference = new WeakReference<>(context);
mCallbackWeakReference = new WeakReference<>(callback);
}
#Override
public void onResponse(JSONObject jsonObject) {
Context context = mContextWeakReference.get();
JsonCallback callback = mCallbackWeakReference.get();
if (context != null && callback != null) {
callback.onSuccess(jsonObject);
} else {
Log.d(TAG, "Context was GCed");
}
}
}
public static class StringResponseListener implements Response.Listener<String> {
private final WeakReference<Context> mContextWeakReference;
private final WeakReference<StringCallback> mCallbackWeakReference;
public StringResponseListener(Context context, StringCallback callback) {
mContextWeakReference = new WeakReference<>(context);
mCallbackWeakReference = new WeakReference<>(callback);
}
#Override
public void onResponse(String response) {
Context context = mContextWeakReference.get();
StringCallback callback = mCallbackWeakReference.get();
if (context != null && callback != null) {
callback.onSuccess(response);
} else {
Log.d(TAG, "Context was GCed");
}
}
}
public static class ErrorListener implements Response.ErrorListener {
private final WeakReference<Context> mContextWeakReference;
private final WeakReference<ErrorCallback> mCallbackWeakReference;
public ErrorListener(Context context, ErrorCallback callback) {
mContextWeakReference = new WeakReference<>(context);
mCallbackWeakReference = new WeakReference<>(callback);
}
#Override
public void onErrorResponse(VolleyError error) {
Context context = mContextWeakReference.get();
ErrorCallback callback = mCallbackWeakReference.get();
if (context != null && callback != null) {
callback.onError(error);
} else {
Log.d(TAG, "Context was GCed");
}
}
}
}

GC depends on many many things that are happening. One possible cause for your case is that when you do your first request after a 'cold boot' you app must init various custom objects, fragments, activities, views caches etc. and thus needs memory before increases the heap and thus do a GC.
The solution I propose however is to change your architecture.
1) it seems that u keep ref to context but it is never used. just drop it
2) you have Volley callbacks which delegates to your custom callbacks which you need to pass anyway, why don't you simply use 1 set of callbacks which you pass to the respective requests.
3) you WeekRef your custom callbacks but u cannot do without them. Week Referencing is not the ultimate solution to memory leaks. you have to find out why the ref is still there when you don't need it.
so if you leak issue is in JsonCallback, StringCallback and ErrorCallback implementations just try to figure this out instead of making the chain longer and cutting it at the end.

Thanks to djodjo's answer which helped me to reach a solution
Always use addToRequestQueue(request, TAG). Here TAG bit is what we'll use to cancel requests when their Activity/Fragment/View or anything is GCed
What i did is create a base activity and add all this request cancellation code in that activity. Here's what it looks like
public abstract class BaseActivity extends AppCompatActivity {
public final String tag;
public BaseActivity() {
super();
tag = getClass().getSimpleName();
}
#Override
protected void onDestroy() {
App.getInstance().cancelRequests(tag);
super.onDestroy();
}
protected <T> void addToRequestQueue(Request<T> request) {
App.getInstance().addToRequestQueue(request, tag);
}
}
cancelRequests is just simple code
getRequestQueue().cancelAll(tag);
Extend your activities from this BaseActivity and use addToRequestQueue to make requests, which will get cancelled automatically when your activity is destroyed. Do similar thing for fragment / dialog / whatever else.
If you make requests from anywhere else which doesn't follow a life-cycle, make sure that it's not binding to any Context and you'll be fine.

Related

ActivityPluginBinding is returning null

I am trying to run a AlertDialog in my flutter plugin. So I need the Activity context. I tried using the Application context. However, I was greeted with this fine error and learned that I must use the Activity context.
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
For some reason when I call getActivity() it always returns null. I was wondering if anyone could give me some pointers as to why this is happening. Here is my Plugin class I cleaned it up so it only contains the ActivityAware code. Did I not implement something correctly? Any help would be much appreciated!
public class MyPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
private ActivityPluginBinding activityBinding;
private FlutterPluginBinding flutterBinding;
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
flutterBinding = flutterPluginBinding;
}
#Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
activityBinding = binding;
}
#Override
public void onDetachedFromActivity() {
activityBinding = null;
}
#Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
activityBinding = binding;
}
#Override
public void onDetachedFromActivityForConfigChanges() {
activityBinding = null;
}
// Implementation
public Context getApplicationContext() {
return (flutterBinding != null) ? flutterBinding.getApplicationContext() : null;
}
public Activity getActivity() {
return (activityBinding != null) ? activityBinding.getActivity() : null;
}
}
Those objects might have cleared by time they are accessed. You can keep WeakReference to Application context and Activity context and access them later. This way you will avoid memory leak as well.
Something like
private WeakReference<Context> weakApplicationContext;
private WeakReference<Activity> weakActivity;
and then
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
weakApplicationContext = new WeakReference<>(flutterPluginBinding.getApplicationContext());
}
#Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
weakActivit = new WeakReference<>(binding.getActivity());
}
Then access them as
public Activity getActivity() {
return weakActivity.get();
}

How to implement .aidl file in flutter

I am trying to use flutter to communicate with SunMi mobile printer. I want to invoke the printer using AIDL as one of the ways to communicate with the printer but i don't know how and where to place the AIDL files in flutter or is it even possible to use flutter in my case. I need to know if it is possible to communicate with the printer using its AIDL. I am opting to use flutter or android studio with java for my application.
Source of question : https://github.com/flutter/flutter/issues/49413#issue-554566892
i could not find the proper answer so posted the question here.
Since this is an AIDL file we're talking about, it would be safe to assume that this is an Android only feature.
To do this, like any other Android-specific MethodChannel implementation, you'll need to create a MethodCallHandler and/or an StreamHandler (depending if you want to do streams or just an command -> result approach), register it on the Main FlutterActivity, and call it from dart through MethodChannels.
DISCLAIMER
The code below is not tested and might have some syntax errors, let me know if you find issues and I'll address them.
I have combined everything in one file for brevity, you can move some parts of the code to separate files as you see fit.
Creating the MethodCallHandler/StreamHandler
Inside your android/app/src/main/com/some/path folder in your flutter app, create a new java file and implement ServiceConnection, MethodChannel.MethodCallHandler and/or EventChannel.StreamHandler:
public class PrinterPlugin implements MethodChannel.MethodCallHandler, EventChannel.StreamHandler, ServiceConnection {
public static final CHANNEL = "com.some.path/printer";
public static final EVENT_CHANNEL = "com.some.path/printer-events";
private Context context;
private IPrinterService printerService = null; // where IPrinterService would be the AIDL's name
public EventChannel.EventSink eventSink = null;
public PrinterPlugin(Context context) {
this.context = context;
if (printerService == null) {
// these strings should be in your documentation or you can find these values from the package manager
Intent intent = new Intent("com.your.printer.service");
intent.setPackage("com.whatever.aidl");
context.bindService(intent, this, Context.BIND_AUTO_CREATE);
}
}
public void disconnect() {
context.unbindService(this);
}
// streamhandler implementation
#Override
public void onListen(Object arguments, EventChannel.EventSink events) {
this.eventSink = events;
}
#Override
public void onCancel(Object arguments) {
this.eventSink = null;
}
// /streamhandler implementation
// methodcallhandler implementation
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
try {
switch (call.method) {
case "initialize": printerService.printerInit(); break;
case "print-text": printerService.printText(call.argument("data")); break;
// implement other aidl methods
}
} catch (RemoteException e) {
result.error("", ex.getMessage(), null);
}
}
// /methodcallhandler implementation
// serviceConnection implementation
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
printerService = IPrinterService .Stub.asInterface(service);
if (eventSink != null) {
eventSink.success("Printer Connected");
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
printerService = null;
if (eventSink != null) {
eventSink.success("Printer Disconnected");
}
}
// /serviceConnection implementation
}
Register the PrinterPlugin to the MainActivity as a MethodChannel and EventChannel
Now that that's out of the way, you'll need to register this plugin on the MainActivity's configureFlutterEngine method:
public class MainActivity extends FlutterActivity {
private PrinterPlugin printerPlugin;
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
Context context = getContext();
printerPlugin = new PrinterPlugin(context);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), PrinterPlugin.CHANNEL)
.setMethodCallHandler(printerPlugin);
new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), PrinterPlugin.EVENT_CHANNEL)
.setStreamHandler(printerPlugin);
}
#Override
protected void onDestroy() {
super.onDestroy();
this.printerPlugin.unbindService();
}
}
Call the methods from dart
Now the last thing that you need to do is call these from dart.
const MethodChannel _channel = MethodChannel('com.some.path/printer'); // should match the CHANNEL constant on the java side
const EventChannel _evntChannel = EventChannel('com.some.path/printer-events'); // should match the EVENT_CHANNEL constant on the java side
class PrinterPlugin {
static Stream<dynamic> _printerStream = _eventChannel.receiveBroadcastStream();
Stream<String> status$;
PrinterPlugin() {
status$ = _printerStream;
}
static Future printText(String data) async {
await _channel.invokeMethod('initialize');
await _channel.invokeMethod('print-text', 'Foo Bar');
}
}
Well, that was a lot of code - but that's basically how I did it. Just a MethodCallHandler with a ServiceConnection implementation.
You can go crazy with this implementation as well, like real-time printing progress, or getting streamed printer status, etc.
Let me know if it works for your needs.

RxJava2 success always called twice

I'm implementing an MVP architecture with Retrofit, and RxJava2 (with RxAndroid). I have the following setup
NetworkInterface.java where retrofit calls lie
public interface NetworkInterface {
#GET("/input")
Single<List<InformationData>> getInfoData();
}
InformationDataDao.java Data access layer for the information data
#Dao
public interface InformationDataDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
Completable createInfoData(List<InformationData> infoData);
}
MainPresenter.java where the calls are being made public class MainPresenter {
public MainPresenter() {
}
// Method called to initialize the subscription
#Override
public void subscribe() {
this.collectInfoData();
}
private void collectInfoData() {
Single<List<InformationData>> singleInfoData = networkInterface.getInfoData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
singleInfoData.subscribe(informationDatas -> {
InformationDataDao informationDataDao = database.informationDataDao();
informationDataDao.createInfoData(informationDatas);
// What made me detect that this was being called twice
Log.d(Constant.LOG_TAG, "Info Data: " + informationDatas.size());
// Rest of the code here
}, Throwable::printStackTrace);
}
}
SplashActivity.java The method that calls the presenter
public class SplashActivity extends AppCompatActivity {
// Helps in getting the location
private FusedLocationProviderClient locationClient;
private SplashPresenter mPresenter;
#Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_splash);
mPresenter = new SplashPresenter();
}
#Override
protected void onResume() {
super.onResume();
// These three lines ensure that you have the appropriate permissions to proceed. In the third line for instance, the user will be navigated out to the settings pane to turn on location.
// When he/she returns to the activity, the onResume is called so I'm assuming that's the best place to start the presenter
this.ensureHasPermissions();
boolean isGoogleServiceAvailable = this.ensureGoogleServiceAvailability();
boolean isLocationServiceAvailable = this.ensureLocationServiceAvailable();
if (isLocationServiceAvailable && isGoogleServiceAvailable) {
locationClient.getLastLocation()
.addOnSuccessListener(this, location -> {
// Set the closest location and subscribe
presenter.setClosestLocation(location);
presenter.subscribe();
})
.addOnFailureListener(this, Throwable::printStackTrace);
}
}
}
From the SplashPresenter.java, the log is printed twice, indicating that the complete callback was called twice. Is there any idea why this may be so? I'm quite new to RxJava2 and will greatly appreciate the feedback.
Thanks.

How can i return variable "realImage" which is within onComplete method?

I've created an Asynctask in an activity and now i want to return the variable "realimage" to that Asynctask but i cant seem to access it...
public class PhotoUtils
{
public static Photo getImage(String id)
{
unsplash.getPhoto(id, new Unsplash.OnPhotoLoadedListener()
{
#Override
public void onComplete(Photo photo)
{
Photo realImage=photo;
}
#Override
public void onError(String error)
{
}
});
return realImage; //This line shows error that cannot resolve symbol
realImage
}
}
This is my Async Task which is in the other activity
public class ImageTask extends AsyncTask<Photo,Void,Photo>
{
#Override
protected Photo doInBackground(Photo... photos)
{
Intent intent=getIntent();
Bundle bd=intent.getExtras();
String getId = (String) bd.get("id");
Photo finalPhoto=PhotoUtils.getImage(getId);
return finalPhoto;
}
#Override
protected void onPostExecute(Photo finalPhoto) {
tv1.setText(finalPhoto.getId());
super.onPostExecute(finalPhoto);
}
}
First, looks like you don't even need an Asynctask. Look at the example code
Secondly, you can't return it like that.
OnPhotoLoadedListener is an asynchronous callback.
Just use the image as normal within onComplete.
If you needed to pass back the image to the calling method, extract the callback to a parameter.
// this is void now
public static void getImage(String id, Unsplash.OnPhotoLoadedListener listener) {
unsplash.getPhoto(id, listener);
});
Call this as
PhotoUtils.getImage(id, new Unsplash.OnPhotoLoadedListener() {
// Use image in here
});
Which, if you look closely, all you did was replace normal usage of unsplash.getPhoto with PhotoUtils.getImage, which may be want you want, but you could accomplish a similar approach with a singleton pattern around the unsplash instance variable. That way, you're not rewriting all of Unsplash API calls

Android Async http requests, should I make three different classes?

I'm a beginner in Java and coding for Android. When I was coding an app which makes some network requests, I got NetworkOnMainThreadException exception. I googled and found out the reason and solution. But I still have a question. My app makes requests on different sites and will does different actions with responses (first response on login action, it will be checked, second action is some api calls, third action is requesting images), I think I should not make three different classes for each case (doInBackground is the same, but different onPostExecute methods will be here). How can I fix this problem? Thanks
You can pass an aditional variable as doInBackground Params, save it as "global" class variable and switch in onPostExecute so u dont have to make 3 different classes
Like this
public class Async_DL extends AsyncTask<String, String, String> {
String type_of_request;
String url;
#Override
protected void onPreExecute(){
}
#Override
protected String doInBackground(String... params) {
this.url = params[0];
this.type_of_request = params[1];
doinBackground stuff....
}
#Override
protected void onPostExecute(String result) {
switch(this.type_of_request){
case a:
do some stuff
break;
}
}
}
One solution would be to create an interface callback like this
interface Callback {
public void call (String response); //or whatever return type you want
}
Then you might extends your AsyncTask class like this
private class HttpTask extends AsyncTask <Void,Void,String> {
Callback callback;
HttpTask (Callback callback) {
this.callback = callback;
}
// your doInBackground method
#Override
protected void onPostExecute (String response) {
callback.call(response);
}
}
Then you might call your AsyncTask like this
new HttpTask (new Callback () {
#Override
public void call (String response) { /* Here goes implementation */ }
}).execute();
You dont need to make three separate classes for each action. You can extend only once the AsyncTask and i would suggest to add an interface call which can be implemented by your activity:
public interface RequestListener {
public void onLoginCall();
public void onApiCall();
public void onImageCall();
}
At the same time create an enumeration to hold the requests' types:
public enum RequestType{
LOGIN,
API,
IMAGE;
}
Meanwhile you can extend the AsyncTask and call the necessary listener's methods per each case. You can use the second attribute to hold the type of the request:
public class RequestTask extends AsyncTask<Object, RequestType, Object> {
private RequestListener listener;
#Override
protected void onPreExecute(){
}
#Override
protected String doInBackground(Object... params) {
this.url = params[0];
this.type_of_request = params[1];
doinBackground stuff....
}
#Override
protected void onPostExecute(Object result) {
if(result is from login)
listener.onLoginCall();
... and so on
}
}

Categories