Releasing instance of Fragment without UI when setRetainInstance(true) - android

I need to add a row to my SQLiteDatabase and as a result i want to obtain newly created ID of the row. I extended AsyncTask class in order to run this task in the background thread, here it is:
private class InsertLangTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
if (mDB.isOpen()) {
try {
mId = mDB.insertOrThrow(DBS.D.TN, null, mCV);
}
catch (SQLException e) {
StringBuilder query = new StringBuilder();
query.append("SELECT " + BaseColumns._ID + " FROM " + DBS.D.TN + " WHERE ");
Object[] bindArgs = new Object[mCV.size()];
int i = 0;
for (Map.Entry<String, Object> entry: mCV.valueSet()) {
query.append((i > 0) ? " AND " : "");
query.append(entry.getKey());
query.append(" = ?");
bindArgs[i++] = entry.getValue();
}
SQLiteStatement statement = mDB.compileStatement(query.toString());
for (i = 0; i < bindArgs.length; i++) {
DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
}
try {
mId = statement.simpleQueryForLong();
} finally {
statement.close();
}
}
}
}
catch (NullPointerException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (mActivity != null) { mActivity.onLangInserted(mId); }
super.onPostExecute(result);
}
}
This InsertLangTask class is enclosed in Fragment class where i implements only onCreate(), onAttach() and onDetach(), also i create a method inside to initialize members (cannot do it in constructor, as it must be empty in Fragment classes). I wrap AsyncTask in this Fragment class in order to preserve my mId member and pass it back to my activity even if the configuration was changed and activity was destroyed. I know that it's not exactly the case of long-term operation, but it can possibly be. Here are the parts of this class:
public class InsertLangFragment extends Fragment {
public static interface OnLangInsertedListener {
void onLangInserted(long id);
}
SQLiteDatabase mDB = null;
ContentValues mCV = null;
private OnLangInsertedListener mActivity = null;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
public void RunTask(SQLiteDatabase db, ContentValues cv) {
new InsertLangTask().execute();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
}
And in my Activity i just write this when i want to start this process:
InsertLangFragment fragment = new InsertLangFragment();
getFragmentManager().beginTransaction().add(fragment, "insertTaskFragment").commit();
fragment.RunTask(mSQLiteDatabase, mContentValues);
Obtaining mId in implemented callback
public void onLangInserted(long id) {
}
I used setRetainInstance(true) so this fragment will never be destroyed (except back button) and will be located in memory all time. The question is how should i properly destroy fragment instance? I suppose that i have three possible outcomes:
1) It's not necessary to destroy this Fragments.
2) I should put getFragmentManager().remove("insertTaskFragment").commit() at the end of onLangInserted() in my Activity
3) I should put setRetainInstance(false) at the end of onPostExecute() in AsyncTask.
Please give me an advice how should i manage this Fragment properly.
Thank you. Please don't be too strict, i'm a newbie here.

Related

Get data from server using of AsyncTask

