Android SQLite Attempt to unlock read lock, not locked by current thread - java

My application was running fine, until I stumbled across this SQLite error:
java.lang.IllegalMoniterStateException: attempt to unlock read lock, not locked by current thread
I've tried all the other answers on StackOverflow, but they didn't seem to help. They all said I have multiple threads creating instances of my SQLiteDBAdpdter, but I only have on Activity, but its a SherlockFragmentActivity. Each of the classes create a new instance of the SQliteDBAdapter, so I'm not sure if this is why I'm getting this error.
I've been stuck on this for hours, and would GREATLY appreciate any help.
Fragment 1:
public final class NotesFragment extends SherlockListFragment {
NotesDbAdapter db;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
public static NotesFragment newInstance(String content) {
NotesFragment fragment = new NotesFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db = new NotesDbAdapter(getActivity());
db.open();
if (db.fetchAllNotes().getCount() == 0) {
doWelcomeMessage();
}
db.close();
}
private void doWelcomeMessage() {
// Show welcome dialog
startActivity(new Intent(getActivity(), NotesWelcome.class));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
return null;
}
db = new NotesDbAdapter(getActivity());
db.open();
if (db.fetchAllNotes().getCount() == 0) {
doWelcomeMessage();
}
db.close();
return ll;
}
}
The error is pointing to db.open(); in the onCreate() method. The open() method is in my SQLiteDBAdapter (NotesDBAdapter) which is as follows:
public NotesDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
And theonCreate() in my NotesDBAdapter is:
#Override
public void onCreate(SQLiteDatabase db) {
SQLiteDatabase checkDB = null;
String myPath = "data/data/com.xxx.xxx/databases/data.db";
checkDB = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READONLY);
if (checkDB != null) {
// exists; do nothing
} else {
// create DB
db.execSQL(DATABASE_CREATE);
}
}
Thanks again!
EDIT
The other fragment
creating a new instance
of the database is called fragment 2, but I did not show its code as its really the same as fragment 1.

I am not sure if this is the problem, but seems you are opening the same database twice for write. Please just open one and share it within your application.
Please also read here: Android threading and database locking

You write:
Each of the classes create a new instance of the SQliteDBAdapter, so
I'm not sure if this is why I'm getting this error.
I'm not sure which classes you mean there, since I only see two partial classes in your example -- NotesFragment and NotesDbAdapter. But if your activity depends on other classes that each open and close the database, then you must ensure that there aren't any recursive attempts to open the DB. (I.e., class A opens the database, then calls class B, which attempts to open the database using its own instance of the DBHelper. At that point, B is trying to open the database while A has it open.)
As dongshengcn noted earlier, that could be the bug. It might be more appropriate to open the database in NotesFragment.onCreate, and close it in NotesFragment.onDestroy, and pass the db object from NotesFragment into these other classes.
The next question is, does your NotesDbAdapter class have an appropriate close method? It should look something like this, and (as mentioned above) be called from NotesFragment.onDestroy.
#Override
public synchronized void close() {
if (mDbHelper != null) {
mDbHelper.close();
mDbHelper = null;
}
super.close();
}

Related

Why my sqlite insert method is not working

