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.
Related
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.
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
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.
I am using the MaterialDrawer library to create a simple drawer for my app, some of the instances of classes in the library need a string passed into them when called. An example is the IProfile class:
IProfile profile = new ProfileDrawerItem().withName("John Doe");
where the withName() method takes in a string.
I have created a class MyObservable.java (extends Observable) class that I intend using to get data to be used in my MainActivity which has the MaterialDrawer library implemented. In this class, I have a method implementData() which has my listener for what I need from my firebase database.
This is what it looks like:
public class MyObservable extends Observable {
// Attach our firebase reference...
Firebase userRef = new Firebase("https://myApp.firebaseio.com/users");
AuthData authData;
public String username = "";
public String email = "";
public void changeUsername(String username) {
this.username = username;
setChanged();
notifyObservers(username);
}
public void implementData(){
// Get the authentication data of our user from the reference.
authData = userRef.getAuth();
// This is the listener I have to get the data from.
userRef.child(authData.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
UserSchema user = snapshot.getValue(UserSchema.class);
// This guy needs to be used in the MainActivity appDrawer() method
String userName = user.getName();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
}
class MyObserver implements Observer {
public void update(Observable observable, Object data) {
// For now I just print it out to System.out
System.out.println("Username:" + data);
}
}
Then I notify my observers of the username change with the changeUsername() method:
public void changeUsername(String username) {
this.username = username;
setChanged();
notifyObservers(username);
}
In the MyObservable class, I have a MyObserver class that implements Observer with the update() method called whenever an observer has been updated. In the update() method for now, I just print out the username of the user to ensure something is actually happening.
This is where I need the data from the observer (In the MainActivity):
public class MainActivity extends AppCompatActivity {
...
public void appDrawer(){
IProfile profile = new ProfileDrawerItem()
// I need the user's data here
.withName("Test")
.withEmail("test#test.com")
.withIcon(R.drawable.avatar2);
}
...
}
I have spent hours trying to 'react' to the events happening in the listener by trying to retrieve data to be used in my MainActivity but I'm not sure I'm using the Observable/Observer pattern properly, also since this is an Asynchronous event by Firebase to get data, using an Observer has been the best way to do this.
The appDrawer() method is called in my Activity's onCreate() .
How do I retrieve data from the Listener with an Observer, so I can use it elsewhere?
I can't tell by your design what's really going on. Naming a class Listener then making it Observable seems counter-intuitive. A listener listens or observes. Nonetheless, it sounds like you have another class in the Listener that's an Observer so I'm a little lost but you seem unsure if you've implemented the pattern correctly. That I can clear up with an example.
public class MyObserver implements Observer {
#Override
public void update(Observable o, Object arg) {
System.out.println(arg + " " + ((MyObservable)o).name);
}
}
public class MyObservable extends Observable {
public String name = "Observable";
public void changeMe(String word) {
setChanged();
notifyObservers(word);
}
}
public class Test {
public static void main(String[] args) {
MyObservable myObservable = new MyObservable();
myObservable.addObserver(new MyObserver());
myObservable.changeMe("Hello");
}
}
The update method gives you the object you're observing as well as the arguments (the data you want shared with the observer) you passed into notifyObservers(). If you've done it like this then you should get the behavior you expect.
As you can see data can be sent to the observers that is outside or inside the observable. When you run it the output is...
Hello Observable
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
}
}