E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library “libwvm.so” not found

When I call MediaExtractor.setDataSource, I have error like this:

E/MPEG4Extractor: getNextAtomType – read error
E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library “libwvm.so” not found
W/System.err: java.io.IOException: Failed to instantiate extractor.
W/System.err: at android.media.MediaExtractor.setDataSource(Native Method)
W/System.err: at android.media.MediaExtractor.setDataSource(MediaExtractor.java:196)

Problem was that I opened file from asset and raw resource. If I open file from external storage directly, I don’t have error. For example:


//FAIL
AssetFileDescriptor afd = getResources().getAssets().openFd("dizzy.mp4");
mExtractor = new MediaExtractor();
mExtractor.setDataSource(afd.getFileDescriptor());

//FAIL
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.dizzy);
mExtractor = new MediaExtractor();
mExtractor.setDataSource(afd.getFileDescriptor());

//SUCCESS
String path = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), "dizzy.mp4").getAbsolutePath();
mExtractor = new MediaExtractor();
mExtractor.setDataSource(path);

Then I can’t use asset or raw resource? NO, you can if you do like this:

mExtractor.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library “libwvm.so” not found

request runtime permission(WRITE_EXTERNAL_STORAGE) in androidTest

I wanted to save files in sdcard in androidTest environment, but I had to see EACCESS. Then how can I get WRITE_EXTERNAL_STORAGE permission?

  1. launch activity and request permission

I tried to launch empty activity for requesting runtime permission, but

  • When I make an activity in androidTest and launch it with @Rule, I CAN’T. Activity should not be in test APK.
  • When I made an activity in application and launch it with @Rule, I CAN’T. I can not see permission dialog even though the activity is created with onCreate().

2. adb command

adb has a command that control permissions, adb grand/revoke. So I can turn on permisson in Gradle like https://stackoverflow.com/a/38351580, or in code like  https://stackoverflow.com/a/42570592. But I CAN’T even though I tried in command line directly.

3. Solution

From https://groups.google.com/forum/#!topic/android-testing-support-library/Om0IcAxAEJI, I can use getTargetContext.getExternalFilesDir(). getExternalFilesDir() DOES NOT NEED PERMISSION! Developer document says, “There is no security enforced with these files. For example, any application holding WRITE_EXTERNAL_STORAGE can write to these files”.

request runtime permission(WRITE_EXTERNAL_STORAGE) in androidTest

REALM: java.lang.IllegalStateException: Async query cannot be created on current thread. Realm cannot be automatically updated on a thread without a looper.

There is very simple example:

realm.executeTransaction(realm1 -> {
    Foo foo = realm1.createObject(Foo.class);
    foo.value = 100;
});
RealmResults<Foo> realmResults = realm.where(Foo.class).findAllAsync();

I stored a value to DB and read it asynchronously. Even though it seems no problems, if I run it on android instrumentation test I can see this error message:

java.lang.IllegalStateException: Async query cannot be created on current thread. Realm cannot be automatically updated on a thread without a looper.