Save data with SQLite
Hi guys, I'm new to Android and i'm working in a Notes App, until now I have done with the main interphace and recieve the inputs from the Edittext, but when it comes to saving data to my SQLite DB, my code is failling, can you please tell me what I been doing wrong ?.
FormActivity method
public void saveToDB(View view) {
try {
String subjet = asunto.getText().toString();
String body = cuerpo.getText().toString();
int day = myCalendar.get(Calendar.DAY_OF_MONTH);
int month = myCalendar.get(Calendar.MONTH);
int year = myCalendar.get(Calendar.YEAR);
Note note = new Note(subject,body,day,month,year); // Create an instance of Note class
DBHelper db = new DBHelper(this,"TaskList.db",null,1); // New Instance of DBHelper class
db.InsertIntoTable(note); // pass the newly created Note instance to DBHelper insert method
db.close(); // we close Database
Toast.makeText(this,"Note saved !!",Toast.LENGTH_SHORT).show(); // Notify the user of the operation success
Intent intent = new Intent(FormActivity.this,MainActivity.class); // Lets return to MainActivity again
startActivity(intent);
}
catch (Exception e){
Toast.makeText(this,"Unable to save note",Toast.LENGTH_SHORT).show();
}
}
DBHelper insert method
public Long InsertIntoTable(Note note){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
/* We organize the values in the respective rows */
values.put(col_subject,note.getSubject());
values.put(col_note,note.getBody());
values.put(col_day,note.getDay());
values.put(col_month,note.getMonth());
values.put(col_year, note.getYear());
// Time to insert data to Database
long rowID = db.insert(table_name,null,values);
return rowID;
}
I put a Toast, to prevent my app from crashing in case and to see if the operation succed, but it fails everytime.
RecyclerAdapter class
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.itemview,null);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
Note note = noteList.get(i);
viewHolder.asunto.setText(note.getAsunto());
viewHolder.nota.setText(note.getCuerpo());
viewHolder.fecha.setText(note.getDia() + "/" + note.getMes() + "/" + note.getAno());
viewHolder.etiqueta.setText(note.getTag());
}
This is in my stacktrace:
E/RecyclerView: No adapter attached; skipping layout

How to use Android's AsyncTask to get all of my data through JSoup, then send that information to another class?

I'm pretty new to developing in Android, but I understand that I cannot run Jsoup.connect(url).get() without some kind of thread as it is a synchronous call. I tried creating a class called Product that extends AsyncTask, and then having my original class called List call Product and then have the int displayed. However, regardless of the actual result, it always prints out 0.
I have tested my code before, so I know it works and that the issue must be something related to Android.
List Class:
public class List extends AppCompatActivity{
String itemURL;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_list_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Bundle itemData = getIntent().getExtras();
if(itemData==null){
return;
}
//Gets URL
itemURL = itemData.getString("itemURL");
int listSize=new Product(itemURL).getListSize();
System.out.println(listSize);
}
}
Product class:
public class Product extends AsyncTask<Void,Void,Void>{
String url;
String title;
int listSize;
public Product(String url){
this.url = url;
}
protected Void doInBackground (Void... voids) {
//Create JSoup connection
try{
Document doc = Jsoup.connect("url").get();
//Gets title
String link = doc.select("h2#s-result-count").first().text();
System.out.println(link);
listSize=Integer.parseInt(link.substring(0,1));
System.out.println(listSize);
try{
int listSize= Integer.parseInt(link.substring(0,2));
System.out.println(listSize);
}catch(Exception e){}
}catch (Exception e){}
}
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
getListSize();
}
public int getListSize(){
return listSize;
}
}
All advice would be appreciated, thanks for your time and help!
You have to override
onPostExecute()
to make sure you are getting the variable after it is updated from the thread. Call your
getListSize()
from inside the overridden function.
Consider that calling
execute()
and then immediately calling
getListSize()
will happen line by line while the actual thread you have spun off will continue working. You're asking for a value that hasn't been updated.
Edit::
For clarification:
public class List extends AppCompatActivity{
String itemURL;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_list_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Bundle itemData = getIntent().getExtras();
if(itemData==null){
return;
}
//Gets URL
itemURL = itemData.getString("itemURL");
int listSize=new Product(itemURL).getListSize(); // this doesn't do anything meaningful for you
System.out.println(listSize);
}
}
Note the comment I have added to
int listSize = new Product(itemURL).getListSize();
First,
doInBackground()
will never run until you call
execute()
You need to call
new Product(itemURL).execute();
Afterword, you have to provide some kind of callback to the Activity since you haven't nested the product class or initialized an anonymous instance of AsyncTask.
In your Product class you can set a variable
Context
public class Product extends AsyncTask<...> {
public Context context;
}
Then, in place of
int listSize = new Product(itemURL).getListSize();
put:
Product product = new Product();
product.context = this;
product.execute();
Then, in Product's
onPostExecute()
put:
if (context.getClass().equals(List.class)) {
((List) context).some_method_that_does_something_with_list_size()
}
Alternatively, you can add it to Product's constructor:
public Product(String url, Context context) {...}
Edit 2::
For further clarification
((List) context).some_method_that_does_something_with_list_size()
was meant to serve as a placemarker for any method available in your activity. It could easily be substituted with:
System.out.println(listSize);
Edit 3::
For further, further clarification:
In your Activity, ItemListView, define a method called
printListSize(int listSize) {
System.out.println("list size: " + listSize);
}
Then, in onPostExecute() call:
((ItemListView) context).printListSize(listSize);