In my program a number of values are stored on the server.
I read these data using of the AsyncTask class from the server.
This is my code:
public class TestActivity extends AppCompatActivity {
private static List<String> mPackName;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPackName = new ArrayList<>();
try {
boolean check = new GetDataAsyncTask().execute("1234567").get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
e.printStackTrace();
e.printStackTrace();
}
}
private class GetDataAsyncTask extends AsyncTask<String, Void, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
final String mCode = params[0];
APIGettingPosts apiGettingPosts = new APIGettingPosts(TestActivity.this, "get_off_code.php");
apiGettingPosts.getOffCode(new APIGettingPosts.OnOffCodeReceived() {
#Override
public void onReceived(List<Posts> posts) {
if (!(posts == null || posts.isEmpty()))
for (int i = 0; i < posts.size(); ++i) {
mPackName.add(posts.get(i).getTitle());
Log.e("mPackName["+String.valueOf(i)+"]",mPackName.get(i));
}
}
});
Log.e("Size of mPackName: ", String.valueOf(mPackName.size()));
for (int i = 0; i < mPackName.size(); ++i)
if (mCode.equals(mPackName.get(i))) {
Log.e("Is Equal: ", mPackName.get(i));
return true;
}
return false;
}
}
}
The program correctly receives the data from the server and stores it in the mPackName list. At the end of the doInBackground function, the program checks if the input value in the GetDataAsyncTask().execute("1234567") function exists in the mPackName list, returns the true value.
Although the input value of the GetDataAsyncTask().execute("1234567") function is in the mPackNamelist, the program returns the false value.
The Log cat output is as follows:
08-28/com.green E/Size of mPackName:: 0
08-28/com.green E/mPackName[0]: 1234567
08-28/com.green E/mPackName[1]: QAZXSWE
08-28/com.green E/mPackName[2]: QWERTYU
The size of the mPackName list is also zero in Log cat , although it has three values {'1234567','QAZXSWE','QWERTYU'}.
The question is: How do I search '1234567' value in the mPackName list to return the true value in check = new GetDataAsyncTask().execute("1234567").get();
code?
Edited Answer
Looks like you even don't need AsyncTask as getOffCode method already runs in background thread.
Remove GetDataAsyncTask class and create a method like below.
private void search(final SearchCallback callback) {
APIGettingPosts apiGettingPosts = new APIGettingPosts(TestActivity.this, "get_off_code.php");
apiGettingPosts.getOffCode(new APIGettingPosts.OnOffCodeReceived() {
#Override
public void onReceived(List<Posts> posts) {
if (!(posts == null || posts.isEmpty())) {
for (int i = 0; i < posts.size(); ++i) {
mPackName.add(posts.get(i).getTitle());
Log.e("mPackName[" + String.valueOf(i) + "]", mPackName.get(i));
if (mCode.equals(mPackName.get(i))) {
callback.onSearchFound(true);
break;
}
}
}
callback.onSearchFound(false);
}
});
}
public interface SearchCallback{
void onSearchFound(boolean success);
}
Then call from onCreate method like below
search(new SearchCallback(){
#Override
public void onSearchFound(boolean success) {
}
});
Try placing a switch in the onPostExecute() method.
EG.
...
private class GetDataAsyncTask extends AsyncTask<String, Void, Boolean> {
#Override
void onPostExecute(Object o){
handleResults()
}
...
void handleResults(){
// Insert your check here
}

View not updated from callback method

I am fetching data from database. My views are updating only first time when I open the activity. Then when I again open the activity, my views are not updated.(Activity is starting again, hence onCreate() is called again & all settings are same). If I getText() after setting the text, I am getting proper values in log but nothing is displayed in view.
Here is my code snippet:
//My Call Back method
#Override
public void onRatingDataLoaded(ReviewJsonModel review) {
int ratingCount = 0, ownRating = 0;
String averageRating = "0";
if (review != null) {
ratingCount = review.review_count;
DecimalFormat format = new DecimalFormat("##.00");
averageRating = format.format(review.rating);
if (review.ownreviews != null) {
try {
ownRating = Integer.parseInt(review.ownreviews.rating);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
} else {
// do something
}
mTotalRatingCount.setText(String.format(getResources().getString(R.string.review_count), ratingCount));
mAverageRating.setText(averageRating);
// Log.v("LoggingReview", mTotalRatingCount.getText().toString().trim);
myRating.setRating(ownRating);
}
//Here I am setting listner as well as loading data.
public void loadReviewData(RatingDataLoadListener listener, int destinationId) {
if (mDataLoadListener == null)
mDataLoadListener = listener;
new getReviews().execute(destinationId);
}
Next is my asyntask
private class getReviews extends AsyncTask<Integer, Void, ReviewJsonModel> {
#Override
protected ReviewJsonModel doInBackground(Integer... integers) {
Cursor appCursor = mRatingApi.getDestinationReview(integers[0]);
ReviewJsonModel mReviewData = new ReviewJsonModel();
if (appCursor != null && appCursor.getCount() > 0) {
appCursor.moveToFirst();
while (!appCursor.isAfterLast()) {
mReviewData = getDocument(appCursor);
appCursor.moveToNext();
}
appCursor.close();
}
return mReviewData;
}
#Override
protected void onPostExecute(ReviewJsonModel result) {
super.onPostExecute(result);
if (mDataLoadListener != null)
mDataLoadListener.onRatingDataLoaded(result);
}
}
Can't find cause of problem. Any help is appreciated.
Looks like there is callback issue, can you please try below
public void loadReviewData(RatingDataLoadListener listener, int destinationId) {
mDataLoadListener = listener;
new getReviews().execute(destinationId);
}

onStartLoading gets called multiple times

I'm having an issue with Android Loaders.
I have an activity populated from internet data, and I have a bookmarks option to store and load them locally.
I'm implementing a recyclerView displaying the items.
When I change the sorting criteria the adapter gets cleared and repopulated with new data, and when I choose to see the bookmarked items a local query is started to the ContentProvider.
Now, I'm having issues with the bookmarked data, since I get multiple copies of the same item in my adapter.
I've done some logging and I noted that the loader is called multiple times when loading locally (adding the same items each time), but I can't see why.
Note that this occurs also when I get back to the activity, but does not occur when I start the app with the bookmarks preference.
If I start from bookmarks, select a bookmark and go back, multiple calls are done, too.
Can anyone help me? Here's the code:
In MainActivity this method is called at the end of onCreate
private void loadPosters() {
Log.d(TAG,"Loading posters");
if (mPagesLoaded < MAX_PAGES) {
Bundle args = new Bundle();
args.putInt("page",mPagesLoaded+1);
getSupportLoaderManager().restartLoader(LOADER_ID,args,this);
}
}
My loader code:
public Loader<ArrayList<Movie>> onCreateLoader(int id, final Bundle args) {
return new AsyncTaskLoader<ArrayList<Movie>>(this) {
ArrayList<Movie> mData;
#Override
protected void onStartLoading() {
Log.d(TAG,"Start Loading");
super.onStartLoading();
if (mData!=null){
deliverResult(mData);
}else{
if (mPagesLoaded == 0) {
mProgressBar.setVisibility(View.VISIBLE);
}
mErrorTextView.setVisibility(View.INVISIBLE);
forceLoad();
}
}
#Override
public ArrayList<Movie> loadInBackground() {
Log.d(TAG,"Load in background");
if (args.size() == 0) {
return null;
}
int page = args.getInt("page");
NetworkUtils networker = new NetworkUtils(getApplicationContext());
String criterion = getSharedPreferences(getString(R.string.movie_preferences), Context.MODE_PRIVATE).getString("sorting", "popular");
if (!(criterion.equals(getString(R.string.pref_bookmarked)))) {
URL request = networker.buildMoviesUrl(page, criterion);
try {
String JSONResponse = networker.getResponseFromHttpUrl(request);
ArrayList<Movie> res = fetchMoviesFromJson(JSONResponse);
mPagesLoaded++;
return res;
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return null;
}
else{
Log.d(TAG,"Local Loading");
Cursor cursor = getContentResolver().query(MovieContract.MovieEntry.CONTENT_URI,null,null,null,null);
if (cursor!=null){
Log.d(TAG,"Cursor is not null");
ArrayList<Movie> res = fetchMoviesFromCursor(cursor);
cursor.close();
return res;
}
return null;
}
}
#Override
public void deliverResult(ArrayList<Movie> data) {
mData = data;
mProgressBar.setVisibility(View.INVISIBLE);
super.deliverResult(data);
}
};
}
My onLoadFinished callback:
#Override
public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> movies) {
Log.d(TAG,"Load finished");
mProgressBar.setVisibility(View.INVISIBLE);
if (movies != null) {
mPostersAdapter.addMovies(movies);
Log.d(TAG,mPostersAdapter.getItemCount() + " items loaded");
showPosters();
} else {
showErrorMessage();
}
}
And my SharedPreferences code:
private void initSharedPreferences() {
mSharedPrefs = getApplicationContext().getSharedPreferences("movie_preferences", MODE_PRIVATE);
mOnSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "Shared preferences for " + key + "changed. Pref: " + sharedPreferences.getString(key, null));
mPagesLoaded = 0;
mPostersAdapter.clear();
loadPosters();
}
};
mSharedPrefs.registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
}
I had to call destoryLoader() on loader Manager, to solve this. Not sure if this is the right way..

