loadermanager.loadercallbacks android studio - java

I am having trouble with LoaderCallbacks in my project. I have implements LoaderManager in android studio. I am trying to restart the loader whenever search button is press by user.
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Book>>
mSearchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Check connection status
checkConnection(cm);
if (isConnected) {
updateQueryUrl(mSearchViewField.getQuery().toString());
restartLoader();
Log.i(LOG_TAG, "Search value: " + mSearchViewField.getQuery().toString());
}else{
// Clear the adapter of previous book data
mAdapter.clear();
// Set mEmptyStateTextView visible
mEmptyStateTextView.setVisibility(View.VISIBLE);
// ...and display message: "No internet connection."
mEmptyStateTextView.setText("No Internet Connection");
}
}
});
But, under restartLoader(), when I try to call getLoaderManager() to restart the loader, it's saying that callback argument is wrong, 3rd argument type. I am not sure what should i use for the callback.
public void restartLoader() {
mEmptyStateTextView.setVisibility(GONE);
progressBar.setVisibility(View.VISIBLE);
getLoaderManager().restartLoader(BOOK_LOADER_ID,null, MainActivity.this);
}

getLoaderManager is deprecated and uses the deprecated framework Loaders.
You should use LoaderManager.getInstance(MainActivity.this) instead, which uses the correct Support Library/AndroidX Loaders, which is probably the LoaderManager.LoaderCallbacks you've imported.
LoaderManager.getInstance(MainActivity.this)
.restartLoader(BOOK_LOADER_ID, null, MainActivity.this);

Related

How to preserve the state of android activity

I have two activities my MainActivity and some other one called DetailActivity. When the app is first started it opens MainActivity and there it binds DataService and makes a call to fetch some data and populate a list view.
From that list view user has a button to open so called detail view for every item in the list. Opening that detail view means starting the second activity (DetailActivity).
Its done like this:
final Intent intent = new Intent(getContext(), DetailActivity.class);
intent.putExtra("data", dto);
getContext().startActivity(intent);
When second one is opened user is able to go back either by using back button (one left of home button on android) or by clicking back arrow in the header.
Everything works as expected except that when user comes back to MainActivity DataService is binded again and call to fetch the data is made and the list is updated. So if user is somewhere at item no. 205 he will be returned back to the start item.
Is there a way to hold the data or the state of MainActivity when user comes back to it that its not refreshed ?
Service is bonded like this
#Override
protected void onStart() {
super.onStart();
bind(DataService.class);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
private void bind(final Class... toBind) {
for (final Class clazz : toBind) {
bindService(new Intent(this, clazz), connection, BIND_AUTO_CREATE);
}
}
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
if (service instanceof DataService.LocalBinder) {
dataService = ((DataService.LocalBinder) service).getInstance();
dataService.readData();
}
}
#Override
public void onServiceDisconnected(final ComponentName name) {
// Empty By Default
}
};
You can achieve this by binding the service in onCreate() instead of onStart().
You should also have a look at the symmetry of your life cycle. Currently you are binding in start and unbinding in destroy. If you bind in onStart you should probably unbind in onStop. When you move the binding to onCreate you can keep the unbinding in onDestroy
Explanation: onCreate() is called when the activity is created first. onStart() is called every time your activity becomes visible.
Managed to solve it like this
#Override
public boolean onSupportNavigateUp() {
onBackPressed(); // one inherited from android.support.v4.app.FragmentActivity
return false;
}
Doing it this way seamed to do the trick. I would return to the place where I left from and there is no need to load any data since everything is already there

Sharing objects through the app using the Application class