Putting and getting savedInstanceState data

I'm just trying to understand the Activity lifetime with this simple example by trying to find how I get back the SomeIntegers g_values object and the ArrayList AnInteger objects within it.
As it is, it is not of much meaning but will serve as a paradigm of my real situation where initial setup requires the app to schlepp through countless reams of pre-processing eg access and list fonts, analyse all my available games, in the APK, on file and online in my website, players records etc. The final app is a system of games and activities to help SpLD (dyslexia) students of all ages exercise their reading, spelling, organisational skills and short term memory. It is of serious intent. Although free running, it is best used with SpLD supervisors/tutors who can set the work schedule of their charge and even add their own games.
Anyway enough of the irrelevant background.
Can I save my somewhat complex objects using access to the savedInstanceState (somewhat hampered by their being no putxxxxx method of the correct form) or should abandon this approach and recover the data from persistent files or databases? This can be discussed hopefully within the limits of this simple example, the real thing is simply more of the same but with different details.
Note added after. There is also the issue of taking the user/player back to where he/she was when the app experienced the need to save its InstanceState. As the major influence seems to be the orientation of the tablet, I could maybe side step that by locking the orientation at start up. This would simplify many display issues also but is it an "unacceptable" style?
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class TestBundle extends AppCompatActivity {
SomeIntegers g_values;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("onCreate (" + (savedInstanceState == null ? "null)" : "set)"));
if (savedInstanceState == null)
{ g_values = new SomeIntegers();
String result = g_values.report();
System.out.println("Startup Result: " + result);
setContentView(R.layout.activity_test_bundle); // Where do I put this line?
}
else
{ //Do I get g_values back here?
//More relevantly, can I, and how can I, put g_values in the
//savedInstanceState when onSaveInstanceState is called?
String result = g_values.report();
System.out.println("Result: " + result);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
System.out.println("onSaveInstanceState (" + (outState == null ? "null)" : "set)"));
//How do I add g_values to the Bundle?
}
// Following is just stuff to watch the progress of the
// Activity in the ADB Log. Not of much relevance. Or is it?
#Override
protected void onStop() {
super.onStop();
System.out.println("onStop()");
}
#Override
protected void onStart() {
super.onStart();
System.out.println("onStart()");
}
#Override
protected void onRestart() {
super.onRestart();
System.out.println("onRestart()");
}
#Override
protected void onResume() {
super.onResume();
System.out.println("onResume()");
}
#Override
protected void onPause() {
super.onPause();
System.out.println("onPause()");
}
#Override
protected void onDestroy() {
super.onDestroy();
System.out.println("onDestroy()");
}
}
public class SomeIntegers {
private ArrayList<AnInteger> c_values;
SomeIntegers() {
c_values = new ArrayList<AnInteger>();
c_values.add (new AnInteger(1));
c_values.add (new AnInteger(2));
c_values.add (new AnInteger(3));
c_values.add (new AnInteger(4));
c_values.add (new AnInteger(29));
c_values.add (new AnInteger(30));
}
String report() {
String g = "";
for (AnInteger ai : c_values) {
if (!g.isEmpty()) g = g + ", ";
g = g + ai.getC_value();
}
return (g.isEmpty() ? "Empty" : g);
}
}
public class AnInteger {
private int c_value;
AnInteger(int value) { c_value = value); }
public int getC_value () { return c_value; }
}
Thank you. Josie Hill
First make your data models implement Parcelable :
AnInteger:
public class AnInteger implements Parcelable {
private int c_value;
public AnInteger(int value) {
this.c_value = value;
}
public int getC_value() {
return c_value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.c_value);
}
protected AnInteger(Parcel in) {
this.c_value = in.readInt();
}
public static final Parcelable.Creator<AnInteger> CREATOR = new Parcelable.Creator<AnInteger>() {
#Override
public AnInteger createFromParcel(Parcel source) {
return new AnInteger(source);
}
#Override
public AnInteger[] newArray(int size) {
return new AnInteger[size];
}
};
}
SomeIntegers:
public class SomeIntegers implements Parcelable {
private ArrayList<AnInteger> c_values;
public SomeIntegers() {
c_values = new ArrayList<>();
c_values.add(new AnInteger(1));
c_values.add(new AnInteger(2));
c_values.add(new AnInteger(3));
c_values.add(new AnInteger(4));
c_values.add(new AnInteger(29));
c_values.add(new AnInteger(30));
}
public String report() {
String g = "";
for (AnInteger ai : c_values) {
if (!g.isEmpty()) {
g = g + ", ";
}
g = g + ai.getC_value();
}
return (g.isEmpty() ? "Empty" : g);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.c_values);
}
protected SomeIntegers(Parcel in) {
this.c_values = in.createTypedArrayList(AnInteger.CREATOR);
}
public static final Parcelable.Creator<SomeIntegers> CREATOR = new Parcelable.Creator<SomeIntegers>() {
#Override
public SomeIntegers createFromParcel(Parcel source) {
return new SomeIntegers(source);
}
#Override
public SomeIntegers[] newArray(int size) {
return new SomeIntegers[size];
}
};
}
Then in your activity saving and restoring gets pretty easy, here is an example using your current data model:
//set up class fields/members
private final static String STATE_G_VALS = "STATE_G_VALS";
SomeIntegers g_values = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_bundle);
System.out.println("onCreate (" + (savedInstanceState == null ? "null)" : "set)"));
if (savedInstanceState != null) {
// get g_values back here
g_values = savedInstanceState.getParcelable(STATE_G_VALS);
}
if (g_values == null) {
// ok its null, lets make one
g_values = new SomeIntegers();
}
// log some stuff
String result = g_values.report();
System.out.println("Result: " + result);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
//set g_values to the Bundle/saved state (even if it is null)
outState.putParcelable(STATE_G_VALS, g_values);
super.onSaveInstanceState(outState);
}
The concept of restoring the activity states is based on device orientation. So for example if you pull some changes from persisted file, loaded it, when the screen changes it angle of rotation that data will be recreated. So the activity uses a bundle to wrap that data, and permits the user to save the current working state of such file, which then can be restored. Here is a great link. Your requirements sounds consistent as it regards data changes, as per my first question regarding the anticipated file sizes, your requirements sounds relatively small.
To work compound data types and abstract data types , do consider using GSON.which is a Java serialization/deserialization library to convert Java Objects into JSON and back
Therefore I can recommend you using the power of shared preferences in android.If you have a relatively small collection of key-values that you'd like to save, you should use the SharedPreferences APIs. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. In simple terms,Shared Preferences allow you to save and retrieve data in the form of key,value pair.
Android provides many ways of storing data of an application. If your requirements needs storage consistency, I would go with the database approach, I would recommend using realm.Realm is a mobile database and a replacement for SQLite. Although is an OO database it has some differences with other databases. Realm is not using SQLite as it’s engine. Instead it has own C++ core and aims to provide a mobile-first alternative to SQLite.
Hope this was helpful:)

Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference

