Change TextView text from another class using interface - java

I'm trying to make some utils functions to use in a bigger app later(download file from url, upload file to url etc)
So in MainActivity I have only 2 buttons that on click call static methods from Utils class.
However, I want on MainActivity to have some indicators of how things working on download/upload methods(connecting, connection success/fail, percent of download etc) so I put on MainActivity a TextView that will show that. I made an interface ICallback that contains void setConnectionStatus(String status) and from Utils class I use this to send to MainActivity the status.
Here are some parts of the code :
public class MainActivity extends AppCompatActivity implements ICallback {
Button btnDownloadDB, btnUploadDB, btnUploadPics;
TextView txtStatus;
ProgressBar pb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initialize stuffs
initViews();
//Setting listeners
btnDownloadDB.setOnClickListener(v -> {
txtStatus.setText(R.string.connecting);
pb.setVisibility(View.VISIBLE);
Utils.downloadFile(DOWNLOAD_DB, DB_FILE_NAME);
});
}
#Override
public void setConnectionStatus(String status) {
Log.d("MIHAI", status);
txtStatus.setText(status);
}
The interface :
public interface ICallback {
void setConnectionStatus(String status); }
And the Utils class :
public class Utils {
static ICallback callback= new MainActivity();
public static void downloadFile(String downloadURL, String fileName) {
IFileTransferClient client = ServiceGenerator.createService(IFileTransferClient.class);
Call<ResponseBody> responseBodyCall = client.downloadFile(downloadURL);
responseBodyCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("MIHAI", "connection ok");
callback.setConnectionStatus("Connection successful");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("MIHAI", "err...fail");
callback.setConnectionStatus("Connection failed. Check internet connection.");
}
});
}
The problem appear on MainActivity, when I try to set text of the txtStatus TextView getting a null reference error even if the txtStatus is initialized on initViews() method.
The Logs are working fine so I get the right status in MainActivity. I tried to initialize the TextView again in that function before seting the text and im getting : "java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:183)"
Is there any chance to make this work?
Thank you for reading.
Kind regards,
Mihai

There are multiple problems with your solution but the main one is this line:
static ICallback callback= new MainActivity();
First of all, never hold a static reference to Activity, Fragment, Context or any Context related classes. These classes are either bound to a Context or represent the Context itself. You may leak memory this way. But that is the other problem.
What is the actual problem in your code is that new MainActivity() in Utils class creates an absolutely different instance of MainActivity that has nothing to do with MainActivity that is responsible for displaying your UI in the runtime.
What you should do instead is pass an instance of ICallback to the function as an argument:
public static void downloadFile(String downloadURL, String fileName, ICallback callback) {
...
}
And remove static ICallback callback= new MainActivity();.
Note: when you pass a callback object to a function make sure when it is called your Activity is not in a finished state.

Related

null object after JSON data request

