Since Webworkers is only implemented from Android 4.4 onwards, is it possible to have a wrapper in the application code that provides this functionality to the contained WebView?
An example on how to solve this would really help.
Thanks,
Rajath
I guess you are talking about running a javascript code block in the background, i.e. different thread. Had tried doing that using RhinoJS on Android. Tested on Android 2.2 and above
https://github.com/devthon/SilentJSAndroid
The main features are
Execute Javascript code without browser context
Execute Javascript code from a script file
Load other JS files in the same context
Execute a method in the background thread and return a result
Execute a Object.Method() call
Execute a prototype method call
Run long running script in the background after app is closed.
May not be a full fledged Web worker as such, since it doesn't have API to check the status in between. But that can be still added to the interface I believe.
If this is the direction you are looking for, I can explain more on how it is done.
How much of the Worker spec do you need to implement, and how flexible does the implementation need to be? You could probably get basic functionality up and running using a JavaScript interface[1] and spawning threads natively from Java. However this will get complex quite quickly.
Perhaps if you can describe what you are using workers for I might be able to offer a different/better suggestion.
[1] http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)
--
Adding some pseudo code
In JavaScript spawn a Java worker thread:
var worker_id = window.Android.spawnWorker();
In JavaScript, run a task on that worker:
var task_id = window.Android.doAdditionOnWorker(2,2, worker_id);
Handle the result in JavaScript
function onReceiveResultForWorkerTask(task_id, result) {
alert("the answer was " + result);
}
Java side:
public int spawnWorker() {
HandlerThread worker = new HandlerThread();
worker.start();
Handler h = new Handler(worker.getLooper()) {
#Override
handleMessage(Message msg) {
switch(msg.what)
case ADD:
// calculate the answer and send back to JS via UI thread
// Unpack parameters and task id from Message
mWebView.post(new Runnable(
public void run() {
mWebView.loadUrl("javascript:onReceiveResultForWorkerTask(task_id, " + (a+b) +");");
}
)
}
};
mWorkerMap.put(mWorkerId++, h);
return mWorkerId;
}
public int doAdditionOnWorker(int a, int b, int worker_id) {
Handler h = mWorkerMap.get(worker_id);
Bundle b = new Bundle();
int task_id = mTaskId++;
// pack arguments and task_id into the bundle
h.postMessage(Message.obtain(h, ADD, b);
return task_id;
}
Don't forget to go through and tear down all the worker threads that you spawn when the app doesn't need them anymore. Depending on how many workers you need you might also prefer to use a thread pool rather than creating new threads every time.
Related
I know Android UI is not really meant for executing functions and waiting for them to finish, however, I think there are use cases were it is required, like networking.
My problem is, I want to run a series of network operations that rely on each other and take a bit more time than the split second it takes to the next execution, so some waiting is in order:
Start hotspot
Get network interfaces and IP
Start socket
Initially I tested that all is working using buttons, then it waited between my button presses. But now I'd like to automatize it. I googled but all I found are solutions with Async task, which is deprecated. I tried with threads and join, but that usually causes weird crashes in the runnable, and it is not very elegant. I wonder if there is another solution?
The best thing you can do with SDK it's use Executors to run your work in background sequentially
val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
newSingleThreadExecutor.execute {
// 1...
}
newSingleThreadExecutor.execute {
// 2...
}
But if you want to touch the UI from background should create handler check if view's not null
val handler = Handler(Looper.myLooper()!!)
newSingleThreadExecutor.execute {
handler.post {
view?.visibility = View.GONE
}
}
How about something like this?
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startHotspot();
getNetworkInterfaceAndIP();
startSocket();
}
}, 300);
I'm making image processor app. I need to scan the phone for pictures and list them with their number of pixels. So that's gonna be a a large impact on performance and as I understood, I need to make it work on background thread.
So my question is, what is the best approach for this? I understand that IntentService may be the best solution, but I'm not sure how I will implement progress bar with it, and I need to return Picture objects and later update the UI on shuffle button. I'm doing update with Glide library so that's gonna go smooth.
Reading about Asynctasks, I stumbled about comments how it's bad and leads to leaks in memory and should avoid using it. rXJava is too complicated at the moment.
This is my code:
Main activity:
#OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
#OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
IntentService
IntentService is definitely a valid approach. You can use Broadcasts to return your result to another component of the app, be it Activity or another Service, for example:
Start the IntentService - if you need some parameters, place them in the Extras of the service intent.
Your IntentService runs on the background thread until the computation is finished.
Upon finishing, send a broadcast with computation result placed in intent extras.
In your activity, register a BroadcastReceiver that will listen for your computation result broadcast.
Upon getting the broadcast in your Activity, retrieve the computation result from intent extras.
You might also implement broadcasts received by your Service for things like cancellation of the computation or updating the parameters.
One of the advantages of IntentService is that you can easily integrate it with the JobScheduler API to defer execution until certain system conditions are met.
Alternatives
You can use a bus library, such as https://github.com/greenrobot/EventBus to communicate between Activity and Service - the only problem is, EventBus won't work with remote services (running in a separate process).
Like you've mentioned, using RxJava with IO and computation schedulers is also a good idea.
AsyncTask is fine as long as you not tie it with a hard reference to an activity - don't implement it as an inner class of Activity and if you want to communicate the result back, do it through a WeakReference<T>
AsyncTask is fine, you just need to be careful with its implementation.
However, for longer running tasks there are better options. IntentService is a good option.
When it comes to a responsive UI when using an IntentService you could add two things.
Notifications
Create an ongoing notification that indicates that your App is working on something. This lets users know that their CPU cycles are being eaten by something in the background and they are less likely(?) to be confused and cranky about their device running slower.
Additionally, it gives your App more of an allowance for staying alive when Android is looking for background Apps to kill to release memory.
EventBus
You can make UI reporting extremely simple by using an EventBus library. I am personally a fan of greenbot/EventBus, but there are others.
Example
In your Activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
In your IntentService:
EventBus.getDefault().post(new ProgressEvent(5000));
I'm kinda new to android development, but i'm trying to make a xml parser. I've been using android.sax library, following some tutos, and this works great.
One thing that is making me sceptical, is that I don't actually launch the parser in a separate thread, I just use a handlers to communicate between my parser and my main activity.
Should I parse in a Thread ?
Here's my main activity code :
tv = (TextView)findViewById(R.id.tv);
try {
URL url = new URL("https://www.googleapis.com/shopping/search/v1/public/products?key=AAAAABBBBBCCCCDDDEEEEFFF&country=FR&restrictBy=gtin=5030999075254&alt=atom");
XMLHandler xHandler = new XMLHandler();
Feed feed = xHandler.parse(url.openStream());
if(feed.getTotalResults()==0 || feed.getItems() == null || feed.getItems().isEmpty()) {
Log.w("XMLPARSENull", "No items
}
else {
tv.setText(feed.getTotalResults()+" " + feed.getItemsPerPage() + " " + feed.getStartIndex() + " " + feed.getTitle());
Iterator<Product> it = feed.getItems().iterator();
while(it.hasNext()) {
Log.w("XMLPARSEFeed",it.next().getName());
}
}
} catch(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("XMLPARSEException", "ERROR",e);
}
I don't think that it's the parsing so much as the network operation, as any network request should not be performed in the UI thread. You can either spawn a new Thread or an AsyncTask to perform your network request. Here are some links to help:
AsyncTask
Painless threading
Expensive operations
Designing for responsiveness
Thread documentation
Handler documentation
A simple answer:
If you have connections in your main ( UI ) thread, the user interface will become frozen whilst the thread is used to download the information and parse it. It takes some time for a connection so it may appear to freeze.
Apart from that, android 4.0 now does not actually allow you to call HTTP requests from this thread, it will throw an error which will be seen in logcat before crashing.
It is best practise to use an Async Task here, here is some more information on that.
Information on Async Tasks!
Yes, it is imperative that you move your parsing into a background thread, particularly if your XML is being streamed across the network; otherwise your app is likely to be killed off by the system for blocking the main thread.
You don't have to use a raw Thread, though; Android provides things like ASyncTask to make it a bit easier.
Everything which isn't related to direct interaction with GUI should be moved to separate thread, prefferably AsyncTask if you need interaction with GUI from that thread since Android won't allow accessing GUI from regular Thread class.
http://developer.android.com/reference/android/os/AsyncTask.html
Right now I have a native function which sort of does this:
Object o = new Object();
while (!o.done()) { o.compute(); }
return o.result();
This computing can take a while and I would want the UI in Android to be update with some kind of progress bar. So what I need is three different native functions, one for each step above. The problem I'm having is how to save the "native object" inbetween calls. Any tips?
Thanks
Your Android NDK C code statics act like statics do in normal application programming. As long as the current process (app) is running, your data will be preserved. If you have a method that will do a lot of processing, call it from a Java thread to work in the background like this:
new Thread(new Runnable()
{
public void run()
{
<call native method here>
}
}).start();
I use the android.os.Handler class to perform tasks on the background. When unit testing these, I call Looper.loop() to make the test thread wait for the background task thread to do its thing. Later, I call Looper.myLooper().quit() (also in the test thread), to allow the test thread to quit the loop and resume the testing logic.
It's all fine and dandy until I want to write more than one test method.
The problem is that Looper doesn't seem to be designed to allow quitting and restarting on the same thread, so I am forced to do all of my testing inside a single test method.
I looked into the source code of Looper, and couldn't find a way around it.
Is there any other way to test my Hander/Looper code? Or maybe some more test friendly way to write my background task class?
The source code for Looper reveals that Looper.myLooper().quit() enqueues a null message in the Message queue, which tells Looper that it is done processing messages FOREVER. Essentially, the thread becomes a dead thread at that point, and there is no way to revive it that I know of. You may have seen error messages when attempting to post messages to the
Handler after quit() is called to the effect "attempting to send message to dead thread". That is what that means.
This can actually be tested easily if you aren't using AsyncTask by introducing a second looper thread (other than the main one created for you implicitly by Android). The basic strategy then is to block the main looper thread using a CountDownLatch while delegating all your callbacks to the second looper thread.
The caveat here is that your code under test must be able to support using a looper other than the default main one. I would argue that this should be the case regardless to support a more robust and flexible design, and it is also fortunately very easy. In general, all that must be done is to modify your code to accept an optional Looper parameter and use that to construct your Handler (as new Handler(myLooper)). For AsyncTask, this requirement makes it impossible to test it with this approach. A problem that I think should be remedied with AsyncTask itself.
Some sample code to get you started:
public void testThreadedDesign() {
final CountDownLatch latch = new CountDownLatch(1);
/* Just some class to store your result. */
final TestResult result = new TestResult();
HandlerThread testThread = new HandlerThread("testThreadedDesign thread");
testThread.start();
/* This begins a background task, say, doing some intensive I/O.
* The listener methods are called back when the job completes or
* fails. */
new ThingThatOperatesInTheBackground().doYourWorst(testThread.getLooper(),
new SomeListenerThatTotallyShouldExist() {
public void onComplete() {
result.success = true;
finished();
}
public void onFizzBarError() {
result.success = false;
finished();
}
private void finished() {
latch.countDown();
}
});
latch.await();
testThread.getLooper().quit();
assertTrue(result.success);
}
I've stumbled in the same issue as yours. I also wanted to make a test case for a class that use a Handler.
Same as what you did, I use the Looper.loop() to have the test thread starts handling the queued messages in the handler.
To stop it, I used the implementation of MessageQueue.IdleHandler to notify me when the looper is blocking to wait the next message to come. When it happen, I call the quit() method. But again, same as you I got a problem when I make more than one test case.
I wonder if you already solved this problem and perhaps care to share it with me (and possibly others) :)
PS: I also would like to know how you call your Looper.myLooper().quit().
Thanks!
Inspired by #Josh Guilfoyle's answer, I decided to try to use reflection to get access to what I needed in order to make my own non-blocking and non-quitting Looper.loop().
/**
* Using reflection, steal non-visible "message.next"
* #param message
* #return
* #throws Exception
*/
private Message _next(Message message) throws Exception {
Field f = Message.class.getDeclaredField("next");
f.setAccessible(true);
return (Message)f.get(message);
}
/**
* Get and remove next message in local thread-pool. Thread must be associated with a Looper.
* #return next Message, or 'null' if no messages available in queue.
* #throws Exception
*/
private Message _pullNextMessage() throws Exception {
final Field _messages = MessageQueue.class.getDeclaredField("mMessages");
final Method _next = MessageQueue.class.getDeclaredMethod("next");
_messages.setAccessible(true);
_next.setAccessible(true);
final Message root = (Message)_messages.get(Looper.myQueue());
final boolean wouldBlock = (_next(root) == null);
if(wouldBlock)
return null;
else
return (Message)_next.invoke(Looper.myQueue());
}
/**
* Process all pending Messages (Handler.post (...)).
*
* A very simplified version of Looper.loop() except it won't
* block (returns if no messages available).
* #throws Exception
*/
private void _doMessageQueue() throws Exception {
Message msg;
while((msg = _pullNextMessage()) != null) {
msg.getTarget().dispatchMessage(msg);
}
}
Now in my tests (which need to run on the UI thread), I can now do:
#UiThreadTest
public void testCallbacks() throws Throwable {
adapter = new UpnpDeviceArrayAdapter(getInstrumentation().getContext(), upnpService);
assertEquals(0, adapter.getCount());
upnpService.getRegistry().addDevice(createRemoteDevice());
// the adapter posts a Runnable which adds the new device.
// it has to because it must be run on the UI thread. So we
// so we need to process this (and all other) handlers before
// checking up on the adapter again.
_doMessageQueue();
assertEquals(2, adapter.getCount());
// remove device, _doMessageQueue()
}
I'm not saying this is a good idea, but so far it's been working for me. Might be worth trying out! What I like about this is that Exceptions that are thrown inside some hander.post(...) will break the tests, which is not the case otherwise.