Reputation: 1267
Basically, I'm taking some links from a website in a thread and adding it to an ArrayList. But when I check the size of the ArrayList it returns 0. Is there anyway to make the new values in it reflect outdie the thread.
Here is the code:
lsiter.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final ArrayList<String>LinkAr = new ArrayList<String>();
new Thread(new Runnable() {
@Override
public void run() {
Document doc = null;
try {
doc = Jsoup.connect(message).get();
} catch (IOException e) {
e.printStackTrace();
}
//Title
Element TitleSpan = doc.select("span.detail-info-right-title-font").first();
String TitleString = TitleSpan.text();
//Chaps Names
Elements Chaps = doc.getElementsByClass("title3");
for(Element item: Chaps){
LinkAr.add(item.text());
}
}
}).start();
int pos = parent.getPositionForView(view);
String ClickedVal = (String) parent.getItemAtPosition(position);
Toast.makeText(ChapterPage.this, LinkAr.size() + "@ " + ClickedVal, Toast.LENGTH_SHORT).show();
}
});
Upvotes: 1
Views: 405
Reputation: 338336
Caveat: I do not know Android. I'm speaking of Java generally here.
Caveat: As others commented, it may or may not make sense for your app to do your particular workload on a background thread. I will ignore that issue, and focus on answering how to produce data on a background thread. It is up to the reader to decide if doing so is appropriate within their particular app.
In Java, we no longer need manage threads manually. The Executors framework (Oracle tutorial) was built into Java 5 to free app developers from the tricky work of juggling threads.
ExecutorService
as global variableEstablish your executor service, backed by a thread pool. Keep a reference to that executor service somewhere in your app. This executor service will be used repeatedly.
To configure an ExecutorService
object, use factory methods found in the Executors
class.
ExecutorService executorService = Executors.newCachedThreadPool() ;
onItemClick
codeIn your onItemClick
method, define the work to be done as a Callable
. A Callable
returns a value. In your case, you want to return a List
or Set
of URLs. There is no need for the background thread to access a list in your onItemClick
code. Just let the background thread generate a new list or set, and return that list/set back to the calling code. Cleaner separation.
Apparently you are using String
objects to represent your URLs. But we have a class for that: URL
. Let's produce a list of URL
objects, specified as List < URL >
. We expect our Callable
object to eventually produce and return a List < URL >
. If something goes wrong with our task, we expect the Callable
to return an empty list rather than throw an exception (if that is appropriate to your business logic).
Callable < List < URL > > urlFetch =
( ) -> {
List < URL > urls = List.of(); // Default to empty list.
// … do the work to fetch your URLs and build a list.
return urls;
};
Submit that task to the executor service. The executor service then assigns that task to a thread in its backing pool. Wo do not care about how that thread assignment is done; perhaps a new thread is started, or perhaps an existing idle thread is re-used, not our problem.
Upon submitting our task to the executor service, we get back a Future
object. This Future
is our handle back to our task. We can ask the Future
about the status of that work being done.
Future < List < URL > > futureUrls = executorService.submit( urlFetch );
// … do other work while you wait.
Later, whenever you want, you can check the status of our task. The Future
may report that the task is done or is cancelled. Otherwise, we can assume the task is incomplete, and still be worked on.
If the Future
reports as complete, we can retrieve its results. Here that would be a List< URL >
object.
// … check to see if the URL fetch work has been done yet.
if ( futureUrls.isDone() )
{
List < URL > urls = futureUrls.get();
// … Use your list of URLs.
} else if ( futureUrls.isCancelled() )
{
// … deal with cancellation.
} // else neither done nor cancelled, so still being worked. Check back again later.
That syntax above is incomplete. We must trap for certain exceptions. Let's add the try-catch.
// … check to see if the URL fetch work has been done yet.
if ( futureUrls.isDone() )
{
try
{
List < URL > urls = futureUrls.get();
// …Use your URL collection…
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
catch ( ExecutionException e )
{
e.printStackTrace();
}
} else if ( futureUrls.isCancelled() )
{
// … deal with cancellation.
} // else neither done nor cancelled, so still being worked. Check back again later.
If you want the calling thread to wait indefinitely until the task finishes, call Future::get
. But avoid doing this in the main user-interface thread, as your app becomes unresponsive to the user, appearing to have frozen/crashed.
If you want the calling thread to wait briefly for the task to finish, call Future::get
while passing the amount of time to wait. When the wait time expires with our task still not done, the calling thread moves on to other work. Again, do not wait for long in the UI thread, as the UI freezes for that amount of time.
Important: Never access the user-interface from a background thread. Bad things may or may not happen.
Lastly, be sure to gracefully shutdown your executor service before your app ends. Otherwise its backing pool of threads may continue indefinitely.
Upvotes: 2