I'm trying to use notify() and wait(). Here is my desired class.
I have a problem, when I try to call addNewItem(). If I call tryToReadItem() first and then call addNewItem() method, that log will not be printed. Note that my DemoClass is the singleton.
public class DemoClass {
private static final String TAG = "DemoClass";
private static DemoClass instance;
private Object lock = new Object();
private static Thread executor;
private static Runnable reader;
static MyQueue queue;
private DemoClass() {
queue = MyQueue.getInstance();
reader = new Runnable() {
#Override
public void run() {
tryToReadRequest();
}
};
}
public static DemoClass getInstance() {
if (null == instance) {
instance = new RequestExecutor();
executor = new Thread(reader);
executor.run();
}
return instance;
}
public boolean addNewItem() {
synchronized (lock) {
lock.notify(); // executor will be run
Log.i(TAG, "executor run...");
}
return true;
}
public void tryToReadItem() {
try {
while (true) {
synchronized (lock) {
if (queue.checkTopValue() == null) {
Log.v(TAG, "queue is empty");
lock.wait();
} else {
//TODO other code...
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here is the usage of that class:
DemoClass executor = DemoClass.getInstance();
boolean bool = executor.addNewItem();
Am I missing something?
Edit: I just changed my code. Now tryToReadRequest() is executed continuously while queue is not empty. but my problem is that the line lock.notify(); does not execute.
There are many problems with this code
First of all
if (queue.checkTopValue() == null) {
Log.v(TAG, "queue is empty");
lock.wait();
}
Depends on official documentation
Note: Always invoke wait inside a loop that tests for the condition
being waited for. Don't assume that the interrupt was for the
particular condition you were waiting for, or that the condition is
still true.
Your DemoClass is Singleton. But not thread safe Singleton
because multiple threads can pass null == instance condition at the same time
if (null == instance) {
instance = new RequestExecutor();
executor = new Thread(reader);
executor.run();
}
Right way is additional check in synchronized block and using volatile instance.
so add volatile to instance
private static volatile DemoClass instance;
and rewrite getInstance() method to something like this
public static DemoClass getInstance() {
DemoClass localInstance = instance;
if (localInstance == null) {
synchronized (DemoClass.class) {
localInstance = instance;
if (localInstance == null) {
localInstance = new DemoClass();
instance = localInstance;
executor = new Thread(reader);
executor.run();
}
}
}
return localInstance;
}
note, you can leave only check inside synchronized block, but that will make getInstance method too slow.
Related
The test function test_init_1() succeed, but test_init_2() fails
That is because MyService has been already initialized.
#RunWith(RobolectricTestRunner::class)
class TrackingServiceInitTest {
#Test
fun test_init_1() {
val result = MyService.init(context, id1)
assertTrue(result) // result = `true`
}
#Test
fun test_init_2() {
val result = MyService.init(context, id2)
assertTrue(result) // AlreadyInitialized Exception has thrown!
}
#After
fun tearDown() {
// what should I do here to clear MyService's state?
}
}
MyService looks like:
public class MyService {
public static synchronized boolean init(Context context) {
if (sharedInstance != null) {
Log.d(TAG, "Already initialized!");
throw new AlreadyInitialized();
}
// initializing..
sharedInstance = new CoreService();
return true
}
}
How can I clear such status?
The right solution would be adding a static method to MyService marked with #VisibleForTesting which releases sharedInstance:
public class MyService {
public static synchronized boolean init(Context context) {
if (sharedInstance != null) {
Log.d(TAG, "Already initialized!");
throw new AlreadyInitialized();
}
// initializing..
sharedInstance = new CoreService();
return true;
}
#VisibleForTesting
public static void destroy() {
sharedInstance = null;
}
}
And then in your tearDown you can call MyService.destroy().
I've built an headless webview in an Android application for scrape an URL from a webpage. Every time I retrieve an URL I may need to redoing the scraping in the webpage with this URL. I'm using RxJava to handle these operations concurrently, and I'm using the flatMap function to make a recursive call.
The problem is that I need to dispose the WebView in the mainThread, so I tried to add .unsubscribeOn(AndroidSchedulers.mainThread()) but it seems it doesn't work, and the dispose() method in HeadlessRequest is called in the last thread I called observeOn(Schedulers.computation()). What should I change to execute the dispose() method in the mainThread?
This is my code:
HeadlessRequest
public class HeadlessRequest implements Disposable {
...
private class HeadlessWebView extends WebView {
...
private void destroyWebView() {
this.removeAllViews();
this.clearCache(false);
this.loadUrl("about:blank");
this.onPause();
this.removeAllViews();
this.destroy();
this.isDisposed = true;
}
}
#Override
public void dispose() {
// This doesn't print the mainThread id
Log.d(TAG, "Disposing on thread " + Thread.currentThread().getId());
this.webView.destroyWebView();
this.webView = null;
}
#Override
public boolean isDisposed() {
return (this.webView == null || this.webView.isDisposed);
}
}
NetworkUtils
public static Single<Document> downloadPageHeadless(final String url, final int delay, final Context context) {
return Single.create((SingleEmitter<Document> emitter) -> {
try {
emitter.setDisposable(new HeadlessRequest(url, USER_AGENT, delay, context, emitter::onSuccess, emitter::onError));
} catch (Exception e) {
emitter.onError(e);
}
}).unsubscribeOn(AndroidSchedulers.mainThread()) // It MUST be executed on the mainThread
.subscribeOn(AndroidSchedulers.mainThread());
}
ServerService
private static Single<String> resolveRecursive(String url, Context context) {
Server server = getServerInstance(url, context);
if (server == null) {
return Single.error(new UnsupportedOperationException("Server for " + url + " not supported"));
} else if (server.isVideo()) {
return server.resolve(url, context); // This method return a Single with observeOn(Schedulers.computation())
} else {
return server.resolve(url, context)
.observeOn(Schedulers.computation())
.flatMap(resolvedUrl -> resolveRecursive(resolvedUrl, context));
}
}
public static Single<String> resolveURL(String url, Context context) {
return resolveRecursive(url, context)
.observeOn(AndroidSchedulers.mainThread());
}
At the end I found another method to dispose the webview in the mainThread without RxJava. I've used the post method of the WebView.
private void destroyWebView() {
this.post(() -> {
this.removeAllViews();
this.clearCache(false);
this.loadUrl("about:blank");
this.onPause();
this.removeAllViews();
this.destroy();
this.isDisposed = true;
});
}
I get an error every time I launch my application.
E/SQLiteLog: (283) recovered 22 frames from WAL file /data/data/com.dmitrysimakov.kilogram/databases/androidx.work.workdb-wal
The application is working fine, but I want to know why this error occurs. databases/androidx.work.workdb-wal it is a Worker's journal. I use Worker to prepopulate my database.
Room.databaseBuilder(app, KilogramDb::class.java, "kilogram.db")
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance().enqueue(request)
}
})
.fallbackToDestructiveMigration()
.build()
This message indicates that the database hasn't been closed prior to exiting and thus that the WAL file wasn't cleaned up properly.
So when the App starts it's realising that it needs to do the cleanup of the WAL file and then does so, but issues the Error as it could indicate something serious.
To resolve the issue you need to close the database when done with it.
You may find this of interest (Richard Hipp being the main person responsible for SQLite, if you didn't already know) Continuous recovery of journal
I am using a bound service to connect to room database, so I use this code in my RoomService.onDestroy() method:
#Override
public final void
onDestroy()
{
super
.onDestroy();
if(roomDatabase != null)
{
if(roomDatabase
.isOpen())
{
roomDatabase
.close();
}
}
}
If you create your RoomDatabase instance in your Application singleton or in your Activity, you may do the same thing there (in corresponding onDestroy() method).
For convenience here is the code I use in my MainActivity class to close database in bound service:
#Override
protected final void
onDestroy()
{
super.onDestroy();
if(isFinishing())
{
if(mainViewModel != null)
{
mainViewModel
.onDestroy();
}
}
}
In MainViewModel.onDestroy() I send a message to bound service to close roomDatabase and then I unbind roomService:
public final void
onDestroy()
{
if(contextWeakReference != null)
{
final Context
context =
contextWeakReference
.get();
if(context != null)
{
if(roomServiceConnection != null)
{
if(boundToRoomService)
{
sendDBCloseMessageToRoomService();
context
.unbindService
(roomServiceConnection);
}
}
}
}
}
private void
sendDBCloseMessageToRoomService()
{
try
{
final Message message =
Message.obtain
(null, MSG_DB_CLOSE);
if(message != null)
{
if(messengerToRoomService != null)
{
messengerToRoomService
.send(message);
}
}
}
catch(final RemoteException e)
{
e.printStackTrace();
}
}
In RoomService I catch the message to close roomDatabase:
public class RoomService
extends Service
{
#NonNull #NonNls public static final
String DATABASE_NAME = "room_database";
public static final int MSG_DB_CLOSE = 108;
#Nullable public RoomDatabase roomDatabase;
private final IBinder roomBinder = new Binder();
private WeakReference<Context> contextWeakReference;
#Nullable public Messenger messengerFromRoomService;
#Nullable public Messenger messengerToRoomService;
private static class RoomServiceHandler
extends Handler
{
#Nullable private final
WeakReference<RoomService> roomServiceWeakReference;
RoomServiceHandler
(#Nullable final
RoomService service)
{
if(service != null)
{
roomServiceWeakReference =
new WeakReference<RoomService>
(service);
}
else
{
roomServiceWeakReference = null;
}
}
#Override
public final void
handleMessage
(#Nullable final
Message message)
{
if(message != null)
{
final int what =
message.what;
switch(what)
{
case MSG_DB_CLOSE:
{
handleDBCloseMessage
(message);
break;
}
}
}
}
private void
handleDBCloseMessage
(#Nullable final
Message message)
{
if(message != null)
{
final RoomService
service =
roomServiceWeakReference
.get();
if(service != null)
{
if(service
.roomDatabase != null)
{
if(service
.roomDatabase
.isOpen())
{
service
.roomDatabase
.close();
}
}
}
}
}
}
#Override
public final void
onCreate()
{
super.onCreate();
// initialize application context weak reference
final Context
applicationContext =
getApplicationContext();
if(applicationContext != null)
{
contextWeakReference =
new WeakReference<Context>
(applicationContext);
// initialize database
roomDatabase =
Room
.databaseBuilder
(applicationContext,
MyRoomDatabase.class,
DATABASE_NAME)
.build();
if(roomDatabase != null)
{
// initialise your DAO here
yourDao =
roomDatabase
.yourDao();
}
}
final RoomServiceHandler
roomServiceHandler =
new RoomServiceHandler(this);
if(roomServiceHandler != null)
{
messengerToRoomService =
new Messenger(roomServiceHandler);
}
}
#Nullable
#Override
public final IBinder
onBind
(#Nullable final
Intent intent)
{
IBinder result = null;
if(messengerToRoomService != null)
{
final IBinder
roomBinder =
messengerToRoomService
.getBinder();
if(roomBinder != null)
{
result = roomBinder;
}
}
return result;
}
}
I want to write and read from file in the same time without errors.
For example, I will starting new Thread for writing to file from my running service.
In my activity i will starting new Thread for reading from the same file.
I wan't to do this synchronously. Some thing like this :
To wait execution of next thread until previous finished.
Next thread must not start until previous thread stops, irrespective of time consumption.
My code for read and write:
public static final String ROUTE_FILE_NAME = "route.txt";
public static void savePointToFile(Context context, String point) throws IOException {
FileOutputStream fOut = context.openFileOutput(ROUTE_FILE_NAME, Context.MODE_APPEND);
OutputStreamWriter osw = new OutputStreamWriter(fOut);
osw.write(point);
osw.flush();
osw.close();
}
public static String readRouteFromFile(Context context) {
StringBuffer fileContent = new StringBuffer(UIUtils.emptyString());
byte[] buffer = new byte[1024];
try {
FileInputStream fis = context.openFileInput(ROUTE_FILE_NAME);
int length;
while ((length = fis.read(buffer)) != -1) {
fileContent.append(new String(buffer));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fileContent.toString();
}
Thanks in advance.
If you just want the read method called from a thread to wait for the write method called from another thread to be finished, and vice versa, just synchronize both methods on a common object:
private static final Object fileLock = new Object();
public static String readFile() {
synchronize(fileLock) {
[your current read code here]
}
}
public static void write(String data) {
synchronize(fileLock) {
[your current write code here]
}
}
You can look at a special thread pool executor service.
final ExecutorService threadpool = Executors.newSingleThreadExecutor();
Its fairly easy, just create runnables and put it in the threadpool. It contains a single thread so all your runnables are queued sequentially. Otherwise you could create a normal executorservice and set the threadpool to 1. Effectively its the same. Hope this helps
http://www.concretepage.com/java/newsinglethreadexecutor_java
So its like
WorkerThread.get(context).read()
WorkerThread.get(context).write()
You can even implement future calls instead of defining an explicit callback.
Just a general idea of how it can work. You need to save filepointers so you know where to pause and continue read/write. Other you will always start from the first data position in the file.
class WorkerThread {
interface Callback {
void onCompleteRead(String buffer, int pauseReadPointer);
void onCompleteWrite(int pauseWritePointer);
}
enum Action {
READ,
WRITE
}
private static WorkerThread singleton;
public static synchronized WorkerThread get(final Context context) {
if (instance == null) {
instance = new WorkerThread(context);
}
return instance;
}
private final Context context;
private final ExecutorService threadPool;
private WorkerThread(context) {
threadPool = Executors.newSingleThreadExecutor()
}
// PUBLIC READ CALL
public void read(int resumeReadPointer, Callback callback, "other params") {
queueJob(READ, null, resumeReadPointer, callback);
}
// PUBLIC WRITE CALL
public void write(String in, int resumeWritePointer, Callback callback, "other params") {
queueJob(WRITE, in, resumeWritePointer, callback);
}
private void queueJob(final Action action, String buffer, final int pointer, final Callback callback) {
/* Create handler in UI thread. */
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
ResultPack pack = (ResultPack) msg.obj;
if (Action.READ == action) {
callback.onCompleteRead(pack.result, pack.pointer);
} else {
callback.onCompleteWrite(pack.pointer);
}
}
};
// single threadpool. everything is FIFO
threadPool.submit(new FileRunnable(action, buffer, handler, pointer));
}
private class ResultPack {
private final String result;
private final int pointer;
private ResultPack(String s, int p) {
this.result = s;
this.pointer = p;
}
}
private class FileRunnable implements Runnable {
private int pointer = 0;
private final Handler handler;
private final buffer = buffer;
FileRunnable(final Action action, String buffer, final Handler handler, final int pointer) {
this.pointer = pointer;
this.handler = handler;
this.buffer = buffer;
}
#Override
public void run() {
if (Action.READ == action) {
ResultPack pack = readRouteFromFile(..., pointer);
} else { // write
ResultPack pack = savePointToFile(..., buffer, pointer);
}
Message message = Message.obtain();
message.obj = pack;
handler.sendMessage(message);
}
}
}
My goal is to have an AsyncTask that
can execute multiple times (one task at a time of course)
its current task can be cancelled
can be used by any activity
can execute many different tasks
does not have any problem with screen rotation (or phonecalls etc)
To achieve that i have created the classes shown below. But my experience with (and understanding of) threads is very limited. And since i don't know of any way to debug multiple threads, there is no way (for me) of knowing if this is going to work or not. So what i'm really asking is: Is this code ok?
And since there is no code that it is currently using this, here's an example use for it:
Data2Get d2g = new Data2Get(this, Data2Get.OpCountNumbers);
d2g.setParam("up2Num", String.valueOf(800));
LongOpsRunner.getLongOpsRunner().runOp(d2g);
So, here we go. This is the interface that every activity that wants to execute a long task (operation - op) should implement:
public interface LongOpsActivity {
public void onTaskCompleted(OpResult result);
}
This is a class to enclose any result of any task:
public class OpResult {
public LongOpsActivity forActivity;
public int opType;
public Object result;
public OpResult(LongOpsActivity forActivity, int opType, Object result){
this.forActivity = forActivity;
this.opType = opType;
this.result = result;
}
}
And finally the big part, the singleton async task class:
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import android.os.AsyncTask;
public class LongOpsRunner extends AsyncTask<Void, OpResult, Void> {
public class Data2Get implements Cloneable {
// one id for each operation
public static final int OpCountNumbers = 1;
public static final int OpCountLetters = 2;
public LongOpsActivity forActivity;
public int opType;
private HashMap<String, String> params = new HashMap<String, String>();
public Data2Get(LongOpsActivity forActivity, int opType) {
this.forActivity = forActivity;
this.opType = opType;
}
public void setParam(String key, String value) {
params.put(key, value);
}
public String getParam(String key) {
return params.get(key);
}
public void clearParams() {
params.clear();
}
#Override
protected Object clone() throws CloneNotSupportedException {
// deep clone
Data2Get myClone = (Data2Get) super.clone();
myClone.clearParams();
for (Entry<String, String> entry : params.entrySet()) {
myClone.setParam(new String(entry.getKey()), new String(entry.getValue()));
}
return myClone;
}
}
private class IntermediateResult extends OpResult {
public IntermediateResult(LongOpsActivity forActivity, int opType, Object result) {
super(forActivity, opType, result);
}
}
// not really needed
private class FinalResult extends OpResult {
public FinalResult(LongOpsActivity forActivity, int opType, Object result) {
super(forActivity, opType, result);
}
}
private final ReentrantLock lock = new ReentrantLock();
private final Condition executeOp = lock.newCondition();
private volatile boolean finished = false;
private volatile boolean waiting = true;
private volatile boolean shouldCancel = false;
private volatile boolean activityHasBeenNotified = true;
private Data2Get startingOpParams = null;
private Data2Get currentOpParams = null;
private FinalResult currentOpResult;
protected Void doInBackground(Void... nothing) {
try {
lock.lockInterruptibly();
do {
waiting = true;
while (waiting) {
executeOp.await();
}
shouldCancel = false;
activityHasBeenNotified = false;
boolean opCancelled = false;
try {
currentOpParams = (Data2Get) startingOpParams.clone();
} catch (CloneNotSupportedException cns) {
// do nothing
}
switch (currentOpParams.opType) {
case Data2Get.OpCountNumbers:
int numberCounter = 0;
int numLoopCount = 0;
while ((!opCancelled) & (numLoopCount <= 5000000)) {
if (!shouldCancel) {
numberCounter = (numberCounter + 1)
% Integer.parseInt(currentOpParams.getParam("up2Num"));
if (numberCounter == 0) {
numLoopCount++;
publishProgress(new IntermediateResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Numbers loop count:" + numLoopCount));
}
} else {
opCancelled = true;
activityHasBeenNotified = true;
}
if (!opCancelled) {
currentOpResult = new FinalResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Numbers loop completed.");
publishProgress(currentOpResult);
}
}
break;
case Data2Get.OpCountLetters:
int letterLoopCount = 0;
char ch = 'a';
while (!opCancelled & (letterLoopCount <= 5000000)) {
if (!shouldCancel) {
ch++;
if (Character.toString(ch).equals(currentOpParams.getParam("up2Letter"))) {
ch = 'a';
letterLoopCount++;
publishProgress(new IntermediateResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Letters loop count:" + letterLoopCount));
}
} else {
opCancelled = true;
activityHasBeenNotified = true;
}
if (!opCancelled) {
currentOpResult = new FinalResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Letters loop completed.");
publishProgress(currentOpResult);
}
}
break;
default:
}
} while (!finished);
lock.unlock();
} catch (InterruptedException e) {
// do nothing
}
return null;
}
public void cancelCurrentOp() {
shouldCancel = true;
}
#Override
protected void onProgressUpdate(OpResult... res) {
OpResult result = res[0];
if (result instanceof IntermediateResult) {
// normal progress update
// use result.forActivity to show something in the activity
} else {
notifyActivityOpCompleted(result);
}
}
public boolean currentOpIsFinished() {
return waiting;
}
public void runOp(Data2Get d2g) {
// Call this to run an operation
// Should check first currentOpIsFinished() most of the times
startingOpParams = d2g;
waiting = false;
executeOp.signal();
}
public void terminateAsyncTask() {
// The task will only finish when we call this method
finished = true;
lock.unlock(); // won't this throw an exception?
}
protected void onCancelled() {
// Make sure we clean up if the task is killed
terminateAsyncTask();
}
// if phone is rotated, use setActivity(null) inside
// onRetainNonConfigurationInstance()
// and setActivity(this) inside the constructor
// and all that only if there is an operation still running
public void setActivity(LongOpsActivity activity) {
currentOpParams.forActivity = activity;
if (currentOpIsFinished() & (!activityHasBeenNotified)) {
notifyActivityOpCompleted(currentOpResult);
}
}
private void notifyActivityOpCompleted(OpResult result) {
if (currentOpParams.forActivity != null) {
currentOpParams.forActivity.onTaskCompleted(result);
activityHasBeenNotified = true;
}
}
private static LongOpsRunner ref;
private LongOpsRunner() {
this.execute();
}
public static synchronized LongOpsRunner getLongOpsRunner() {
if (ref == null)
ref = new LongOpsRunner();
return ref;
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
I hope someone helps with making this work, as it would be very useful not only for me, but many other people out there. Thank you.
Try Loaders. I switched from simple AsyncTasks to AsyncTaskLoaders and they solve lots of problems. If you implement a Loader as a standalone class, it would meet all of your requirements, especially when it comes to rotation which is the biggest issue with old AsyncTask.