Actually this situation is written on the document Realm (https://realm.io/docs/java/latest/#queries). I need Looper to use async query and unfortunately main thread in android instrumentation test does not have Looper. I found out a solution like below, which is not so clean:

HandlerThread thread = new HandlerThread("test");
thread.start();

Handler handler = new Handler(thread.getLooper());
CountDownLatch latch = new CountDownLatch(1);
handler.post(() -> {
    Realm realm = Realm.getInstance(realmConfiguration);
    realm.executeTransaction(realm1 -> {
        Foo foo = realm1.createObject(Foo.class);
        foo.value = 100;
    });
    RealmResults<Foo> realmResults = realm.where(Foo.class).findAllAsync();
    //assertThat(realmResults)...;
    latch.countDown();
});
latch.await();

Additionally, It is worth to see RunInLooperThread (https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/rule/RunInLooperThread.java). It seems same functionality with above code, and easy to use as a rule (https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java).

REALM: java.lang.IllegalStateException: Async query cannot be created on current thread. Realm cannot be automatically updated on a thread without a looper.

Realm: javasisst.CannotCompileException: [source error]

realm을 사용해 보려고 디펜던시 추가하고 모델 추가해서 DB에 넣는 간단한 테스트를 돌리는데, 난데없이 이런 에러가 발생하면서 빌드가 안 된다.

javasisst.CannotCompileException: realmSet$AAA(TYPE) not found in BBB

관련 링크는 아래와 같다.

https://github.com/realm/realm-java/issues/2936

https://github.com/realm/realm-java/pull/3030

요약하면 RealmObject를 상속받은 모델의 필드에 직접 접근하면 문제가 생긴다. 구글의 빌드툴 버그이며, 수정을 요청해 놓은 상태이다. 지금은 getter/setter를 사용하면 해결된다.

Realm: javasisst.CannotCompileException: [source error]

Lightweight-Stream-API added Comparator backport

LSA has not provided Comparator backport so I could not use reversed(), thenComparing(), etc. As of 1.1.6, it started to provide ComparatorCompat.

v1.1.6

aNNiMON released this 21 days ago · 15 commits to master since this release

  • Added Stream.withoutNulls()Stream.nullsOnly() and Predicate.Util.notNull(). Thanks to @IlyaGulya
  • Added Java 8 Comparator backport. Thanks to @BattleShipPark

Even though I don’t like the name ComparatorCompat, Enjoy it!

Lightweight-Stream-API added Comparator backport

꼬리 재귀 호출 최적화

꼬리 재귀 호출 최적화에 대해 이 링크(http://www.drdobbs.com/jvm/tail-call-optimization-and-java/240167044)를 읽고 도움을 받았다. 최적화가 된다는 사실은 알고 있었는데 어떻게 처리되는지에 대해서는 이번에 알게 되었다.

링크를 보면 아래와 같은 꼬리 재귀 호출 코드가

int func_a(int data) {
    data = do_this(data);
    return do_that(data);
}

아래와 같은 어셈블리어를 생성한다는 것을 알 수 있다

...         ! executing inside func_a()
push EIP    ! push current instruction pointer on stack
push data   ! push variable 'data' on the stack
jmp do_this ! call do_this() by jumping to its address
...         ! executing inside do_this()
push EIP    ! push current instruction pointer on stack
push data   ! push variable 'data' on the stack
jmp do_that ! call do_that() by jumping to its address
...         ! executing inside do_that()
pop data    ! prepare to return value of 'data'
pop EIP     ! return to do_this()
pop data    ! prepare to return value of 'data'
pop EIP     ! return to func_a()
pop data    ! prepare to return value of 'data'
pop EIP     ! return to func_a() caller
...

여기서 최적화를 하게 되면 아래처럼 몇 가지 명령어가 제거된다

...         ! executing inside func_a()
push EIP    ! push current instruction pointer on stack
push data   ! push variable ‘data’ on the stack
jmp do_this ! call do_this() by jumping to its address
...         ! executing inside do_this()
push EIP    ! push current instruction pointer on stack
push data   ! push variable ‘data’ on the stack
jmp do_that ! call do_that() by jumping to its address
...         ! executing inside do_that()
pop data    ! prepare to return value of ‘data’
pop EIP     ! return to do_this()
pop data    ! prepare to return value of ‘data’
pop EIP     ! return to func_a() caller
pop data    ! prepare to return value of ‘data’
pop EIP     ! return to func_a() caller
...

함수 호출로 처리하기 위해 IP와 인자를 스택에 넣고 빼는 작업이 필요한데, 이게 빠지면서 jmp do_that만 실행하는 것이다. 즉 스택 프레임을 계속 만들지 않고 현재 스택 프레임을 그대로 사용하게 된다.

그럼 우리에게 익숙한 팩토리얼 예제로 확인해 보면,

int Factorial(int n)
{
    if (n == 1) return 1;
    return n * Factorial(n-1);
}

Factorial()을 다시 호출하지 않고 jmp만 해버리면 루프와 다를게 없어진다. 즉 아래와 같이 처리되는 것이다.

int FactorialTail(int n)
{
    int acc = 1;
    do
    {
        if (n == 1) return;
        acc = acc * n;
        n = n - 1;
    } while (true);
    return acc;
}

그런데 충격적인 것은, 자바는 이런 꼬리 호출 최적화를 지원하지 않는다는 것이다!!

꼬리 재귀 호출 최적화