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;
}
});
Related
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.
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
Hello I have a problem with getter in my custom class, I don't know why it returns null every time. I'm setting value after response from server when it is without any errors. While I'm debugging, I see that response from server is OK and new instance of my object is created but when I try to get it in my activity there is a null. Here is couple lines of code where is a problem (in my opinion).
method from my custom class:
public void responseFromServer(){
showProgressDialog();
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
TitleInterface titleInterface = retrofit.create(TitleInterface.class);
Call<MovieResponse> call = titleInterface.getMovie(API_KEY,movie);
call.enqueue(new Callback<MovieResponse>() {
#Override
public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
List<Movie> movieList = response.body().getMovieList();
ItemAdapter itemAdapter = new ItemAdapter(context.getApplicationContext(),generateData(movieList));
setItemAdapter(itemAdapter);
}
#Override
public void onFailure(Call<MovieResponse> call, Throwable t) {
Toast.makeText(context, "ERROR", Toast.LENGTH_SHORT).show();
progressDialog.dismiss();
}
});
}
and here is my Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_list);
movieListView = (ListView) findViewById(R.id.movieListView);
String movie = getIntent().getStringExtra(TAG);
presenter = new Presenter(this,movie);
presenter.responseFromServer();
item=presenter.getItemAdapter();
movieListView.setAdapter(presenter.getItemAdapter());
presenter.getItemAdapter().notifyDataSetChanged();
presenter.getProgressDialog().dismiss();
}
Thanks for any help.
It seems like your request haven't finished when the view is created. If you want to make your call synchronous use this:
Call<MovieResponse> call = titleInterface.getMovie(API_KEY,movie);
Response<MovieResponse> responseBody = call.execute();
List<Movie> movieList = response.body().getMovieList();
You are using asynchronous calls in your app, so try to replace all necessary methods for updating UI from onCreate() to onResponse(). It should help. If you want to use Retrofit synchronous calls, the best practice for that is Loaders.
I have an android app that is retrieving data from a mysql db via php.
It works fine, but i have a (simple) variable problem.
I want to create a variable inside MainActivity class.
Then inside MainActiviy class i have onCreate method - and inside that I have some json stuff that retrieves my data from mysql.
I now want to assign some mysql value to the variable i created in MainActivity class (it is assigned inside onResponse method from the json stuff), and then I simply want to use that variable and write it out on a textview, and I will do that in the bottom of the onCreate method.
But for some reason, it "forgets" the value I assigned to the variable, when I have to use it outside the onResponse method.
(if i set the textview text inside the onResponse method, it works fine).
public class MainActivity extends ActionBarActivity {
// I create the variable here
String someString;
TextView text;
RequestQueue reqq;
String showUrl = "http://www.someurl.com/get_data.php";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.textid);
reqq = Volley.newRequestQueue(getApplicationContext());
JsonObjectRequest jsonob = new JsonObjectRequest(Request.Method.POST,
showUrl, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray dataAr = response.getJSONArray("json_data");
for (int i = 0; i < dataAr.length(); i++) {
JSONObject dat = dataAr.getJSONObject(i);
// Here I want to assign some data from my mysql db to the variable
someString = dat.getString("aar");
// If I set the the textview to display value of someString HERE, it works!
// text.setText(someString);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
});
reqq.add(jsonob);
// HERE i want to display the value of the variable in the textview!
// - but it doesnt "remember" the value
text.setText(someString);
}
}
If I use static keyword on the someString variable, it remembers the value, but only the SECOND time i open the app!
I'm very new at this, and have tried google, and tried some stuff with a singleton class, but I just don't seem to understand this!
I would love it, if someone could link me some information to help me get this, AND give an example of how my code should be, so it will work!
THANKS! :D
This behavior is due to the fact that
text.setText(someString);
is executed immediately in the onCreate method, & by immediately I mean that it does not wait for any response from the Volley request (the Volley request that you set up before). In other words, you need to wait till you get a response before you set the text on to your TextView.
That's why it successfully sets your TextView's text from within the onResponse method.
I have a XML file containing some data, so I created a class representing it :
public class MyData
{
ArrayList<SpecialData> list;
int currentPage, totalPages;
}
As you can guess I have a list of SpecialData items, each one containing many fields, and currentPage/totalPages are two unique values in the XML file. I need to get and parse the XML file asynchronously, so I created this class :
class GetXMLTask extends AsyncTask<String, Void, MyData>
{
#Override
protected MyData doInBackground(String... params)
{
MyData md = null;
// Getting/parsing data
return md;
}
}
I gave it a try and the problem doesn't come from here because I correctly parse my XML file and my MyData object is perfect. But then I use this task like this in my main Activity class :
MyData md = null;
GetXMLTask task = new GetXMLTask(this);
task.execute(new String[]{url});
// How can this change my md object?
This may be very silly but I simply don't know how to link my MyData instance from my main class to the one that I get with AsyncTask. What should I do? Thanks.
Override AsyncTask's onPostExecute method:
protected void onPostExecute(MyData result) {
md = result;
}
Note that this assumes your AsyncTask is an inner class to your activity. If that isn't the case, you can pass in a reference to your Activity in the constructor to your AsyncTask. In those cases, you should be careful to use a WeakReference to your Activity to prevent resource leaks:
GetXMLTask(MyActivity activity)
{
this.mActivity = new WeakReference<MyActivity>(activity);
}
protected void onPostExecute(MyData result)
{
MyActivity activity = this.mActivity.get();
if (activity == null) // Activity was destroyed due to orientation change, etc.
return;
activity.updateUiFromXml(result);
}
You probably want to implement a callback of some sort. This way you avoid exposing your data by making it publicly accessible, and you can implement other callbacks (such as an error callback if there is a problem loading the data).
For example, you could define an interface like this:
interface MyAsyncFinishedLister {
void onFinished(MyData resultData);
}
Your AsyncTask will have an instance of MyAsyncFinishedListener, and you can call in onPostExecute as so:
protected void onPostExecute(MyData result) {
myAsyncFinishedListener.onFinished(result);
}
Your main activity will implement this interface and look something like:
class MyActivity extends Activity implements MyAsyncFinishedListener {
MyData md;
public void onCreate() {
super.onCreate();
GetXMLTask task = new GetXMLTask(this);
task.execute(new String[]{url});
task.setOnFinishedListener(this);
}
onFinished(MyData result) {
md = result;
}
}
If you want an AsyncTask to return a data object, you need to store it in a variable in class scope, not function scope. To make this easy, the task is usually a private inner class.
Declare MyData as a variable visible to the whole class and try to access it in onPostExecute() by assigning the result to the MyData variable.