I want to add a logging mechanism in my android in which i want to log messages to a file on the sd card.
Since I had to log from different threads and as I do not want these threads to be blocked, I tried to do logging on a different thread.
I've built a class something like this so far -
public class TestLogger {
private static String LOG_FILE_NAME = "/logs.txt";
private static int LOG_FILE_SIZE_LIMIT = 100 * 1024;
private static FileHandler logHandler;
private static Logger logger;
private static ExecutorService executorService;
public synchronized static void init() {
String logFilePathName = Environment.getExternalStorageDirectory() + LOG_FILE_NAME;
try {
logHandler = new FileHandler(logFilePathName, LOG_FILE_SIZE_LIMIT, 1, true);
logHandler.setFormatter(new SimpleFormatter());
logger = Logger.getLogger("com.test.android");
logger.setLevel(Level.ALL);
logger.addHandler(logHandler);
executorService = Executors.newFixedThreadPool(1);
} catch (IOException e) {
e.printStackTrace();
}
}
private synchronized static void writeLog(String msg) {
if(logger == null)
return;
logger.log(Level.FINE, msg);
}
public static void logMessage(String msg) {
executorService.submit(new LogToFile(msg));
}
private static class LogToFile implements Runnable {
String msg;
public LogToFile(String msg) {
this.msg = msg;
}
#Override
public void run() {
TestLogger.writeLog(msg);
}
}
}
When the app is entered, I call TestLogger.init() and from there on I call `TestLogger.logMessage("log message"); whenever I have to log something to the file.
This works fine but instead of creating logs.txt, i also see other files created. Like logs.txt.1 and logs.txt.2 along with their .lck files. By having keyword synchronized, have I not made sure that no two threads write to the same file at the same time?
What am I doing wrong?
I am now using a blocking queue to do the job. it works great now :)
synchronized works with variables not files. You can use the synchronized block to fill a string builder for e.g. and write to the file in a final step.
Related
So the thing is that i want to get Bitmap from absolute path, so i pass those paths as ArrayList<Strings> to my presenter, where i have next piece of code:
private void decodeImageUri(final ArrayList<String> imageUris) {
while(imageCounter < imageUris.size()) {
DecodeBitmapsThreadPool.post(new Runnable() {
#Override
public void run() {
Bitmap bitmap = BitmapFactory.decodeFile(imageUris.get(imageCounter));
mImagesBase64Array.add(bitmapToBase64(bitmap));
}
});
}
DecodeBitmapsThreadPool.finish();
Log.d("SIZE OF BASE64", " ---------- " + mImagesBase64Array.size());
}
And this is my ThreadPool class:
public class DecodeBitmapsThreadPool {
private static DecodeBitmapsThreadPool mInstance;
private ThreadPoolExecutor mThreadPoolExec;
private static int MAX_POOL_SIZE;
private static final int KEEP_ALIVE = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
public static synchronized void post(Runnable runnable) {
if (mInstance == null) {
mInstance = new DecodeBitmapsThreadPool();
}
mInstance.mThreadPoolExec.execute(runnable);
}
private DecodeBitmapsThreadPool() {
int coreNum = Runtime.getRuntime().availableProcessors();
MAX_POOL_SIZE = coreNum * 2;
mThreadPoolExec = new ThreadPoolExecutor(
coreNum,
MAX_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
workQueue);
}
public static void finish() {
mInstance.mThreadPoolExec.shutdown();
}
}
So when i start ThreadPool it looks like it get in some kind of a endless loop (according to Logcat) and then i just get OutOfMemoryException. I wonder what i`m i doing wrong, since i cant debug it. I simply want to decode the bitmap in background threads and create base64 representation of those Bitmaps, so i can upload them to the server. P.S. Any ideas how can this be implemented with RxJava2? Thanks in advance!
You are not incrementing imageCounter, so it actually is an endless loop.
An enhanced for loop is less error prone:
for (String uri : imageUris) {
...
Bitmap bitmap = BitmapFactory.decodeFile(uri);
...
imageCounter value not changed in while loop,so this condition (imageCounter < imageUris.size()) always true and while loop run infinite times
while(imageCounter < imageUris.size()) {
DecodeBitmapsThreadPool.post(new Runnable() {
#Override
public void run() {
Bitmap bitmap = BitmapFactory.decodeFile(imageUris.get(imageCounter));
mImagesBase64Array.add(bitmapToBase64(bitmap));
}
});
}
The issue is that you don't increment the while loop, so the while loop's condition will never be met. You should increment it at the end of code within the while loop like so:
while(imageCounter < imageUris.size()) {
DecodeBitmapsThreadPool.post(new Runnable() {
#Override
public void run() {
Bitmap bitmap = BitmapFactory.decodeFile(imageUris.get(imageCounter));
mImagesBase64Array.add(bitmapToBase64(bitmap));
}
});
imageCounter++;
}
Though as mentioned in bwt's answer, an enhanced for loop is generally the best approach for these types of tasks.
I am trying to implement a repository pattern that use realm and still keep the live objects feature while handling the creating and closing of the realm instances. This my current approach, sadly it does not work. I run all that code on my custom background thread.
public Observable<List> getAll(Class clazz) {
Realm realm = Realm.getDefaultInstance();
SerializedSubject relay = new SerializedSubject<>(PublishSubject.create());
try {
realm.where(clazz).findAllAsync().addChangeListener(new RealmChangeListener<RealmResults>() {
#Override
public void onChange(RealmResults element) {
relay.onNext(realm.copyFromRealm(element));
}
});
return relay.asObservable();
} finally {
relay.onCompleted();
try {
if (!realm.isClosed())
realm.close();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
Background Thread:
public class JobExecutor implements ThreadExecutor {
private static final int INITIAL_POOL_SIZE = Runtime.getRuntime().availableProcessors();
// Sets the amount of time an idle thread waits before terminating
private static final int KEEP_ALIVE_TIME = 10;
// Sets the Time Unit to seconds
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
#NonNull
private final ThreadPoolExecutor threadPoolExecutor;
public JobExecutor() {
threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, INITIAL_POOL_SIZE,
KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, new LinkedBlockingQueue<>(), new JobThreadFactory());
}
#Override
public void execute(#NonNull Runnable runnable) {
this.threadPoolExecutor.execute(runnable);
}
private static class JobThreadFactory implements ThreadFactory {
private static final String THREAD_NAME = "android_";
private int counter = 0;
#NonNull
#Override
public Thread newThread(#NonNull Runnable runnable) {
return new Thread(runnable, THREAD_NAME + counter++);
}
}
I use it like this:
private <T> Observable.Transformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.from(mThreadExecutor)) // my background thread
.observeOn(mPostExecutionThread.getScheduler()); // main thread
}
This exception is thrown:
java.lang.IllegalStateException: Your Realm is opened from a thread without a Looper. Async queries need a Handler to send results of your query
When i use .findAll() instead of .findAllAsync() this exception is thrown:
java.lang.IllegalStateException: You can't register a listener from a non-Looper thread or IntentService thread.
Thanks in advance and looking forward to your responses.
You need a Thread that has a Looper/Handler, as stated by the IllegalStateException. Right now, it looks like you're just using the normal Schedulers class, and not the AndroidSchedulers. Check out this repo for Android specific schedulers.
https://github.com/ReactiveX/RxAndroid/tree/1.x/rxandroid/src/main/java/rx/android/schedulers
So, my task is to push my device's Location info, when it changes, to the remote server Json API service. If remote server is unavailable, my DatabaseManager must save them to a local database.
Here is my Retrofit API:
public interface GpsService {
#POST("/v1/savelocationbatch")
SaveResponse saveLocationBatch(#Body LocationBatch locationBatch);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(myBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
GpsService service = retrofit.create(GpsService.class);
And a POJO class:
public class LocationBatch{
#SerializedName("LocationPointList")
ArrayList<LocationPoint> locationPointList;
#SerializedName("ClientId")
String clientId;
#SerializedName("ClientSecret")
String clientSecret;
//setter & getter
}
My LocationPoint model:
#Table(name="LocationPoints", id = "_id")
public class LocationPoint extends Model {
#SerializedName("Latitude")
#Column(name="latitude")
public Double latitude;
#SerializedName("Longitude")
#Column(name="longitude")
public Double longitude;
#SerializedName("Altitude")
#Column(name="altitude")
public Double altitude;
//... setters, getters etc
}
All of my last locations are stored in a CurrentLocationHolder singleton (for batch sending/saving to DB/emitting from Observable). It's setLocation() method updates currentLocation variable, then puts it to the locationBuffer, than checks the buffer's size, than if buffer's size exceeds my MAX_BUFFER_SIZE variable, it fires locationBufferChanged.onNext(with a copy of a locationBuffer as argument), then it clears locationBuffer...
public class CurrentLocationHolder {
private List<LocationPoint> locationBuffer =
Collections.synchronizedList(new ArrayList<>());
private LocationPoint currentLocation;
private final PublishSubject<List<LocationPoint>> locationBufferFull =
PublishSubject.create();
public Observable<List<LocationPoint>>
observeLocationBufferFull(boolean emitCurrentValue) {
return emitCurrentValue ?
locationBufferFull.startWith(locationBuffer) :
locationBufferFull;
}
public void setLocation(LocationPoint point) {
this.currentLocation = point;
locationBuffer.add(point);
if (locationBuffer.size() >= MAX_BUFFER_SIZE) {
locationBufferChanged.onNext(new ArrayList<>(this.locationBuffer));
}
locationBuffer.clear();
}
}
And here is my DatabaseManager:
public class DatabaseManager {
private Subscription locationBufferSubscription;
private static DatabaseManager instance;
public static void InitInstance() {
if (instance == null)
instance = new DatabaseManager();
}
}
public void saveToDb(ArrayList<LocationPoint> locArray){
ActiveAndroid.beginTransaction();
try {
for (int i = 0; i < locArray.size(); i++) {
locArray.get(i).save();
}
ActiveAndroid.setTransactionSuccessful();
}
finally {
ActiveAndroid.endTransaction();
}
}
}
My application's main goal:
To write all of the listened LocationPoints to the HTTP server through Retrofit. If a remote server will be suddenly down for some reason (or internet connection would lost), my app should seamlessly write new locationPoints to a local database. When the server (or internet) will be up, some mechanism should provide saved local data to Retrofit's call.
So, my questions are:
How to create an Rx-Observable object, which will emit List normally to a Retrofit service, but when server (or internet) goes down, it should provide unsaved LocationPoints to DatabaseManager.saveToDb() method?
How to catch internet connection or server "up" state? Is it a good idea to create some Observable, which will ping my remote server, and as result should emit some boolean value to it's subscribers? What is the best way to implement this behavior?
Is there a simple way to enqueue Retrofit calls with a locally saved data (from local DB), when internet connection (server) will become "up"?
How not to loose any of my LocationPoints on the server-side? (finally my client app must send all of them!
Am I doing something wrong? I am a newbie to Android, Java and
particularly to RxJava...
Interesting task! First of all: you don't need to create DB for storing such tiny info. Android has good place for storing any Serializable data.
So for saving locally data crate Model like:
public class MyLocation implements Serializable {
#Nonnull
private final String id;
private final Location location;
private final boolean isSynced;
// constructor...
// getters...
}
Singleton class:
public class UserPreferences {
private static final String LOCATIONS = "locations";
#Nonnull
private final SharedPreferences preferences;
#Nonnull
private final Gson gson;
private final PublishSubject<Object> locationRefresh = PublishSubject.create();
public void addLocation(MyLocation location) {
final String json = preferences.getString(LOCATIONS, null);
final Type type = new TypeToken<List<MyLocation>>() {
}.getType();
final List<MyLocation> list;
if (!Strings.isNullOrEmpty(json)) {
list = gson.fromJson(json, type);
} else {
list = new ArrayList<MyLocation>();
}
list.add(lication);
final String newJson = gson.toJson(set);
preferences.edit().putString(LOCATIONS, newJson).commit();
locationRefresh.onNext(null);
}
private List<String> getLocations() {
final String json = preferences.getString(LOCATIONS, null);
final Type type = new TypeToken<List<MyLocation>>() {
}.getType();
final List<MyLocation> list = new ArrayList<MyLocation>();
if (!Strings.isNullOrEmpty(json)) {
list.addAll(gson.<List<MyLocation>>fromJson(json, type));
}
return list;
}
#Nonnull
public Observable<List<MyLocation>> getLocationsObservable() {
return Observable
.defer(new Func0<Observable<List<MyLocation>>>() {
#Override
public Observable<List<MyLocation>> call() {
return Observable.just(getLocations())
.filter(Functions1.isNotNull());
}
})
.compose(MoreOperators.<List<MyLocation>>refresh(locationRefresh));
}
// also You need to create getLocationsObservable() and getLocations() methods but only for not synced Locations.
}
Change:
public interface GpsService {
#POST("/v1/savelocationbatch")
Observable<SaveResponse> saveLocationBatch(#Body LocationBatch locationBatch);
}
Now the most interesting...make it all works.
There is extention for RxJava. It has a lot of "cool tools" (btw, MoreOperators in UserPref class from there), also it has something for handling retrofit errors.
So let assume that location saving suppose to happens, when Observable saveLocationObservable emit something. In that case your code looks like:
final Observable<ResponseOrError<SaveResponse>> responseOrErrorObservable = saveLocationObservable
.flatMap(new Func1<MyLocation, Observable<ResponseOrError<SaveResponse>>>() {
#Override
public Observable<ResponseOrError<SaveResponse>> call(MyLocation myLocation) {
final LocationBatch locationBatch = LocationBatch.fromMyLocation(myLocation); // some method to convert local location to requesr one
return saveLocationBatch(locationBatch)
.observeOn(uiScheduler)
.subscribeOn(networkScheduler)
.compose(ResponseOrError.<SaveResponse>toResponseOrErrorObservable());
}
})
.replay(1)
.refCount();
final Observable<Throwable> error = responseOrErrorObservable
.compose(ResponseOrError.<SaveResponse>onlyError())
.withLatestFrom(saveLocationObservable, Functions2.<MyLocation>secondParam())
.subscribe(new Action1<MyLocation>() {
#Override
public void call(MyLocation myLocation) {
// save location to UserPref with flag isSynced=flase
}
});
final Observable<UserInfoResponse> success = responseOrErrorObservable
.compose(ResponseOrError.<SaveResponse>onlySuccess())
.subscribe(new Action1<SaveResponse>() {
#Override
public void call(SaveResponse response) {
// save location to UserPref with flag isSynced=true
}
});
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 Android app should do the following:
The MainActivity launches another thread at the beginning called UdpListener which can receive UDP calls from a remote server. If it receives a packet with a content "UPDATE", the UdpListener should notify the MainActivity to do something.
(In the real app, the use case looks like this that my app listens on the remote server. If there is any new data available on the remote server, it notifies every client (app) by UDP, so the client knows that it can download the new data by using HTTP).
I tried to simulate this in an JUnit test. The test contains an inner class which mocks the MainActivity as well as it sends the UDP call to the UdpListener:
public class UdpListener extends Thread implements Subject {
private DatagramSocket serverSocket;
private DatagramPacket receivedPacket;
private boolean running = false;
private String sentence = "";
private Observer observer;
private static final String TAG = "UdpListener";
public UdpListener(Observer o) throws SocketException {
serverSocket = new DatagramSocket(9800);
setRunning(true);
observer = o;
}
#Override
public void run() {
setName(TAG);
while (isRunning()) {
byte[] receivedData = new byte[1024];
receivedPacket = new DatagramPacket(receivedData, receivedData.length);
try {
serverSocket.receive(receivedPacket);
}
catch (IOException e) {
Log.w(TAG, e.getMessage());
}
try {
sentence = new String(receivedPacket.getData(), 0, receivedPacket.getLength(), "UTF-8");
if ("UPDATE".equals(sentence)) {
notifyObserver();
}
}
catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage());
}
}
}
private boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void notifyObserver() {
observer.update();
}
}
This is the corresponding test:
#RunWith(RobolectricTestRunner.class)
public class UdpListenerTest {
private MainActivityMock mainActivityMock = new MainActivityMock();
#Before
public void setUp() throws Exception {
mainActivityMock.setUpdate(false);
}
#After
public void tearDown() throws Exception {
mainActivityMock.setUpdate(false);
}
#Test
public void canNotifyObserver() throws IOException, InterruptedException {
UdpListener udpListener = new UdpListener(mainActivityMock);
udpListener.setRunning(true);
udpListener.start();
InetAddress ipAddress = InetAddress.getByName("localhost");
DatagramSocket datagramSocket = new DatagramSocket();
DatagramPacket sendPacket = new DatagramPacket("UPDATE".getBytes(), "UPDATE".length(), ipAddress, 9800);
datagramSocket.send(sendPacket);
datagramSocket.close();
assertTrue(mainActivityMock.isUpdate());
udpListener.setRunning(false);
}
private class MainActivityMock implements Observer {
private boolean update = false;
#Override
public void update() {
update = true;
}
public boolean isUpdate() {
return update;
}
public void setUpdate(boolean update) {
this.update = update;
}
}
}
The good thing is that my concept works. But, this test doesn't. That means it only does when I stop with a breakpoint at this line datagramSocket.close(); and wait for about a second. Why this happens is clear. But how can I do that automatically? I thought about using wait() but I have to invoke notify() from the other thread for that. The same problem with CountDownLatch. I'm not sure how to solve that without changing UdpListener.
You could write a simple loop with a specified timeout.
try {
long timeout = 500; // ms
long lastTime = System.currentTimeMillis();
while(timeout > 0 && !mainActivityMock.isUpdate()) {
Thread.sleep(timeout);
timeout -= System.currentTimeMillis() - lastTime;
lastTime = System.currentTimeMillis();
}
} catch(InterruptedException e) {
} finally {
assertTrue(mainActivityMock.isUpdate());
}
By the way - you should declare your running attribute to volatile.
one solution would be to use a blocking queue with size 1 for storing your received results.
The request for isUpdate (which would take an element from the blocking queue) would block until the update package(or any other package) is put into the queue.
is case you want all your calls to be non-blocking you could use a Future for receiving your result. Future.get() would block ontil your result is received.