I'm trying to implement a To-do list app into my app by using a Fragments instead of Activities. I ran into some issues I was able to fix, but now the activity is unable to start.
TaskDbHelper
class TaskDbHelper(fragment: HomeFragment) : SQLiteOpenHelper(fragment.requireContext(), TaskContract.DB_NAME, null, TaskContract.DB_VERSION) {
override fun onCreate(db: SQLiteDatabase) {
val createTable = "CREATE TABLE " + TaskContract.TaskEntry.TABLE + " (" +
TaskContract.TaskEntry.ID + " INTEGER, " +
TaskContract.TaskEntry.COL_TASK_TITLE + " TEXT NOT NULL, " +
TaskContract.TaskEntry.COL_TASK_TEXT + " TEXT NOT NULL, " +
TaskContract.TaskEntry.COL_TASK_DATE + " TEXT NOT NULL);"
db.execSQL(createTable)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS " + TaskContract.TaskEntry.TABLE)
onCreate(db)
}
}
I changed the parameters from context: to fragments: and fragment.requireContext(), in the helper.
This is what the log says
Process: com.khumomashapa.notes, PID: 15097
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.khumomashapa.notes/com.khumomashapa.notes.activities.MainActivity}: java.lang.IllegalStateException: Fragment HomeFragment{1c275a3} (14bd8ea4-7821-4630-9a9a-c93eaaa2e0d5)} not attached to a context.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3654)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3806)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2267)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8167)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
Caused by: java.lang.IllegalStateException: Fragment HomeFragment{1c275a3} (14bd8ea4-7821-4630-9a9a-c93eaaa2e0d5)} not attached to a context.
at androidx.fragment.app.Fragment.requireContext(Fragment.java:805)
at com.khumomashapa.notes.sql.TaskDbHelper.<init>(TaskDbHelper.kt:10)
at com.khumomashapa.notes.fragments.HomeFragment.<init>(HomeFragment.kt:25)
at com.khumomashapa.notes.activities.MainActivity.onCreate(MainActivity.kt:33)
at android.app.Activity.performCreate(Activity.java:7963)
at android.app.Activity.performCreate(Activity.java:7952)
When someone starts my app, the Home Fragment is the first one they are supposed to see, but it crashes immediately.
Here's the Home Fragment, but only the lines that cause the exception
class HomeFragment : Fragment() {
private var recyclerView: RecyclerView? = null
private var mHelper: TaskDbHelper = TaskDbHelper(this)
private val adapter = MyAdapter(this, mHelper)
private val TAG: String = "HomeFragment"
You're initialising TaskDbHelper before your fragment gets attached to an activity. You need to make sure you're accessing context/view related things in the correct points in your fragments lifecycle which you can read more about here.
Generally you should perform any view related logic in onViewCreated and accessing context can be done even earlier, most likely in onAttached.
You're calling homefragment.requireContext() before the Fragment is properly initialised. See Fragment Lifecycle
You could solve this by making mHelper a lateinit var and filling it in an appropiate callback, e.g.
override fun onAttach(context: Context) {
super.onAttach(context)
mHelper = TaskDbHelper(this)
}
... but thats a quick & dirty solution. Personally i would most probably look into dependency-injection...
Related
I have the following Asyntask in Android Studio.
I'm battling to get the progressbar to work though, the progressbar pops up but then immediately disappears.
I want to know:
Why is it not waiting for the tasks in the DoInBackground to complete before it disappears?
Any advice would be appreciated. I am a beginner so please excuse the silly question if it is indeed a silly question.
The tasks within DoInBackground are calls to a volley requests, which copy data from a remote sql database into the devices sqlite database.
Thanks.
public class AsyncTask extends android.os.AsyncTask<Integer, Integer, String> {
ProgressBar pb;
int status = 0;
public void setProgressBar(ProgressBar progressBar) {
this.pb = progressBar;
}
#Override
protected void onPreExecute() {
Log.d(TAG, "zzzzz2: " + "Pre");
pb.setVisibility(View.VISIBLE);
super.onPreExecute();
}
#Override
protected String doInBackground(Integer[] objects) {
Log.d(TAG, "zzzzz2: " + "DoIn");
// if user doesnt exist in db add to server db
String strUser = mFirebaseUser.getUid();
String strDisplayName = mFirebaseUser.getDisplayName();
String strEmail = mFirebaseUser.getEmail();
clsServerClass.addUserToServer(strUser, strDisplayName, strEmail, context);
// load 12 tables into sqlite database
clsServerClass.copyTblVersions(context);
clsServerClass.copyAnimalClassTable(context);
clsServerClass.copyAnimalGroupTable(context);
clsServerClass.copyAnimalTable(context);
clsServerClass.copyTblSeasons(context);
clsServerClass.copyRegions(context);
clsServerClass.copyCountries(context);
clsServerClass.copyProvinces(context);
clsServerClass.copyHotspots(context);
clsServerClass.copyHabitats(context);
clsServerClass.getMyPlaces(strUser, context);
clsServerClass.getSightingsUser(strUser, context);
Cursor cntry = getCsr("animal_unique_key", "tbl_lookup_animals");
Log.d(TAG, "yyyya: " + cntry.getCount());
JSONArray arrayList1 = createListArray("animal_class_key", 1);
createListSharedPref("List1_name", "List 1: All birds", "List1_where", arrayList1);
JSONArray arrayList2 = createListArray("animal_class_key", 2);
createListSharedPref("List2_name", "List 2: All Mammals", "List2_where", arrayList2);
JSONArray arrayList3 = createListArray("animal_class_key", -99);
createListSharedPref("List3_name", "List 3: All Animals", "List3_where", arrayList3);
JSONArray arrayList4 = createListArray("animal_class_key", 3);
createListSharedPref("List4_name", "List 4: All Reptiles", "List4_where", arrayList4);
JSONArray arrayList5 = createListArray("animal_class_key", 4);
createListSharedPref("List5_name", "List 5: All Amphibians", "List5_where", arrayList5);
return null;
}
#Override
protected void onProgressUpdate(Integer[] values) {
Log.d(TAG, "zzzzz3: " + "Update");
pb.setProgress(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String s) {
Log.d(TAG, "zzzzz2: " + "post");
pb.setVisibility(View.GONE);
super.onPostExecute(s);
}
}
Problem lies in onProgressUpdate method.
Docs
onProgressUpdate() : This method receives progress updates from
doInBackground method, which is published via publishProgress method,
and this method can use this progress update to update the UI thread
Since you are not passing any value to your onProgressUpdate, your progress bar disappears immediately.
In doInBackground method after Log.d(TAG, "zzzzz2: " + "DoIn");
Add
publishProgress(5); // Calls onProgressUpdate()
//5 is just an example integer
Now, it should work fine.
To learn more about how to better make use of onProgressUpdate(), consult https://www.tanelikorri.com/tutorial/android/asynctask-tutorial/
I am using CursorLoader to load data from database inside my Fragment, which has a RecyclerView. This fragment is being used inside a ViewPager. The ViewPager is contained inside a ContainerFragment, which in turn is inside an Activity.
The ContainerFragment initializes first 4 loaders out of the 10 required in onActivityCreated(). From the log I can see that the loader(s) are in fact getting created. However, onLoadFinished is not getting called for any of the loaders.
Now the twist comes when I swipe through the ViewPager to the third fragment. Now I see the third loader's onLoadFinished getting called. Now this is probably not called from the fragment but from the PagerAdapter's getItem() method, which in addition to instantiating the fragments, also initializes loaders with their IDs in case they haven't been already.
From all other questions on StackOverflow about onLoadFinished not getting called, I have tried the following solutions which are not working for me:
Using forceLoad()
this.getListView().refreshDrawableState(); -> not tried, and don't understand why this should work. Moreover, I am using RecyclerView.
"Importing the correct class" -> I am using AndroidX, and moreover, the loader does load sometimes. If it was the wrong class, it wouldn't have worked any time right?
"Fragment should use SupportLoaderManager" -> tried replacing in the fragment with getActivity().getSupportLoaderManager. For fragment I think it's just getLoaderManager. It was already working sometimes with just getLoaderManager. (No difference observed)
#onActivityCreated
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
startLoaders();
}
startLoaders()
private void startLoaders() {
while(loaderSeqIndex < CATEGORIES.size() && loaderSeqIndex < 4) {
Bundle bundle = new Bundle();
bundle.putString("category", CATEGORIES.get(loaderSeqIndex));
getLoaderManager().initLoader(loaderSeqIndex++, bundle, this);
}
}
LoaderCallbacks
#NonNull
#Override
public Loader<Cursor> onCreateLoader(int id, #Nullable Bundle args) {
Timber.i("Loader created onCreateLoader called");
CursorLoader loader = ArticleLoader.newCategorizedArticlesInstance(getContext(), args.getString("category"));
loader.registerListener(id, new Loader.OnLoadCompleteListener<Cursor>() {
#Override
public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
int position = loader.getId();
cursorHashMap.put(CATEGORIES.get(position), data);
Timber.i("mPager.getCurrentItem(): " + mPager.getCurrentItem() + " position: " + position);
if (position == mPager.getCurrentItem()) {
ArticleListViewModel model = ViewModelProviders.of(ArticleListContainerFragment.this).get(ArticleListViewModel.class);
model.setCursor(data);
}
}
});
return loader;
}
#Override
public void onLoadFinished(#NonNull Loader<Cursor> loader, Cursor data) {
int position = loader.getId();
cursorHashMap.put(CATEGORIES.get(position), data);
Timber.i("mPager.getCurrentItem(): " + mPager.getCurrentItem() + " position: " + position);
if(position == mPager.getCurrentItem()) {
ArticleListViewModel model = ViewModelProviders.of(this).get(ArticleListViewModel.class);
model.setCursor(data);
// mPagerAdapter.notifyDataSetChanged();
}
}
PagerAdapter's #getView
private class MyPagerAdapter extends FragmentStatePagerAdapter {
...
#Override
public Fragment getItem(int position) {
ArticleListFragment fragment = ArticleListFragment.newInstance();
Bundle bundle = new Bundle();
bundle.putString("category", CATEGORIES.get(position));
bundle.putInt("id",position);
fragment.setArguments(bundle);
// fragment.setCursor(cursorHashMap.get(CATEGORIES.get(position)));
return fragment;
}
...
}
I was expecting timber to print from the #onLoadFinished method just to make sure that it's getting called, which isn't happening.
Something weird that is happening is that:
the cursorHashMap that I am using, get's properly populated I open the app a second time (when refresh doesn't happen). And the cursor get's populated without #onLoadFinished being called.
I'm trying to periodically update a Google Maps marker in FXML. I tried to do this with a Timer and a new Thread, but can't get any of it to work.
I tested the new Thread with the simple task to update a TextField in my UI, which works just fine.
However, when I use the actual code that I need to update the map:
#FXML
public void handleTracking() throws IOException, InterruptedException {
new Thread() {
#Override
public void run() {
while (true) {
try {
double ar[] = FileImport.getGpsPosition();
System.out.println("Latitude: " + ar[0] + " Longitude: " + ar[1]);
double Ltd = ar[0];
double Lng = ar[1];
webEngine.executeScript(""
+ "window.lat = " + Ltd + ";"
+ "window.lon = " + Lng + ";"
+ "document.goToLocation(window.lat, window.lon);");
try {
Thread.sleep(555);
} catch (InterruptedException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}.start();
}
I get the Output message:
Exception in thread "Thread-26" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-26
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1216)
at javafx.scene.web.WebEngine.executeScript(WebEngine.java:980)
at de.fkfs.v2x.eval.FXMLDocumentController$1.run(FXMLDocumentController.java:84=)
A similar thing happens when I use a Timer, it works for the task of updating a label, however if I try to update the marker position it throws the message:
Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = Timer-0
Updates to the UI, including calls to webEngine.executeScript(...) must be executed on the FX Application thread.
On the other hand, the FX Application Thread is (effectively) the thread used for rendering the UI and processing user input. So if you block this thread with an infinite loop, or other long running process, or if you schedule too many things to run on that thread, you will make the UI unresponsive.
What you are trying to do in your code appears to be to update the UI as fast as you can. If you put the loop in the FX Application Thread you will block it entirely: if you put it on a background thread and schedule the updates using Platform.runLater(...) you will flood the FX Application Thread with too many updates and prevent it from doing its usual work, and it will become unresponsive.
The general solution here revolves around the fact that it's really redundant to update the UI so often. The human eye can only detect visible changes at a limited rate, and in technology terms you are limited by, e.g. the refresh rate of the physical screen and of the underlying graphical software. JavaFX attempts to update the UI at no more than 60Hz (in the current implementation). So there's really no point in updating more often than the underlying JavaFX toolkit updates the scene.
The AnimationTimer provides a handle method that is guaranteed to be invoked once per scene update, no matter how frequently that occurs. AnimationTimer.handle(...) is invoked on the FX Application Thread, so you can safely make changes to the UI here. So you could implement your tracking with:
private AnimationTimer tracker ;
public void initialize() {
tracker = new AnimationTimer() {
#Override
public void handle(long timestamp) {
try {
double ar[] = FileImport.getGpsPosition();
// System.out.println("Latitude: " + ar[0] + " Longitude: " + ar[1]);
double Ltd = ar[0];
double Lng = ar[1];
webEngine.executeScript(""
+ "window.lat = " + Ltd + ";"
+ "window.lon = " + Lng + ";"
+ "document.goToLocation(window.lat, window.lon);");
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
}
#FXML
public void handleTracking() {
tracker.start();
}
The only thing to be wary of here is that, because handle() is invoked on the FX Application Thread, you should not perform any long-running code here. It looks as though your FileImport.getGpsPosition() method performs some IO operations, so it should probably be delegated to a background thread. The trick here, which is the one used by JavaFX classes such as Task, is to continually update a value from a background thread, and only schedule a call to Platform.runLater(...) if one is not already pending.
First, just define a simple class for representing the location (make it immutable so it is thread-safe):
class Location {
private final double longitude ;
private final double latitude ;
public Location(double longitude, double latitude) {
this.longitude = longitude ;
this.latitude = latitude ;
}
public double getLongitude() {
return longitude ;
}
public double getLatitude() {
return latitude ;
}
}
and now:
#FXML
private void handleTracking() {
AtomicReference<Location> location = new AtomicReference<>(null);
Thread thread = new Thread(() -> {
try {
while (true) {
double[] ar[] = FileImport.getGpsPosition();
Location loc = new Location(ar[0], ar[1]);
if (location.getAndSet(loc) == null) {
Platform.runLater(() -> {
Location updateLoc = location.getAndSet(null);
webEngine.executeScript(""
+ "window.lat = " + updateLoc.getLatitude() + ";"
+ "window.lon = " + updateLoc.getLongitude() + ";"
+ "document.goToLocation(window.lat, window.lon);");
});
}
}
} catch (IOException exc) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
});
thread.setDaemon(true);
thread.start();
}
The way this works is that it creates a (thread-safe) holder for the current location, and updates it as fast as possible. When it updates it, it (atomically) also checks if the current value is null. If it's null, it schedules a UI update via Platform.runLater(). If not, it simply updates the value but schedules no new UI update.
The UI update (atomically) gets the current (i.e. most recent) value and sets it to null, indicating it is ready to receive a new UI update. It then processes the new update.
This way you "throttle" the UI updates so that new ones are only scheduled when the current one is being processed, avoiding flooding the UI thread with too many requests.
All JavaFX UI elements must be updated inside the FX application thread!
If using an additional thread be sure to use platform.Runlater() to update your UI elements!
I use jms:message-driven-channel-adapter and jms:outbound-channel-adapter in my project to get and put messages from/to IBM MQ . I need to get timestamps before and after each put and get. How could i achieve this. Please advise.
Please see my question updated below:
We need time taken for each put and get operation. So what i believed is, if I could get the timestamp in the following way, I will be able to achieve what I wanted.
1)At jms:message-driven-channel-adapter: Note timestamp before and after each get -> derive time taken for each get
2)At jms:outbound-channel-adapter: Note timestamp before and after each put -> derive time taken for each put
Please advise.
Thanks.
Well. It isn't clear what you want to have, because you always can get deal with System.currentTimeMillis().
But from other side Spring Integration maps a jMSTimestamp property of JmsMessage to the Message header jms_timestamp of incomming message in the <jms:message-driven-channel-adapter>.
Another point, that each Spring Integration Message has its own timestamp header.
So, if you switch on something like this:
<wire-tap channel="logger"/>
<logging-channel-adapter id="logger" log-full-message="true"/>
You always will see the timestamp fro each message in te logs, before it is sent to the channel.
UPDATE
OK. Thanks. Now it is more clear.
Well, for the outbound part (put in your case) I can say that your solution is laying with a custom ChannelInterceptor :
public class PutTimeInterceptor extends ChannelInterceptorAdapter {
private final Log logger = LogFactory.getLog(this.getClass());
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
logger.info("preSend time [" + System.currentTimeMillis() + "] for: " + message);
return message;
}
#Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
logger.info("postSend time [" + System.currentTimeMillis() + "] for: " + message);
}
}
<channel id="putToJmsChannel">
<interceptors>
<bean class="com.my.proj.int.PutTimeInterceptor"/>
</interceptors>
</channel>
<jms:outbound-channel-adapter channel="putToJmsChannel"/>
Keep in mind that ChannelInterceptor isn't statefull, so you should calculate the put time manually for each message.
Another option is <jms:request-handler-advice-chain>, when you should implements the custom AbstractRequestHandlerAdvice:
public class PutTimeRequestHandlerAdvice extends AbstractRequestHandlerAdvice {
private final Log logger = LogFactory.getLog(this.getClass());
#Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
long before = System.currentTimeMillis();
Object result = callback.execute();
logger.info("Put time: [" + System.currentTimeMillis() - before + "] for: " + message);
return result;
}
}
These are for the put only.
You can't derive the execution time for the get part, because it is a MessageListener, which is an event-driven component. When the message is in the queue you just receive it and that's all. There is no a hook to tie when the listener starts retrieve a message from the queue.
I'm developing a database application for Windows Phone 7.5 (mango). I trying (during tapping on a button) to update a textblock with the text "Searching..." This button performs a rather lengthy search in a big table and thus I want to inform the user. However everything I trying is failed! Here is one of the code snippets that I used. Is there any way to achieve this? Any help helping me understand what's wrong would be appreciated.
private void btnSearch_Tap(object sender, GestureEventArgs e)
{
workerThread = new Thread(new ThreadStart(turnVisibilityOn));
workerThread.Start();
while (!workerThread.IsAlive) ;
Thread.Sleep(500);
//Search database takes about 15 sec on windows phone device!
Procedures[] results = CSDatabase.RunQuery<Procedures>(#"select Code, Description from tblLibraries where Description like '%" +
textBox1.Text + "%' or Code like '%" + textBox1.Text + "%'");
this.MyListBox.ItemsSource = results;
// Of course this not work
Search1.Text = ""
}
private void turnVisibilityOn()
{
// Inform the user updating the Search1 textblock
// UIThread is a static class -Dispatcher.BeginInvoke(action)-
UIThread.Invoke(() => Search1.Text = "Searching...");
}
public static class UIThread
{
private static readonly Dispatcher Dispatcher;
static UIThread()
{
// Store a reference to the current Dispatcher once per application
Dispatcher = Deployment.Current.Dispatcher;
}
/// <summary>
/// Invokes the given action on the UI thread - if the current thread is the UI thread this will just invoke the action directly on
/// the current thread so it can be safely called without the calling method being aware of which thread it is on.
/// </summary>
public static void Invoke(Action action)
{
if (Dispatcher.CheckAccess())
action.Invoke();
else
Dispatcher.BeginInvoke(action);
}
}
I am not sure I understand the problem correctly.
The "Searching..." text does not show up? The
// Of course this not work
Search1.Text = ""
line doesn't work? (Why do you write "Of course this not work"? Why wouldn't it work?)
I don't understand why you change the text to "Searching..." in a background thread. You could do it in the UI thread, and make the time-consuming work in the background thread, something like this (I switched to using a ThreadPool):
private void btnSearch_Tap( object sender, GestureEventArgs e )
{
Search1.Text = "Searching..."
ThreadPool.QueueUserWorkItem(p =>
{
Procedures[] results = CSDatabase.RunQuery<Procedures>( #"select Code, Description from tblLibraries where Description like '%" +
textBox1.Text + "%' or Code like '%" + textBox1.Text + "%'" );
// Dispatch manipulation of UI elements:
Dispatcher.BeginInvoke( () =>
{
this.MyListBox.ItemsSource = results;
Search1.Text = "";
} );
} ) ;
}
You always have to manipulate the UI elements from the UI thread (on which the event handler runs) and you have to do the time-consuming work in a background thread.