I am converting my activity unto a fragment, then I received an error which I think I just need to add some code, what should I add
private class AsyncLogin extends AsyncTask<String, String, String> {
ProgressDialog pdLoading = new ProgressDialog(NotifMainActivity.this);
HttpURLConnection conn;
URL url = null;
errors are from the "NotifMainActivity.this" it says "in progress dialog cannot be applied" I have included an image for you to see the error in actual.Click here to see the image
Update. So I have change the "NotifMainActivity.this" with getActivity as suggested by sir Kashif Anwar but doing so made the function of the fragment to not work maybe you can provide me an alternative solution.
Here is the whole code of the fragment(this fragment is the activity of a recyclerview)
package com.capstone.jmilibraryapp;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class NotifMainActivity extends Fragment {
// CONNECTION_TIMEOUT and READ_TIMEOUT are in milliseconds
public static final int CONNECTION_TIMEOUT = 10000;
public static final int READ_TIMEOUT = 15000;
private RecyclerView mRVFishPrice;
private AdapterNotif mAdapter;
/*#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notifmainactivity);
//Make call to AsyncTask
new AsyncLogin().execute();
}*/
View myView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.activity_notifmainactivity, container, false);
return myView;
}
private class AsyncLogin extends AsyncTask<String, String, String> {
ProgressDialog pdLoading = new ProgressDialog(getActivity());
HttpURLConnection conn;
URL url = null;
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method will be running on UI thread
pdLoading.setMessage("\tLoading...");
pdLoading.setCancelable(false);
pdLoading.show();
}
#Override
protected String doInBackground(String... params) {
try {
// Enter URL address where your json file resides
// Even you can make call to php file which returns json data
url = new URL("http://192.168.1.101/notif.php");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return e.toString();
}
try {
// Setup HttpURLConnection class to send and receive data from php and mysql
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(READ_TIMEOUT);
conn.setConnectTimeout(CONNECTION_TIMEOUT);
conn.setRequestMethod("GET");
// setDoOutput to true as we recieve data from json file
conn.setDoOutput(true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return e1.toString();
}
try {
int response_code = conn.getResponseCode();
// Check if successful connection made
if (response_code == HttpURLConnection.HTTP_OK) {
// Read data sent from server
InputStream input = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
// Pass data to onPostExecute method
return (result.toString());
} else {
return ("unsuccessful");
}
} catch (IOException e) {
e.printStackTrace();
return e.toString();
} finally {
conn.disconnect();
}
}
#Override
protected void onPostExecute(String result) {
//this method will be running on UI thread
pdLoading.dismiss();
List<DataNotif> data=new ArrayList<>();
pdLoading.dismiss();
try {
JSONArray jArray = new JSONArray(result);
// Extract data from json and store into ArrayList as class objects
for(int i=0;i<jArray.length();i++){
JSONObject json_data = jArray.getJSONObject(i);
DataNotif fishData = new DataNotif();
fishData.NotifTitle= json_data.getString("notif_Title");
fishData.NotifMessage= json_data.getString("notif_Message");
// fishData.sizeName= json_data.getString("size_name");
fishData.Date= json_data.getString("notif_date");
data.add(fishData);
}
// Setup and Handover data to recyclerview
mRVFishPrice = (RecyclerView)myView.findViewById(R.id.fishPriceList);
mAdapter = new AdapterNotif(getActivity(), data);
mRVFishPrice.setAdapter(mAdapter);
mRVFishPrice.setLayoutManager(new LinearLayoutManager(getActivity()));
} catch (JSONException e) {
Toast.makeText(getActivity(), e.toString(), Toast.LENGTH_LONG).show();
Toast.makeText(getActivity(), "There maybe some error try again",Toast.LENGTH_LONG).show();
}
}
}
}
You should use getActivity() instead of class name,
like this
ProgressDialog pd = new ProgressDialog( getActivity() );
Hope it helps!
Related
I have a simple login servlet which checks the email and password if the user is registered or not , now I'm trying to work on a JavaFX app which does the same job by reading from the servlet the information, my servlet works perfect but I don't know how to interact with my servlet from JavaFX , how can I send a response from the servlet to JavaFX ? I've searched a lot but couldn't find anything
Here's my servlet :
import java.io.*;
import java.security.Principal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
#WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
//int attempts = 3;
Date date;
/**
*
*/
private static final long serialVersionUID = -5498866193863633001L;
/**
* HashMap to store all users credentials
*/
private final Map<String, String> credentialsPairs = new HashMap<>();
#Override
public void init(ServletConfig config) throws ServletException {
String delimiter = ",";
String line = "";
/**
* Credentials file will be there in WEB-INF directory as it provide secured
* access only.
*/
String credentialFile = "/WEB-INF/accounts.txt";
/**
* Read the file and prepare Map with username as key and password as value We
* have put this code in init method as it is called once only that will avoid
* overhead of iterating values from file for each request
*/
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
ServletContext context = config.getServletContext();
try {
/**
* Open stream of file
*/
is = context.getResourceAsStream(credentialFile);
if (is != null) {
/**
* Read the file line by line and store email as a key and password as value
*/
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
while ((line = br.readLine()) != null) {
String[] credentials = line.split(delimiter);
// credentials[0] is email and credentials[1] is password
credentialsPairs.put(credentials[0], credentials[1]);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void service(HttpServletRequest request, HttpServletResponse response) throws
IOException, ServletException {
/**
* Get user entered credentials
*/
String userEmail = request.getParameter("email");
String userPassword = request.getParameter("password");
PrintWriter out = response.getWriter();
boolean isValidUser = false;
/**
* Get value from Map for user entered email address.
*/
String password = credentialsPairs.get(userEmail);
/**
* If User with entered email address found then we will get password for that
* user
*/
if (password != null) {
/**
* Compare password entered by user with one that is retrieved from file
*/
if (password.equals(userPassword)) {
isValidUser = true;
}
}
}
HttpSession session = request.getSession();
if (isValidUser) {
//HttpSession session = request.getSession();
session.setAttribute("email", userEmail);
//request.getRequestDispatcher("welcome.jsp").include(request, response);
response.sendRedirect("welcome.jsp");
//response.setContentType("text/html");
//response.sendError(HttpServletResponse.SC_FOUND,"Hello");
}
else {
int loginAttempt;
if (session.getAttribute("loginCount") == null)
{
session.setAttribute("loginCount", 0);
loginAttempt = 0;
}
else
{
loginAttempt = (Integer) session.getAttribute("loginCount");
}
//this is 3 attempt counting from 0,1,2
if (loginAttempt >= 2 )
{
long lastAccessedTime = session.getLastAccessedTime();
date = new Date();
long currentTime = date.getTime();
long timeDiff = currentTime - lastAccessedTime;
// 20 minutes in milliseconds
if (timeDiff >= 1200000)
{
//invalidate user session, so they can try again
session.invalidate();
}
else
{
// Error message
session.setAttribute("message","You have exceeded the 3 failed login
attempt. Please try loggin in in 20 minutes.");
//request.getRequestDispatcher("fail.jsp");
out.println("You have exceeded the 3 failed login attempt. Please
try loggin in in 20 minutes.");
}
}
else
{
loginAttempt++;
int allowLogin = 3-loginAttempt;
session.setAttribute("message","loginAttempt= "+loginAttempt+". Invalid
username or password. You have "+allowLogin+" attempts remaining. Please try again!");
out.println("Invalid email or password , please try again");
final String message = "The requested page not found";
response.sendError(HttpServletResponse.SC_NOT_FOUND,message);
}
session.setAttribute("loginCount",loginAttempt);
}
}
public void destroy() {
/**
* Free up the map
*/
credentialsPairs.clear();
}
}
Here's my JavaFX code :
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HelloApplication extends Application {
TextField tName;
TextField tPassword;
ComboBox comboBox;
Button login;
VBox vBox;
BorderPane borderPane;
URL url = null;
HttpURLConnection con = null;
int flag =0;
#Override
public void start(Stage stage) throws IOException {
stage.setTitle("login Page");
stage.setResizable(false);
setDesign();
comboBox.setOnAction(e->{
if(comboBox.getSelectionModel().isSelected(0)){
flag =0;
}else if (comboBox.getSelectionModel().isSelected(1)){
flag =1;
}
});
login.setOnAction(e->{
if(flag==0){
try {
System.out.println("get");
String name =tName.getText();
String password =tPassword.getText();
url = new URL("http://localhost:8080/try8_war_exploded/login?
email="+name+"&password="+password);
con = (HttpURLConnection)url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Content-Type","text/html");
con.setConnectTimeout(5000);
con.setReadTimeout(5000);
} catch(Exception c) {
c.printStackTrace();
}
}
else {
}
int status = 0;
StringBuffer data = new StringBuffer();
try {
status = con.getResponseCode();
if(status > 299) {
System.out.println("error "+status);
System.out.println(con.getErrorStream());
return;
}
BufferedReader in = new BufferedReader(new
InputStreamReader(con.getInputStream()));
String str = null;
while((str=in.readLine()) != null) data.append(str);
System.out.println("here");
System.out.println(str);
in.close();
con.disconnect();
} catch(Exception c) {
c.printStackTrace();
}
});
Scene scene =new Scene(borderPane,350,180);
stage.setScene(scene);
stage.show();
}
public void setDesign(){
borderPane = new BorderPane();
borderPane.setPadding(new Insets(20,20,20,20));
vBox = new VBox(10);
vBox.setAlignment(Pos.CENTER);
HBox hBox1 =new HBox(5);
hBox1.setAlignment(Pos.CENTER);
HBox hBox2 =new HBox(5);
hBox2.setAlignment(Pos.CENTER);
Label label1= new Label("E-mail");
tName = new TextField();
tName.setMaxWidth(150);
hBox1.getChildren().addAll(label1,tName);
Label label2= new Label("Password");
tPassword = new TextField();
tPassword.setMaxWidth(150);
hBox2.getChildren().addAll(label2,tPassword);
comboBox = new ComboBox();
comboBox.getItems().add("Get");
comboBox.getItems().add("Post");
comboBox.getSelectionModel().selectFirst();
login = new Button("Login");
vBox.getChildren().addAll(hBox1,hBox2,comboBox,login);
borderPane.setTop(vBox);
}
public static void main(String[] args) {
launch();
}
}
I'm posting the entire codes so that everything is clear
We need an example on how to test ReactiveKafkaConsumerTemplate and ReactiveKafkaProducerTemplate with an embedded-kafka-broker. Thanks.
CORRECT CODE IS HERE AFTR DISCUSSION
You can have your custom de-serializer accordingly to use custom ReactiveKafkaConsumerTemplate
Custom Serialiser:
import org.apache.kafka.common.serialization.Serializer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class EmployeeSerializer implements Serializer<Employee> {
#Override
public byte[] serialize(String topic, Employee data) {
byte[] rb = null;
ObjectMapper mapper = new ObjectMapper();
try {
rb = mapper.writeValueAsString(data).getBytes();
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return rb;
}
}
Use it part of embedded-kfka-reactive test:
import java.util.Map;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.connect.json.JsonSerializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.kafka.core.reactive.ReactiveKafkaProducerTemplate;
import org.springframework.kafka.support.converter.MessagingMessageConverter;
import org.springframework.kafka.test.condition.EmbeddedKafkaCondition;
import org.springframework.kafka.test.context.EmbeddedKafka;
import org.springframework.kafka.test.utils.KafkaTestUtils;
import reactor.kafka.sender.SenderOptions;
import reactor.kafka.sender.SenderRecord;
import reactor.test.StepVerifier;
#EmbeddedKafka(topics = EmbeddedKafkareactiveTest.REACTIVE_INT_KEY_TOPIC,
brokerProperties = { "transaction.state.log.replication.factor=1", "transaction.state.log.min.isr=1" })
public class EmbeddedKafkareactiveTest {
public static final String REACTIVE_INT_KEY_TOPIC = "reactive_int_key_topic";
private static final Integer DEFAULT_KEY = 1;
private static final String DEFAULT_VERIFY_TIMEOUT = null;
private ReactiveKafkaProducerTemplate<Integer, Employee> reactiveKafkaProducerTemplate;
#BeforeEach
public void setUp() {
reactiveKafkaProducerTemplate = new ReactiveKafkaProducerTemplate<>(setupSenderOptionsWithDefaultTopic(),
new MessagingMessageConverter());
}
private SenderOptions<Integer, Employee> setupSenderOptionsWithDefaultTopic() {
Map<String, Object> senderProps = KafkaTestUtils
.producerProps(EmbeddedKafkaCondition.getBroker().getBrokersAsString());
SenderOptions<Integer, Employee> senderOptions = SenderOptions.create(senderProps);
senderOptions = senderOptions.producerProperty(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "reactive.transaction")
.producerProperty(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true)
.producerProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class.getName())
;
return senderOptions;
}
#Test
public void test_When_Publish() {
Employee employee = new Employee();
ProducerRecord<Integer, Employee> producerRecord = new ProducerRecord<Integer, Employee>(REACTIVE_INT_KEY_TOPIC, DEFAULT_KEY, employee);
StepVerifier.create(reactiveKafkaProducerTemplate.send(producerRecord)
.then())
.expectComplete()
.verify();
}
#AfterEach
public void tearDown() {
reactiveKafkaProducerTemplate.close();
}
}
The tests in the framework use an embedded kafka broker.
https://github.com/spring-projects/spring-kafka/tree/main/spring-kafka/src/test/java/org/springframework/kafka/core/reactive
#EmbeddedKafka(topics = ReactiveKafkaProducerTemplateIntegrationTests.REACTIVE_INT_KEY_TOPIC, partitions = 2)
public class ReactiveKafkaProducerTemplateIntegrationTests {
...
added correct serialised with a non-transactional producer. please see the code on top of this page for the answer.
I'm new to programming, what i want to do is read/write data that are stored in a ObservableList, the data are from Student class which are Strings (firstName and lastName), and also i have a TableView to display the data.
Here is my code:
Student Class:
import java.io.Serializable;
import javafx.beans.property.SimpleStringProperty;
public class Student implements Serializable {
private SimpleStringProperty fname;
private SimpleStringProperty lname;
Student() {
this("","");
}
Student(String fn, String ln) {
this.fname = new SimpleStringProperty(fn);
this.lname = new SimpleStringProperty(ln);
}
public void setFirstName(String f) {
fname.set(f);
}
public String getFirstName() {
return fname.get();
}
public void setLastName(String l) {
lname.set(l);
}
public String getLastName() {
return lname.get();
}
#Override
public String toString() {
return String.format("%s %s", getFirstName(), getLastName());
}
}
And here is my code for inputing data using TextFields:
#FXML
ObservableList<Student> data = FXCollections.observableArrayList();
//Just to input the data
#FXML
private void handleButtonAction(ActionEvent event) {
if(!"".equals(txtFirstName.getText()) && !"".equals(txtLastName.getText())){
data.add(
new Student(txtFirstName.getText(),
txtLastName.getText()
));
}
txtFirstName.clear();
txtLastName.clear();
// System.out.println(data);
}
And here is the problem...
Reading/Writing the ObservableList:
#FXML
private void HandleMenuSaveAction(ActionEvent event) {
try {
FileOutputStream f = new FileOutputStream(new File("saveStudentList.txt"));
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(data);
o.close();
f.close();
System.out.println("File Saved Successfully.");
} catch (FileNotFoundException ex) {
System.err.println("Save: File not found.");
} catch (IOException ex) {
System.err.println("Save: Error initializing stream.");
ex.printStackTrace();
}
}
#FXML
private void HandleMenuLoadAction(ActionEvent event) {
try {
FileInputStream fi = new FileInputStream(new File("saveStudentList.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
data = (ObservableList) oi.readObject();
System.out.println(data.toString());
//Refresh the Table everytime we load data
oi.close();
fi.close();
} catch (FileNotFoundException ex) {
System.err.println("Load: File not found.");
} catch (IOException ex) {
System.err.println("Load: Error initializing stream.");
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
This is causing me java.io.NotSerializableException,
Does anyone have an idea how to change my code so that it works?
implement custom serialisation for the Student object (see https://stackoverflow.com/a/7290812/2991525) and copy the contents of the ObservableList to a ArrayList to create a serializable list:
public class Student implements Serializable {
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeObject(getFirstName());
out.writeObject(getLastName());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
fname = new SimpleStringProperty((String) in.readObject());
lname = new SimpleStringProperty((String) in.readObject());
}
...
}
(De)serialisation example
ObservableList<Student> students = FXCollections.observableArrayList();
for(int i = 0; i < 100; i++) {
students.add(new Student("Mark"+i, "Miller"+i));
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// write list
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(new ArrayList<>(students));
}
students = null; // make sure the old reference is no longer available
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// read list
try (ObjectInputStream ois = new ObjectInputStream(bis)){
students = FXCollections.observableList((List<Student>) ois.readObject());
}
System.out.println(students);
Your question is a little confusing as you are using object serialization but the filename you chose is a .txt file. Serialized objects are not human-readable like a plain text file would be.
Fabian's answer above is great if you do want to use serialization. However, if you were looking to generate a simple text file, take a look at the following sample program:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
ObservableList<Student> students = FXCollections.observableArrayList();
// Create some sample students
students.addAll(
new Student("Roger", "Rabbit"),
new Student("Elmer", "Fudd"),
new Student("Daffy", "Duck"),
new Student("Foghorn", "Leghorn"),
new Student("Betty", "Boop")
);
// Write the list to a text file
try {
writeToTextFile("students.txt", students);
} catch (IOException e) {
e.printStackTrace();
}
// Now, read the file into a new List<Student>
List<Student> inputStudents = null;
try {
inputStudents = readStudents("students.txt");
} catch (IOException e) {
e.printStackTrace();
}
// Print out the student names
if (inputStudents != null) {
for (Student student : inputStudents) {
System.out.println(student.toString());
}
}
}
/**
* Write the list of students to a simple text file with first and last names separated by a comma
*/
private static void writeToTextFile(String filename, ObservableList<Student> students)
throws IOException {
FileWriter writer = new FileWriter(filename);
for (Student student : students) {
writer.write(student.getFirstName() + "," + student.getLastName() + "\n");
}
writer.close();
}
/**
* Read the comma-separated list of student names from the text file
*/
private static List<Student> readStudents(String filename)
throws IOException {
List<Student> students = new ArrayList<>();
BufferedReader reader = Files.newBufferedReader(Paths.get(filename));
String line;
while ((line = reader.readLine()) != null) {
String[] names = line.split(",");
// Add the student to the list
students.add(new Student(names[0], names[1]));
}
return students;
}
}
This generates a very simple text file with each student's first and last name on its own line, separated by a comma. It is then read back into a new list, ready for use in your TableView.
If you do decide to go this route, I recommend finding a good CSV library to handle the reading/writing of CSV (comma-separated values) files. Apache Commons has a decent one available.
Here is an example that reads and writes data from an ObservableList of people (first names and last names) to a file in json format.
Example file content for saved json data from the list
[ {
"firstName" : "Fred",
"lastName" : "Flintstone"
}, {
"firstName" : "Wilma",
"lastName" : "Flintstone"
}, {
"firstName" : "Barney",
"lastName" : "Rubble"
} ]
Implementation Notes
Data items are stored as People records.
An ObservableList backs a TableView and holds records of the data items.
The 3rd party Jackson library is used to serialize and deserialize the list of data to JSON, which is stored and read from a file.
On startup, the application generates a temporary file name used to store the saved data file for the lifetime of the application.
On shutdown, the temporary save file is automatically deleted.
The module-info allows the Jackson databind module to perform reflection on the package containing the record definition of the items to be saved.
Before saving and restoring the data items, they are temporarily stored in an ArrayList rather than an ObservableList. This is done because you don't want to try to serialize an entire ObservableList. The ObservableList will also have entries for change listeners that may be attached to the list. You don't want to serialize those listeners.
The ListSerializer class which performs the serialization and deserialization using Jackson uses Java generics so it can save and load any type of data which can be serialized via Jackson (including the Person record in the example). The generics add some complications in the code for determining the correct types to be used in the serialization and deserialization process. The generics do allow for a more generic solution, so, in general, I think the addition of the generic solution is worth the tradeoff of additional complications in the implementation.
The ListSerializerController demonstrates the usage of the ListSerializer to save and load data to an ObservableList backing a TableView.
Maven is used as the build system.
JRE 18 and JavaFX 18 are used as the runtime.
Example Solution
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>ListSerialization</artifactId>
<version>1.0-SNAPSHOT</version>
<name>ListSerialization</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.8.1</junit.version>
<javafx.version>18</javafx.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>18</source>
<target>18</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/java/module-info.java
module com.example.listserialization {
requires javafx.controls;
requires com.fasterxml.jackson.databind;
opens com.example.listserialization to com.fasterxml.jackson.databind;
exports com.example.listserialization;
}
src/main/java/com/example/listserialization/ListSerializerApp.java
package com.example.listserialization;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ListSerializerApp extends Application {
private Path peoplePath;
#Override
public void init() throws IOException {
peoplePath = Files.createTempFile(
"people",
".json"
);
peoplePath.toFile().deleteOnExit();
System.out.println("Using save file name: " + peoplePath);
}
#Override
public void start(Stage stage) throws IOException {
ListSerializerController listSerializerController = new ListSerializerController(
peoplePath
);
stage.setScene(
new Scene(
listSerializerController.getLayout()
)
);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
src/main/java/com/example/listserialization/Person.java
package com.example.listserialization;
record Person(String firstName, String lastName) {}
src/main/java/com/example/listserialization/ListSerializer.java
package com.example.listserialization;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
public class ListSerializer<T> {
private static final ObjectMapper mapper =
new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT);
private final CollectionType listType;
public ListSerializer(Class<T> listItemClass) {
listType =
mapper.getTypeFactory()
.constructCollectionType(
ArrayList.class,
listItemClass
);
}
public void serializeList(Path path, ArrayList<T> list) throws IOException {
Files.writeString(
path,
mapper.writeValueAsString(
list
)
);
}
public ArrayList<T> deserializeList(Path path) throws IOException {
return mapper.<ArrayList<T>>readValue(
Files.readString(path),
listType
);
}
}
src/main/java/com/example/listserialization/ListSerializerController.java
package com.example.listserialization;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
public class ListSerializerController {
private final ListSerializer<Person> listSerializer = new ListSerializer<>(
Person.class
);
private final Person[] TEST_PEOPLE = {
new Person("Fred", "Flintstone"),
new Person("Wilma", "Flintstone"),
new Person("Barney", "Rubble")
};
private final TableView<Person> tableView = new TableView<>(
FXCollections.observableArrayList(
TEST_PEOPLE
)
);
private final Path peoplePath;
private final Parent layout;
public ListSerializerController(Path peoplePath) {
this.peoplePath = peoplePath;
layout = createLayout();
}
private Parent createLayout() {
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(p ->
new ReadOnlyStringWrapper(
p.getValue().firstName()
).getReadOnlyProperty()
);
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(p ->
new ReadOnlyStringWrapper(
p.getValue().lastName()
).getReadOnlyProperty()
);
//noinspection unchecked
tableView.getColumns().setAll(firstNameCol, lastNameCol);
tableView.setPrefHeight(150);
Button save = new Button("Save");
save.setOnAction(this::save);
Button clear = new Button("Clear");
clear.setOnAction(this::clear);
Button load = new Button("Load");
load.setOnAction(this::load);
Button restoreDefault = new Button("Default Data");
restoreDefault.setOnAction(this::restoreDefault);
HBox controls = new HBox(10, save, clear, load, restoreDefault);
VBox layout = new VBox(10, controls, tableView);
layout.setPadding(new Insets(10));
return layout;
}
public Parent getLayout() {
return layout;
}
private void save(ActionEvent e) {
try {
listSerializer.serializeList(
peoplePath,
new ArrayList<>(
tableView.getItems()
)
);
System.out.println("Saved to: " + peoplePath);
System.out.println(Files.readString(peoplePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void clear(ActionEvent e) {
tableView.getItems().clear();
System.out.println("Cleared data in UI");
}
private void load(ActionEvent e) {
try {
if (!peoplePath.toFile().exists()) {
tableView.getItems().clear();
System.out.println("Saved data file does not exist, clearing data: " + peoplePath);
return;
}
tableView.getItems().setAll(
listSerializer.deserializeList(peoplePath)
);
System.out.println("Loaded data from: " + peoplePath);
System.out.println(Files.readString(peoplePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void restoreDefault(ActionEvent e) {
tableView.getItems().setAll(TEST_PEOPLE);
System.out.println("Restored default data in UI");
}
}
I am making an IRC client in a javafx fxml project.
I am having difficulties understanding the structure of the project. I get the MVC pattern however i don't know where i have to position the main code part of the client(object initialisation and communication start).
I want to be able to update a textarea with a String from a serverConnection object when the server sends a message.
So where do i position the code and how do i notify my controller that it has to update its text passing the String simultaneously?
This is the application class:
package jircclient;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.concurrent.*;
public class JircClient extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setTitle("jircClient");
stage.setScene(scene);
stage.show();
System.out.println("Stage SET!");
//This is where i thought that i could write the main part of the program
//but the stage will not load unless the start method finishes
//Creating a serverConnetion is not a problem since it's only an object
//but starting the communication does not let the method we are in to exit
//since it sticks in a while loop forever until i probably cancel it using a
//button (not implemented yet)
//serverConnection server1 = new serverConnection();
//server1.startCommunication();
}
public static void main(String[] args) throws Exception
{
//In this method i cannot go further unless i close the stage-window
//The purpose of it is to launch the start method
Application.launch(args);
}
}
This is the controller class:
package jircclient;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;
public class FXMLDocumentController implements Initializable
{
#FXML
private TextArea txt;
#FXML
private void handleButtonAction(ActionEvent event)
{
//In here i will be handling events, however i will need to have
//access to serverConnection objects
System.out.println("You clicked me!");
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
}
}
This is the serverConnection class:
package jircclient;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
public class serverConnection
{
//VARIABLES
private clientInfo client;
private String server_to_connect = "someserver";
private String channel_to_connect = "#somechannel";
private String serv_resp;
private int port = 6667;
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
private ArrayList<channel> channels;
//DEFAULT CONSTRUCTOR
public serverConnection() {client = new clientInfo("test1", "test1", "test1");}
//FULL CONSTRUCTOR
public serverConnection(String server_to_connect, int port, String channel_to_connect) throws IOException
{
client = new clientInfo("test1", "test1", "test1");
this.server_to_connect = server_to_connect;
this.port = port;
this.channel_to_connect = server_to_connect;
try
{
//Creating socket connection
this.socket = new Socket(this.server_to_connect,port);
//Socket output writer
writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
//Socket input writer
reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
serv_resp = null;
System.out.println("Connection established.");
}
catch(UnknownHostException exc)
{
System.out.println("ERROR: "+ exc.toString());
}
catch(IOException exc)
{
System.out.println("ERROR: "+ exc.toString());
}
finally
{
System.out.println("Closing connection.");
socket.close();
}
}
//server response getter
public String getServerResponse()
{
return serv_resp;
}
//Introduction to server and listen
public void startCommunication() throws IOException
{
this.socket = new Socket(this.server_to_connect,port);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
serv_resp = null;
writer.write("NICK " + client.getClientNickname() + "\r\n");
writer.write("USER " + client.getClientLogin() + " 0 * : " +
client.getClientRealName() + "\r\n");
while ((serv_resp = reader.readLine()) != null)
{
System.out.println(serv_resp);
//FXMLDocumentController.txt.setText(serv_resp);
if (serv_resp.indexOf("004") >= 0)
{
break;
}
else if (serv_resp.indexOf("433") >= 0)
{
System.out.println("Nickname is already in use.");
return;
}
}
//Get channel list
writer.write("LIST \r\n");
writer.flush();
//Join desired client
writer.write("JOIN " + channel_to_connect + "\r\n");
writer.flush();
//keep listening
while ((serv_resp = reader.readLine()) != null)
{
//FXMLDocumentController.txt.setText(serv_resp);
if (serv_resp.startsWith("PING "))
{
this.pingPong();
} else
{
System.out.println(serv_resp);
}
}
}
//Ping respond
public void pingPong() throws IOException
{
writer.write("PONG " + serv_resp.substring(5) + "\r\n");
writer.flush();
}
}
I believe there is no need to add the fxml document since it's big and there is no need.
I also have to state that oracle tutorials were not helpful as they only use the event handling in the controller and they don't implement any other logic.
Here is one possible way to do this:
Give your ServerConnection class a callback to call when it receives a message:
public class ServerConnection {
private Consumer<String> messageCallback ;
public void setMessageCallback(Consumer<String> messageCallback) {
this.messageCallback = mesasgeCallback ;
}
// other fields and methods as before...
public void startCommunication() throws IOException {
// code as before ...
while ((servResp = reader.readLine()) !=null) {
if (messageCallback != null) {
messageCallback.accept(servResp);
}
// etc....
}
// etc
}
}
Now instantiate your ServerConnection class from the controller and pass it a callback:
public class FXMLDocumentController {
ServerConnection serverConnection ;
// ...
public void initialize() {
serverConnection = new ServerConnection();
serverConnection.setMessageCallback(message ->
Platform.runLater(() -> txt.appendText(message+"\n")));
Thread serverThread = new Thread(() -> serverConnection.startListening());
serverThread.setDaemon(true); // thread will not stop application from exiting
serverThread.start();
}
// ...
}
How i can refresh listfragment? no tutorials from net work :(
i have no idea, how reload this listfragment. I tried reload by transaktionmanager, but its colide with ActionBar.TabListener, this is not support.v4. how can i retrieve new data from loadermanager and update listfragment?
Activity:
package sk.test;
import android.app.ActionBar;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import java.util.List;
import java.util.Locale;
import sk.test.frags.TodoFragment;
import sk.test.prefs.EditPreferences;
import sk.test.task.DataListLoader;
import sk.test.xml.Item;
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
private TodoFragment todoFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the action bar.
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have
// a reference to the Tab.
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the callback (listener) for when
// this tab is selected.
actionBar.addTab(
actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.action_settings:
startActivity(new Intent(getApplicationContext(), EditPreferences.class));
return true;
case R.id.refresh:
//HERE CODE FOR RELOAD todoFragment.reloadData() ????
return true;
default:
return true;
}
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private boolean wantDone = false;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
todoFragment = new TodoFragment();
this.wantDone = position == 0 ? false : true;
Bundle args = new Bundle();
args.putBoolean(TodoFragment.TASK_TYPE, this.wantDone);
todoFragment.setArguments(args);
return todoFragment;
}
#Override
public int getCount() {
return 2;
}
public boolean getWantDone(){
return this.wantDone;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.todotask_planned).toUpperCase(l);
case 1:
return getString(R.string.todotask_done).toUpperCase(l);
}
return null;
}
}
}
ListFragment:
package sk.test;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import java.util.List;
import sk.test.adapter.CustomArrayAdapter;
import sk.test.task.DataListLoader;
import sk.test.xml.Item;
/**
* Created by Peter on 29.7.2013.
*/
public class TodoFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Item>> {
public static String TASK_TYPE = "taskType";
private static final String XML_SOURCE = "http://******/";
private boolean wantDone = false;
CustomArrayAdapter mAdapter;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.i("TODOLIST", "DataListFragment.onActivityCreated");
this.wantDone = getArguments().getBoolean(TASK_TYPE);
// Initially there is no data
setEmptyText("No Data Here");
// Create an empty adapter we will use to display the loaded data.
mAdapter = new CustomArrayAdapter(getActivity());
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
setHasOptionsMenu(true);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("TODOLIST", "Item clicked: " + id);
}
#Override
public Loader<List<Item>> onCreateLoader(int i, Bundle bundle) {
Log.i("TODOLIST", "DataListFragment.onCreateLoader");
return new DataListLoader(getActivity(), this.wantDone);
}
#Override
public void onLoadFinished(Loader<List<Item>> listLoader, List<Item> items) {
mAdapter.setData(items);
Log.i("TODOLIST", "DataListFragment.onLoadFinished");
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override
public void onLoaderReset(Loader<List<Item>> listLoader) {
}
public void reloadData(){
//UPDATE LIST.. HOW?
}
}
Loader:
package sk.test;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import sk.test.commons.Commons;
import sk.test.xml.Item;
import sk.test.xml.Response;
/**
* Created by Peter Chovan on 29.7.2013.
*/
public class DataListLoader extends AsyncTaskLoader<List<Item>> {
private List<Item> todoTasks;
private boolean wantDone;
SharedPreferences prefs;
public DataListLoader(Context context, boolean wantDone) {
super(context);
this.wantDone = wantDone;
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
}
#Override
public List<Item> loadInBackground() {
Log.i("TODOLIST", "DataListLoader.loadInBackground");
String xmlData = getXmlData(prefs.getString("service_url", "http://*****/"));
List<Item> entries = new ArrayList<Item>();
String state = wantDone ? "WANT DONE" : "WANT PLANNED";
if (xmlData != null) {
xmlData = xmlData.replaceAll("<([^/]+?)/>", "<$1> </$1>");
Serializer serializer = new Persister();
try {
Response res = serializer.read(Response.class, xmlData, false);
for (Item i : res.getItems().getItem()) {
if (i.isDone() == wantDone) {
entries.add(i);
}
}
} catch (Exception e) {
for (StackTraceElement s : e.getStackTrace()) {
Log.e("TEST serializer", s.toString());
}
}
} else {
Log.e("TODOLIST DATA", "NULL");
}
return entries;
}
public String getXmlData(String uri) {
try {
URL url = new URL(uri);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
con.setRequestProperty("Accept", "application/xml");
Map<String, String> params = new HashMap<String, String>();
params.put("user", prefs.getString("service_login", "devel"));
params.put("pass", prefs.getString("service_password", "devel"));
params.put("class", "GetList");
OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream());
wr.write(Commons.getRequestData(params)); //add request params
wr.flush();
String xmlData = readStream(con.getInputStream());
wr.close();
con.disconnect();
return xmlData;
} catch (Exception e) {
for (StackTraceElement s : e.getStackTrace()) {
Log.e("TODOLIST", "doInBackground" + s.toString());
}
}
return null;
}
private String readStream(InputStream in) {
BufferedReader reader = null;
String result = "";
try {
reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
result += line + "\n";
}
return result;
} catch (IOException e) {
for (StackTraceElement s : e.getStackTrace()) {
Log.e("TODOLIST", "ReadStream || " + s.toString());
}
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
for (StackTraceElement s : e.getStackTrace()) {
Log.e("TODOLIST", "ReadStream || " + "Error while closing Reader");
}
}
}
}
return null;
}
/**
* Called when there is new data to deliver to the client. The
* super class will take care of delivering it; the implementation
* here just adds a little more logic.
*/
#Override
public void deliverResult(List<Item> listOfData) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (listOfData != null) {
onReleaseResources(listOfData);
}
}
List<Item> oldApps = listOfData;
todoTasks = listOfData;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(listOfData);
}
// At this point we can release the resources associated with
// 'oldApps' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
/**
* Handles a request to start the Loader.
*/
#Override
protected void onStartLoading() {
if (todoTasks != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(todoTasks);
}
if (takeContentChanged() || todoTasks == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
#Override
public void onCanceled(List<Item> apps) {
super.onCanceled(apps);
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
/**
* Handles a request to completely reset the Loader.
*/
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (todoTasks != null) {
onReleaseResources(todoTasks);
todoTasks = null;
}
}
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
*/
protected void onReleaseResources(List<Item> apps) {
}
}
Use this to restart the Loader: getLoaderManager().restartLoader(0, null, this);