I am trying to define global objects using the application class.
I therefore define the following class.
public class MyApplication extends Application {
private MyObject myObject=new MyObject();
public MyObject getMyObject(){
return this.myObject;
}
}
Then, I use it in an activity, but I get an error (Cannot resolve method getApplication()):
public class AnActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mood);
Button buttonMusic=(Button) findViewById(R.id.button5);
buttonMusic.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MyApplication myApplication = ((MyApplication)this.getApplication());
Toast.makeText(MoodActivity.this, "playing music", Toast.LENGTH_SHORT).show();
}
});
}
}
I have no clue why I get this error, as it for example works when calling the getApplication() in another activity.
I'm pretty new to Android and Java, so please excuse the ingenuity of the question.
UPDATE
Then I do MyObject myObject=myApplication.getMyObject(); and I don't get any compilation issue but the app dies as soon as I get in that activity.
As I understand it is not advised to use the Application class for such use, what would be a good alternative?
You're getting this error because you call this.getApplication() inside the View.OnClickListener. Because of this, this now references the listener and not the activity.
To do what you need, just create a Context object outside of the listener in your activity's onCreate method and assign this to it. And, inside the listener, use context instead of this. Something like this :-
public class AnActivity extends Activity {
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mood);
context = this;
Button buttonMusic=(Button) findViewById(R.id.button5);
buttonMusic.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MyApplication myApplication = ((MyApplication)context.getApplication());//Changed "the" to "context"
Toast.makeText(MoodActivity.this, "playing music", Toast.LENGTH_SHORT).show();
}
});
}
}
Edit after question update :-
Instead of using the application class, use static objects to achieve global variables. static objects are independent of objects and can be referenced using the class that they belong to. For example, if you define a static variable in your MainActivity.class and name it testObject, then it can be accessed using the following code regardless of your current activity :-
YourObject object = MainActivity.testObject;
Unless you have a specific reason for extending the Application class in Android you probably shouldn't. For reference, look at the note in the documentation for this: https://developer.android.com/reference/android/app/Application.html .
If you are trying to create an object that you can use in your Android app, simply do that as you would in Java:
public class MyObject {
//Your stuff here
}
If there is a reason that you're specifically wanting to extend the Application class then perhaps there's more that people can do to help you if you explain what you're trying to do. I just don't necessarily see a need to go through all that complexity based on your example :)
Change this to AnActivity.this.
Inside the code below the meaning of this changes from AnActivity to View.onClickListener as it is another object and inside those braces you are in the scope of the click listener class
new View.OnClickListener() {
public void onClick(View v) {
MyApplication myApplication = ((MyApplication)this.getApplication());
Toast.makeText(MoodActivity.this, "playing music", Toast.LENGTH_SHORT).show();
}
}
So the code above should become
new View.OnClickListener() {
public void onClick(View v) {
MyApplication myApplication = ((MyApplication)AnActivity.this.getApplication());
Toast.makeText(MoodActivity.this, "playing music", Toast.LENGTH_SHORT).show();
}
}
You can read a bit more about it here

Android fragments: AsyncTask IO error handling