I am writing a code where I request JSON Object from url using Volley library. In my MainActivity I need to use this JSON Object so I need to wait for the response to be ready before going on with the code. For that, I implemented an interface with a method ProcessFinished that I define in my MainActivity and an AppController. Even with that, I still have an error when trying to call the requested Object in my Main Activity. Here's an extract of the used code :
public class GetData{
public String loadAllData(final DataAsyncResponse callBack){
AppController.getInstance().addToRequestQueue(jsonObjectRequest);
return mydata;}
public interface DataAsyncResponse {
void processFinished(String mydata);
}
In my ActivityMain :
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data = new GetData().loadAllData(new DataAsyncResponse() {
#Override
public void processFinished(String mydata) {
}
});
You need to set the value for data within the processFinished() callback. The way you're setting it now won't work because the value isn't ready yet at that point. Although it's hard to tell because you haven't included all the code, something like this should work:
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new GetData().loadAllData(new DataAsyncResponse() {
#Override
public void processFinished(String mydata) {
data = myData;
}
});

possible alternative to static inner classes to prevent memory leaks in android/java?

lately i have been researching about memory leaks in java/android and pretty much everywhere it says that instead of anonymous classes i should use static inner classes with weak references.
so, in my android app i started doing that but very quickly got tired of it because it's a lot of boilerplate code... i think have an alternative solution which i would prefer to use, but i'm juts not sure that it is a valid alternative to static inner classes in terms of preventing memory leaks. as i said before, i haven't seen this solution suggested anywhere else (all say to use static inner classes) so thats why im not sure my alternative will work.
ill use a simple example from my app:
i have a class called WebClient which handles asynchronous web requests and it accepts an interface called iCallback which returns the response from the server to the caller, and in my activity once i get this callback i need to dismiss a dialog, and maybe perform some activity related things (like trigger onBackPressed() and setResult()).
so here is my static inner class i have created:
private static class CallBack implements WebClient.ICallback
{
private WeakReference<ProgressDialog> mProgDiag;
private WeakReference<BaseActivity> mActivity;
public CallBack(BaseActivity activity, ProgressDialog progDiag)
{
this.mProgDiag = new WeakReference<>(progDiag);
this.mActivity = new WeakReference<>(activity);
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
final BaseActivity parentActivity = mActivity.get();
ProgressDialog dialog = mProgDiag.get();
if(dialog != null)
{
dialog.dismiss();
}
if (responseAsString == null)
{
if(parentActivity != null)
{
Utils.makeServerErrorDialog(parentActivity,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
parentActivity.onBackPressed();
}
});
}
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
if(parentActivity != null)
{
Intent result = new Intent();
result.putExtra(...);
parentActivity.setResult(Activity.RESULT_OK, result);
}
}
else
{
Utils.reportErrorToServer(...);
if(parentActivity != null)
{
parentActivity.setResult(Activity.RESULT_CANCELED);
}
}
if(parentActivity != null)
{
parentActivity.onBackPressed();
}
}
}
so for every variable i need in this static inner class i have to create a new weak reference, then retrieve the object itself, and then every time i want to access it i need to check whether it's null... that seems like a lot of code to me.
and here is my suggested alternative:
public abstract class BaseActivity extends AppCompatActivity
implements WebClient.ICallback
{
private static final String TAG = "BaseActivity";
WebClient.ICallback mCallBack;
ProgressDialog mProgDiag;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mCallBack = this;
//some code to invoke a server request on button click
//and passing mCallBack to the request
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
mProgDiag.dismiss();
if (responseAsString == null)
{
Utils.makeServerErrorDialog(this,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
onBackPressed();
}
});
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
Intent result = new Intent();
result.putExtra(...);
setResult(Activity.RESULT_OK, result);
}
else
{
Utils.reportErrorToServer(...);
setResult(Activity.RESULT_CANCELED);
}
onBackPressed();
}
#Override
protected void onPause()
{
mCallBack = null;
super.onPause();
}
#Override
protected void onResume()
{
super.onResume();
mCallBack = this;
}
}
to me this seems much cleaner: no creating and retrieving instances of weak references for every variable i need access to, i can directly invoke activity methods (e.g. onBackPressed()), and no checking for null everywhere.
the only place i would now have to check for null is inside WebClient class before invoking the callBack method.
so my question is, does this approach achieve the same result in terms of preventing memory leaks? is it a "worthy" alternative to static inner classes?
Unfortunately, your approach does not work. By implementing the WebClient.ICallback in your activity, rather than an inner class, you don't get rid of the leak. The leak happens not because the references to activity and dialog are implicit in an anonymous class, or in lambda, or in a non-static inner class instance; the happens when the WebClient keeps this reference while the activity is gone (it is not destroyed, because there is a strong reference to it).
The special mCallBack that you set to null when the activity is paused, gains nothing. Just as well, you can simply pass your activity instance to the WebClient. Now there is a strong reference to your activity, which is managed by someone (async handlers of the WebClient), who is not under your control. If you are unlucky, the async handler will get stuck somewhere and will never release this reference.
Please read this detailed explanation.
Note that WebView itself can cause a memory leak, if special measures are not undertaken!

Getting a value from parent activity returns null