I've been looking for a similar problem to mine in order to find a solution, but I seriously couldn't find anything like that.
I was trying to download from parse an array of posts with an asynctask class, and after it gets the posts, it suppose to set the posts array in my page, and perform the setAdapter function in order to set my new posts array.
the problem is, after I've initialized listView and listAdapter in my home fragment,and then I perform the postArray taking from parse function, after it finishes taking the posts array from parse, it cannot update listAdapter because it says the listAdapter and my listView "haven't initialized yet", even though they have.
p.s.
sorry for not posting my code in a convenient way, I don't tend to post my code problems that often.
here's my code:
my home fragment:
public class HomeFragment extends Fragment {
View root;
ArrayList<PostClass> postsArrayList = new ArrayList<>();
static boolean isPostsArrayUpdated = false;
ListAdapter listAdapter;
PullToRefreshListView listView;
public void updatePostsArrayList(ArrayList<PostClass> postsArrayList){
if(!isPostsArrayUpdated){
// First time updating posts array list
listAdapter = new ListAdapter(getActivity(), root);
listView = (PullToRefreshListView) root.findViewById(R.id.list_container);
this.postsArrayList = postsArrayList;
listView.setAdapter(listAdapter);
isPostsArrayUpdated = true;
root.findViewById(R.id.homeFragmentLoadingPanel).setVisibility(View.GONE);
}else{
// Have updated posts before
this.postsArrayList = postsArrayList;
listAdapter.notifyDataSetChanged();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_home, container, false);
listView = (PullToRefreshListView) root.findViewById(R.id.list_container);
listAdapter = new ListAdapter(getActivity(), root);
Home_Model.getInstance().setPostsArrayList();
return root;
}
public class ListAdapter extends BaseAdapter implements View.OnClickListener{//....}
my home model:
public class Home_Model {
Home_Model(){}
static final Home_Model instance = new Home_Model();
public static Home_Model getInstance() {
return instance;
}
public void setPostsArrayList(){
new setHomePostsArray().execute();
}
public class setHomePostsArray extends AsyncTask<Void, ArrayList<PostClass>, Void>{
ArrayList<String> followersList;
ArrayList<PostClass> postsArrayList;
#Override
protected Void doInBackground(Void... params) {
// Getting posts from parse
String userName = Parse_model.getInstance().getUserClass().get_userName();
followersList = Parse_model.getInstance().getFollowersByUserNameToString(userName);
followersList.add(userName);
postsArrayList = Parse_model.getInstance().getAllUsersPostsByFollowings(followersList);
for (PostClass currPost : postsArrayList) {
for (PostClass currLocalDBPost : LocalDBPostsArray) {
if (currPost.getObjectID().equals(currLocalDBPost.getObjectID())) {
currPost.set_postPicture(currLocalDBPost.get_postPicture());
}
}
}
//Updating home page
onProgressUpdate(postsArrayList);
// Updating local data base in new posts
//checking in local DB if there are any new posts from parse and update them
for (PostClass currPost : postsArrayList) {
boolean isPostExists = false;
for (PostClass currLocalPost : LocalDBPostsArray) {
if (currPost.getObjectID().equals(currLocalPost.getObjectID())) {
isPostExists = true;
}
}
if (!isPostExists) {
ModelSql.getInstance().addPost(currPost);
Log.e("post not exist", "adding local DB");
}
}
//updating followers list in local DB
Parse_model.getInstance().getUserClass().setFollowersArray(followersList);
ModelSql.getInstance().updateFollowersArray(currUser);
return null;
}
#Override
protected void onProgressUpdate(ArrayList<PostClass>... values) {
//pass the updated postsArrayList to home fragment
if(setPostsInHomePageDelegate!= null){
setPostsInHomePageDelegate.setPosts(values[0]);
}
}
}
public interface SetPostsInHomePage {
public void setPosts(ArrayList<PostClass> postsArrayList);
}
SetPostsInHomePage setPostsInHomePageDelegate;
public void setSetPostsInHomePageDelegate(SetPostsInHomePage setPostsInHomePageDelegate) {
this.setPostsInHomePageDelegate = setPostsInHomePageDelegate;
}
main activity:
public class MainActivity extends Activity {
static HomeFragment homeFragment = new HomeFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// the home fragment has already been opened during the app opening
//...
setPostsImHomePage();
}
//...
public void setPostsImHomePage(){
Home_Model.getInstance().setSetPostsInHomePageDelegate(new Home_Model.SetPostsInHomePage() {
#Override
public void setPosts(ArrayList<PostClass> postsArrayList) {
homeFragment.updatePostsArrayList(postsArrayList);
}
});
}
}
Try to move your method setPostsImHomePage(...) from MainActivity to HomeFragmentand call it in OnCreateView before return root;.
Try initializing homeFragment in onCreate before your method call. It's also helpful to know which line(s) are giving you errors.
Obviously your fragment has no View when the result arrives.
You should properly add the fragment to the Activity using the FragmentManager, then in the Fragment's onActivityCreated() callback (which is called by the system after the Fragment has its view properly set), start your AsyncTask.

Updating Fragments - FrameStatePagerAdapter and HTTP Calls

I have been searching for an answer to my problem, but I seem to get none, despite of how many tutorials I followed, how many questions I've gone through and how many things I've tried to do what I want. Basically, I stumbled upon some good tips, and still couldn't manage to do what wanted.
THE PROBLEM
I am creating an Android Application that will use Fragments (alongside with tabs). In these fragments, I have crucial information relating the application, such as text boxes, and buttons. However, I want to do something really simple, which is updating one of my fragments as I come back to it (imagine I swipe back to a fragment, and I update it with the relevant information). Where is the information stored? On a node.js server, to which I call every time I want information. So for that, I created the following structure.
THE STRUCTURE
First of all, I started off creating my Activity.
public class CentralActivity extends FragmentActivity {
CentralPagerAdapter mCentralActivity;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_central);
tabHandler();
}
public void tabHandler() {
mCentralActivity = new CentralPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.CentralPager);
mViewPager.setAdapter(mCentralActivity);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
getActionBar().setSelectedNavigationItem(position);
}
});
//Action Bar Stuff
}
}
With this said, I need my CentralPagerAdapter, which I created as follows.
public class CentralPagerAdapter extends FragmentStatePagerAdapter {
private int nSwipes = 3;
public CentralPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new CentralFragment();
Bundle args = new Bundle();
args.putInt(CentralFragment.ARG_OBJECT, i + 1);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return nSwipes;
}
}
And now, my fragment, which is only a class that contains all of my views, and options and so on.
public class CentralFragment extends Fragment {
public static final String ARG_OBJECT = "object";
private View rootView;
private RESTFunction currentFunction;
//Has the info I want
private ArrayList<Integer> tickets = new ArrayList<Integer>();
#SuppressLint("HandlerLeak")
private Handler threadConnectionHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (currentFunction) {
case GET_CLIENT_TICKETS:
handleGetTickets(msg);
break;
case BUY_CLIENT_TICKETS:
break;
default:
break;
}
}
};
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
final Bundle args = getArguments();
handleFragments(inflater, container);
getTicketInfo(null);
return rootView;
}
private void handleFragments(LayoutInflater inflater, ViewGroup container) {
Bundle args = getArguments();
if (args.getInt(ARG_OBJECT) == 1) {
rootView = inflater.inflate(R.layout.fragment_show_tickets,
container, false);
showTicketsHandler();
} else if (args.getInt(ARG_OBJECT) == 2) {
rootView = inflater.inflate(R.layout.fragment_buy_tickets,
container, false);
buyTicketsHandler();
} else {
rootView = inflater.inflate(R.layout.fragment_history_tickets,
container, false);
}
}
public void showTicketsHandler() {
//Get stuff from the tickets array that the REST call will handle
//And set them to boxes or radio buttons
}
public void buyTicketsHandler() {
//Get stuff from the tickets array that the REST call will handle
//And set them to boxes or radio buttons
//As well as button click listeners
}
public void getTicketInfo(ProgressDialog progDialog) {
//Connect to the thread to get the information
//In this case, I have no parameters
ConnectionThread dataThread = new ConnectionThread("myLink", Method.GET, null, threadConnectionHandler, progDialog);
dataThread.start();
}
//Get stuff from the resulting JSON and store it in the tickets ArrayList
private void handleGetTickets(Message msg) {
JSONObject ticketListing = (JSONObject) msg.obj;
try {
tickets.add(ticketListing.getInt("t1"));
tickets.add(ticketListing.getInt("t2"));
tickets.add(ticketListing.getInt("t3"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
And then, I have my thread..
public class ConnectionThread extends Thread {
private ConnectionRunnable runConnection;
private Handler mHandler;
private ProgressDialog progDialog;
public ConnectionThread(String link, Method method, ArrayList<NameValuePair> payload, Handler handler, ProgressDialog progDialog) {
runConnection = new ConnectionRunnable(link, method.toString(), payload);
mHandler = handler;
this.progDialog = progDialog;
}
#Override
public void run() {
runConnection.run();
threadMsg();
if(progDialog != null)
progDialog.dismiss();
}
public JSONObject getJSON() {
return runConnection.getResultObject();
}
private void threadMsg() {
Message msgObj = mHandler.obtainMessage();
msgObj.obj = getJSON();
mHandler.sendMessage(msgObj);
}
}
And ConnectionRunnable is where I run my HttpURLConnection.
SO WHAT DO I NEED?
Basically, what I'm trying to do, is to get the ticket information from the ConnectionThread BEFORE I load all my view and update them. Plus, I want to be able to swipe back and forth, and update my information on the array as I swipe through the screens (if I go to the second screen, the tickets will update, and if I come back to the first, they will re-update). So basically, call the ConnectionThread everytime I swipe around. If that is possible that, is.
WHAT HAVE I TRIED?
I've tried several things already, and all of them didn't actually help..
The usage of ProgressDialogs to stop the UI Thread on the onCreateView method of the fragment (no use, because it returns the rootView before it handles everything);
Making the UI Thread sleep for 1 second (I don't know why, it blocks all of them);
Overriding the instantiateMethod() of the Adapter, although I think I didn't do it correctly;
Overriding the saveState() of the Adapter, in order to prevent its saved states, and to then get new ticket information;
Giving the fragments tags to update their rootViews on the Adapter, but to no avail;
Getting the information in the activity, and everytime I make a purchase (second fragment), restart the whole activity to get the tickets, which I believe is a really, really bad solution.
I've read several articles, and I still couldn't find my answers.. It's really frustrating. Because it's something so simple, however, the fact that I have to run the HTTP calls on a different thread delays the whole UI updating process.
I've also read the AsyncTask's method. However, I feel like both Threads and AsyncTasks end up in the same.
WHAT TO DO NOW?
Well, that's what I was hoping to find. Because it ends up being annoying as it is.
POSSIBLE REASONS
Is it because I'm separating all classes into spread files, therefore making my work difficult?
Thank you for your time, guys, hope we can find a solution or something.
THE EDIT
So basically, after 4 hours of reading documents and tutorials, I figured that what I needed was setOffscreenPageLimit(int). However, it can't be set to 0, so I will have to do with a setOnPageChangeListener. Now, to figure how to refresh the fragment, and I'll be as good as new.
Alright, it works perfectly! Basically, I did this:
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
((CentralFragment)((CentralPagerAdapter) mViewPager.getAdapter()).instantiateItem(mViewPager, position)).refresh();
getActionBar().setSelectedNavigationItem(position);
}
});
Where my .refresh is:
public void refresh() {
Bundle args = getArguments();
if (args.getInt(ARG_OBJECT) == 0) {
getTicketInfo(0);
} else if (args.getInt(ARG_OBJECT) == 1) {
getTicketInfo(1);
buyTicketsHandler();
} else {
//To Handle Later
}
}
It's as simple as refreshing the page before you go to it. Why didn't I remember this before..? So, here's the reference for those who ever need this!

Categories