I have a main activity which use view pager to display 3 tabs. In each tab fragment, I may call two AsyncTask classes to fetch data from the my web service. When the background task failed to connect to the web service, I would like to handle the ConnectException by closing the main activity(that has the 3 tabs) and redirect to errorActivity. What is the best way to do this?
I have set launchMode="singleTop" in the manifest file.
Currently, what I do for each AsyncTask is:
private class FetchDataTask extends AsyncTask<Void, Void, ArrayList<Person>> {
#Override
protected ArrayList<Person> doInBackground(Void... params) {
try {
return new Fetcher().fetchPeople();
} catch (IOException e) {
Log.e(TAG, "Failed to fetchPeople");
cancel(true);
return null;
}
}
#Override
protected void onPostExecute(ArrayList<Person> people) {
mPeople = people;
….
}
#Override
protected void onCancelled() {
super.onCancelled();
Intent i = new Intent(getActivity(), ErrorActivity.class);
i.putExtra(ErrorFragment.EXTRA_ERROR_MSG, ErrorFragment.MSG_UNEXPECTED_ERROR);
startActivity(i);
getActivity().finish();
}
}
But the problem I am facing is having to open multiple ErrorActivity considering that the first two tabs will be called upon start up.
Create a function in your activity called onConnectionFailed() and in all fragments when service call fails call getActivity() typecaste it into your activity and call onConnectionFailed().
You can also create an interface with this function, make you activity implement that function and provide the body for it, and finally pass the activity as the object of that interface to the fragments.
inside onConnectionFailed() just call finish()
EDIT 1:
onWebServiceCallFailed(){
if(!error){
Intent i = new Intent(this,
i.putExtra(ErrorFragment.EXTRA_ERROR_MSG, ErrorFragment.MSG_UNEXPECTED_ERROR);
startActivity(i);
this.finish();
error = true;
}
}
And inside activity create a boolean error = false

How do I setText() in an textView from a DialogFragment

I have a custom DialogFragment with a couple of options inside of it. The user is simply presented with a "Done" button in the Dialog to signal that they have completed their choice and to resume the activity. I have a textView on the activity below the dialog. My first thought was to use either a database (overkill) or sharedPreferences, but sharedPreferences is not an option in my specific case. So, my question is how do I setText on an textView from a DialogFragment with no sharedPreferences. Thanks
This can be easily done via Interface:
In your DialogFragment class:
public interface OnDoneClickListener {
void onDoneClicked() {}
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
mCallback = (OnDoneClickListener) activity;
} catch (ClassCastException e) {
Log.d("Error", "activity must implement OnDoneClickListener");
}
}
Now simply put mCallback.onDoneClicked() in your desire onClick event.
Back to your Activity which need to implement OnDoneClickListener,
#Override
public void onDoneClicked() {
tv.setText("Done Clicked!!");
}
If you're using an AlertDialog (simplest) then you just need to provide an onClickListener:
http://developer.android.com/reference/android/app/DialogFragment.html#AlertDialog
That page also has an example when using a more "standard" Fragment, basically call a method on the activity.

Mediation and CustomEventBanner java

I would like to create a CustomEventBanner but have some questions. Im not sure if I do the right things at the right place. Where should I add the Banner to my layout? Do I have to call every method of the CustomEventBannerListener? Which are those which are absolutly necessary? How do I know if there is no Ad to display (no anouncer)??
I actually can display Ad with admob but not using my CustomAd :(
Here is my code:
public class CustomAd implements CustomEventBanner, AdResponseHandler {
private CustomEventBannerListener bannerListener;
protected SASBannerView mBannerView;
#Override
public void requestBannerAd(final CustomEventBannerListener listener,
final Activity activity, String label, String serverParameter,
AdSize adSize, MediationAdRequest mediationAdRequest, Object extra) {
// Keep the custom event listener for use later.
this.bannerListener = listener;
// Determine the best ad format to use given the adSize. If the adSize
// isn't appropriate for any format, an ad will not fill.
// Create banner instance
mBannerView = new SASBannerView(activity);
// Set the listener to register for events.
this.mBannerView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
try {
listener.onClick();
} catch (Throwable t) {
}
}
});
// Load the ad with the ad request giving an AdResponseHandler
mBannerView.loadAd(42295, "286177", 18008, true, "", this);
}
#Override
public void destroy() { // The destroy method gets called when the mediation
// framework refreshes
// and removes the custom event. Perform any necessary cleanup here.
if (this.mBannerView != null) {
this.mBannerView.onDestroy();
}
}
#Override
public void adLoadingCompleted(SASAdElement arg0) {
this.bannerListener.onReceivedAd(this.mBannerView);
}
#Override
public void adLoadingFailed(Exception arg0) {
this.bannerListener.onFailedToReceiveAd();
}
}
The code looks pretty good. Though your banner doesn't seem to be doing anything on click other than notifying onClick(). If you're banner ends up hitting an external web browser or the play store, you can also call onPresentScreen() and onLeaveApplication() in the onClickListener.
Note that this is just the Custom Event component of your app to implement the SAS network. Your main activity still needs to create an AdView (with a mediation ID set up to target your custom event) and load an ad into it.
Only the onReceivedAd and onFailedToReceiveAd are absolutely necessary for mediation to run. The others are useful so that your main AdView's AdListener can listen for these events.

Categories