using dialogeFragment from AsyncTask

my application has a background AsyncTask which checks every few second if there's a new reminder if there is an new reminder it should start a dialogeFragment which shows the reminder message.
the problem is from what i read that a dialogeFregment can be used only from an activity
can you maybe share an idea about how to implement this?
this is our current code:
public class checkReminderTask extends AsyncTask<FragmentManager, String, Void> {
Context context;
private MainActivity activity;
FragmentManager fm;
checkReminderTask(Context app_context){
context=app_context;
objPlayer=MediaPlayer.create(context, R.raw.its_time_time_tim);
reminderTable = new ParseObject("ReminderTable");
settingsTable = new ParseObject("SettingsTable");
androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
ParseQuery query = new ParseQuery("SettingsTable");
query.whereEqualTo("userID", androidId);
query.getFirstInBackground(new GetCallback() {
public void done(ParseObject object, ParseException e) {
if (object == null) {
//TODO something, throw exception
} else {
myNumber=object.getString("userNumber");
}
}
});
}
#Override
protected Void doInBackground(FragmentManager... arg) {
//infinitely loop, and move the ball accordingly
fm=arg[0];
while (1 < 2)
{
//downloading the reminder...
try
{
publishProgress(currentDateandTime,reminderExeTime,reciverNumber,message);
Thread.sleep(1000);
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
#Override
protected void onProgressUpdate(String... values)
{
super.onProgressUpdate(values);
if(values[0].equals(values[1])){
MessageReceivedReminder dialog = new MessageReceivedReminder();
dialog.setMessageArguments(context,values[2],values[3]);
dialog.show(fm, "MyDialog");
objPlayer.start();
}
else
{
Log.i("thread", "compareFailed");
}
}
code from the activity(trying to send the FragmentManager directly):
FragmentManager fm = getFragmentManager();
ringThread.execute(fm);
right now we are receiving a run time error because of a null pointer
does anyone have a better idea how we can do such a thing?
thanks!!
if you're passing the app context, this is why it's happening.
when inflating dialogs, you should always use the Activity context and never the getApplicationContext().
for more, read this: Android: ProgressDialog.show() crashes with getApplicationContext

I have problems when using sqlite inside AsyncTask in android?

I face two main problems when using a sqlite command inside an AsncTask in android.
When I execute a select command on the first try I get no results but on the second try (loading a activity that has this Asynctask) I do get results.
Sometimes I get an error that the database is not closed despite that it is already closed/
What is the problem with this?
UPDATE:
This is the code that retrive data from database (db.getAllMessage)
private ArrayList<FriendMessagesResulted> getMessagesFromCach(Context c){
FriendMessagesResulted messagesResulted1 = new FriendMessagesResulted();
DBAdapter db = new DBAdapter(c);
Cursor c1;
db.open();
c1 = db.getAllMessage(Settings.getCurrentUserId(c),Integer.parseInt(fId));
Log.d("***Database count",c1.getCount()+" from: "+Settings.getCurrentUserId(c)+" to:"+Integer.parseInt(fId));
c1.moveToFirst();
if(c1.getCount()>0)
status=true;
if (messagesResultedList == null) {
messagesResultedList = new ArrayList<FriendMessagesResulted>();
}
else
messagesResultedList.clear();
while (c1.isAfterLast() == false) {
if(Integer.parseInt(c1.getString(0))>maxId)
maxId=Integer.parseInt(c1.getString(0));
messagesResulted1.set_mId(Integer.parseInt(c1.getString(0)));
messagesResulted1.set_msgTxt("MD:"+c1.getString(3));
messagesResulted1.set_MessageTime(c1.getString(4));
messagesResulted1.set_dir(c1.getString(5));
messagesResultedList.add(messagesResulted1);
c1.moveToNext();
}
db.close();
c1.close();
return messagesResultedList;
}
and this the code for AsyncTask, where I call get getMessagesFromCach method
private void getMessages(final Context c)
{
handler = new Handler();
r=new Runnable() {
public void run() {
class RecentMessageLoader extends AsyncTask<Void, Void, ArrayList<FriendMessagesResulted>>{
ArrayList<FriendMessagesResulted> messagesResultedList=null;
#Override
protected ArrayList<FriendMessagesResulted> doInBackground(Void... params) {
if(!finishLoadingPastMessages)
{
messagesResultedList=getMessagesFromCach(c);
if(!status){
Log.d("Where are you","I'm in JSON");
messagesResultedList=getMessagesFromJSON(c);
}
}
else{
Log.d("Where are you","I'm in Recent messages");
messagesResultedList=getRecentMessages(c,Settings.getCurrentUserId(c),Integer.parseInt(fId));
}
return messagesResultedList;
}
protected void onPostExecute( ArrayList<FriendMessagesResulted> FMRList ) {
// to disappear loading message
d.dismiss();
finishLoadingPastMessages=true;
if(FMRList!=null){
for(int i=FMRList.size()-1;i>=0;i--)
addMessage(FMRList.get(i),c);
}
handler.postDelayed(r, 2000);
}
}
new RecentMessageLoader().execute();
}
};
handler.post(r);
}
UPDATE 2 : Cach class ..
public class Cach {
static DBAdapter db;
public Cach(Context c)
{
}
public static void AddMessages(Context c,
int id,
int fromId,
int toId,
String message,
String dir,
String MessageTime)
{
db = new DBAdapter(c);
db.open();
long id2;
id2 = db.insertMessage(id, fromId, toId, message, dir,MessageTime);
db.close();
}
}
It seems the problem is with the type of variables you are using.. there must be Static variables of instance variables which are getting set from many sources... try not to use static variables and use local variables I mean in the methods implicitly.

Categories

Resources