I have an activity and a fragment working with.
In the activity, i'm initializing the current user like this(manually):
User currentUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
currentUser = new User("email", "name", "secondName", "age");
\\.....}
the MainActivity contains a method like this :
public User getCurrentUser() {
return currentUser;
}
now, here is the problem :
in the child fragment i'm calling getCurrentUser from mainActivity like this :
User u = MainActivity.newInstance().getCurrentUser();
MainActivity.newInstance() - defined in mainActivity like this, returning a static instance of activity :
private static MainActivity mainActivityInstance = new MainActivity();
public static MainActivity newInstance() {
return mainActivityInstance;
}
the problem is that getCurrentUser returns a null object reference even though currentUser is initialized in mainActivity with valid data.
So, calling for example
u.getName();
will throw me a NullPointerException
How can i solve this? i feel like i'm freaking out
Thanks.
MainActivity.newInstance() - defined in mainActivity like this, returning a static instance of activity :
Never create an instance of an activity directly yourself.
the problem is that getCurrentUser returns a null object reference even though currentUser is initialized in mainActivity with valid data
Those activity instances are separate Java objects.
From a fragment, call getActivity() to retrieve the activity object that is hosting the fragment. This is covered in any good book or course on Android app development.
As many people said: "You can't instantiate an Activity in a Fragment".
Second: there are many options you can implement in order to get the User in a Fragment. Here are the common options:
1 - Pass the user as a parameter in the fragment:
public static FragmentChild newInstance(User user){
Bundle b = new Bundle();
b.putSerializable("USER_PARAM",user);
FragmentChild fragment = new FragmentChild();
fragment.putArguments(b);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
User user = getArguments().getSerializable("USER_PARAM")
}
public class User implements Serializable {}
2 - Use the method ((MainActivity)getActivity()).getCurrentUser(). It will work but if you want to do it in the right way you should use an interface instead of using the MainActivity directly.
Interface option:
public class MainActivity extends AppCompatActivity implements UserDeliver {
#Override
public User getUser(){
return user;
}
}
public class MyFragment extends Fragment {
private UserDeliver userDeliver;
private User user;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
user = userDeliver.getUser();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
userDeliver = (UserDeliver) context;
}
#Override
public void onDetach() {
super.onDetach();
userDeliver = null;
}
public interface UserDeliver {
User getUser();
}
}
First off- you can't create an Activity via new. It will compile, but not be properly initialized because only the framework can do that.
Secondly- never hold an activity in a static variable. This will leak memory. Lots of memory, because the entire view hierarchy has a reference from the Activity. You will cause OOM errors doing this.
U are generating a newInstance of the MainActivity everytime you do MainActivity.newInstance()
Try to retrieve your user with ((MainActivity)getActivity).getCurrentUser();
Or create the instance of your user in the fragment

Android Bug or Java Bug : When execute callback find the wrong instance

Recently I use MVP to develop an Android App.
But I find an Android Bug(maybe I am wrong)
I have two presenter NewsPresenter and MainPresenter. NewsPresenter is created an instance in a Fragment NewsFragment,and MainPresenter is created an instance in an Activity MainActivty.Of course MainActivity include NewsFragment.Both two presenter have two method to implements which to handle the success response and the fail response.
NewsPresenter
public class NewsPresenter extends IPresenter implements IAdapter.OnRecycleItemClickListener{
#Override
protected void onIRequestSuccess(int requestId, IResponse response) {
Log.e("TAG","onIRequestSuccess:"+requestId);
....
}
#Override
protected void onIRequestFail(int requestId, Throwable throwable) {
Log.e("TAG","onIRequestFail:"+requestId);
...
}
}
MainPresenter
public class MainPresenter extends IPresenter{
public MainPresenter(Context context, IMain iMain) {
super(context);
}
#Override
protected void onIRequestSuccess(int requestId, IResponse response) {
//do nothing
}
#Override
protected void onIRequestFail(int requestId, Throwable throwable) {
//do nothing
}
}
Now,in the NewsPresenter I try to make a network request.In the BasePresenter which is its super class I make a log to show the class which try to execute the network request.And the log is that:
01-02 20:09:28.281 17206-17206/com.chengtao.culture E/BasePresenter: class com.chengtao.culture.presenter.NewsPresenter
this mean NewsPresenter execute the request.
But in the IPresente which is NewsPresenter and MainPresenter super class,I try to make a log to show the class which handle the response.And the log is that:
01-02 20:09:38.352 17206-17206/com.chengtao.culture E/TAG: class com.chengtao.culture.presenter.MainPresenter
This mains that the MainPresenter handle the response.
IPresenter
abstract class IPresenter extends BasePresenter{
IPresenter(Context context) {
super(context);
}
#Override
public void onRequestSuccess(int requestId, BaseResponse response) {
Log.e("TAG",this.getClass().toString());
IResponse response1 = (IResponse) response;
Log.e("TAG","onRequestSuccess");
onIRequestSuccess(requestId,response1);
}
#Override
public void onRequestFail(int requestId, Throwable throwable) {
Log.e("TAG",this.getClass().toString());
if (throwable == null || throwable.getMessage() == null){
throwable = new Throwable("请求超时");
onIRequestFail(requestId,throwable);
}else {
onIRequestFail(requestId,throwable);
}
}
protected abstract void onIRequestSuccess(int requestId, IResponse response);
protected abstract void onIRequestFail(int requestId, Throwable throwable);
}
I am so confused why I use NewsPresenter to make the request,but MainPresenter handle the response?It's not scientific,because the log show that the request is NewsPresenter execute,MainPresenter has no relationship with NewsPresenter just both of them extends IPresenter.
But once I delete the instance of MainPresenter in the Activity,the response will be handled by the NewsPresenter.
To see the whole code: https://github.com/ParadiseHell/cultural-creative/tree/master/app/Culture
I make sure my code is all right,So I don't know is Android bug or it's Java bug.If someone knows,please tell me, thanks.
Firstly your Mvp architecture is wrong. List and Adapter are related to UI, so adapter logic must be with fragment not with the presenter.
You should create something other called "Service Class" which will extend the BaseService in which the real API call is sent.
Every API call must have each separate Service Class.We should set the callback method to that serviceclass object so that when the API call gets executed the callback is received for that particular function.
Sample code which explains the above paragraph.
class GetNewsService extends BaseService {
void doApiCall(){
// execute API call . Put the request related code in BaseService.
}
}
class NewsPresenter {
// I am writing psudeo code only.
void getNews(){
GetNewsService newsService=new GetNewsService();
newsService.setCallBack(new RequestClass(){
#Override
onSuccessMethod(Response response){
// send response object to view class through interface and update adapter there
}
#Override
onFailMethod(Error error){}
});
newsService.doApiCall();
}
}
First I have to say,I use MVP right,just because my library has a bug,and I have fixed it.
In the library,there is a class AsyncHttpClient which is singleton pattern,of course it's wrong,because I can init only one response interface in the whole project,but every presenter has a response interface,so it's bad for handling the response.So I delete singleton pattern,use a public constructor,and the problem disappears.
So it's not Android bug or Java bug,just my fault for thinking less.

Using getIdentifier() to Find ID of Image

I am trying to get id of an image by using following code.
public class MovieActivity extends ActionBarActivity {
private Context con;
String name = "test";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
con = MovieActivity.this;
setContentView(R.layout.activity_movie);
}
public void updateScreen(){
int imageResource = con.getResources().getIdentifier("drawable/" + name , null, con.getPackageName());
}
}
When I run it, I get exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
I thought the problem was Context so I added con variable which gets context when code runs. I made some researches and tried another methods but I got same exception every time. Can anyone help me?
EDIT: When I use the same code line in MainActivity, it works perfectly. But in another class, it fails.
public class SomeMovie extends MovieActivity { }
public class MainActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
SomeMovie movie = new SomeMovie();
movie.updateScreen();
}
}
SomeMovie class is child of MovieActivity class. I call the method on that. I debugged the code and noticed that Context is null when code gets into updateScreen() method. Is it wrong to use inheritance on activity classes?
Change
int imageResource = con.getResources().getIdentifier("drawable/" + name , null, con.getPackageName());
to
int imageResource = getResources().getIdentifier("drawable/" + name , null, con.getPackageName());
You are already in an Activity's context, so use it. (No need to use the this keyword to refer to it)
EDIT:
You are not defining a layout for the SomeMovie Activity, so its context is always null.
You have to define the activity's layout in its onCreate method :
public class SomeMovie extends MovieActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.somemoviexml);
...
}
}

Categories