顯示具有 java 標籤的文章。 顯示所有文章
顯示具有 java 標籤的文章。 顯示所有文章

2023年3月15日 星期三

Flutter 開發筆記 - 排除 Could not open settings generic class cache for settings file @ macOS 13.2.1, openjdk 19.0.2, Android Studio Electric Eel | 2022.1.1 Patch 2


最近工作上關係,來回顧一下 Flutter app 的開發,預計寫一款非常簡單的 App 試試水溫。結果在 macOS 13.2.1, openjdk 19.0.2, Android Studio Electric Eel | 2022.1.1 Patch 2 環境,預設弄個專案出來卻不能編譯:

Launching lib/main.dart on sdk gphone64 arm64 in debug mode...
Running Gradle task 'assembleDebug'...

FAILURE: Build failed with an exception.

* What went wrong:
Could not open settings generic class cache for settings file '/Users/UserID/AndroidStudio/usb_displayport_helper/android/settings.gradle' (/Users/UserID/.gradle/caches/7.5/scripts/XXXXXX).
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 63

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 24s
Exception: Gradle task assembleDebug failed with exit code 1

研究了一下,主因是 JDK 與 Gradle 版本問題,需要多做一些事就能排除了。記錄一下。

此時環境:

% java --version
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment (build 19.0.2+7-44)
OpenJDK 64-Bit Server VM (build 19.0.2+7-44, mixed mode, sharing)

這時,在既有專案的左邊上方的 Project 小視窗,挑選 android 目錄,點選右鍵 -> Flutter -> Open Android module in Android Studio -> New Window ,在新的 Android Studio 還沒按 Build 就看到訊息:

Unsupported Java. 
Your build is currently configured to use Java 19.0.2 and Gradle 7.5.

Possible solution:
 - Open Gradle wrapper settings, change `distributionUrl` property to use compatible Gradle version and reload the project

這時就來更換預設的環境: File -> Project Structure -> Project

- Android Gradle Plugin Version: 7.4.2
- Gradle Version: 7.6.1

按下 OK 就會開始抓資料,最後也順利編譯後在模擬器上跑了。

2016年3月6日 星期日

Android 開發筆記 - 在 Android NDK/JNI 環境中,操作 HashMap 和 Vector 方法與資料型態定義

過去在 C++ 把玩 std::vector<std::unordered_map<int, std::string>> ,而搬到 Java 環境中時,就會出現檔案形態的轉變,於是乎就先挑個 Vector<HashMap<String,String>> 來試試,順手筆記一下。

其中 JNI 在呼叫 Java 物件時,Java 八大型態及 Java Class 型態在 JNI 中採用的關鍵字:

Java boolean type => "Z"
Java byte type => "B"
Java char type => "C"
Java short type => "S"
Java int type => "I"
Java long type => "J"
Java float type => "F"
Java double type => "D"
Java object type => "Ljava/lang/Object;"
Java Array of int type => "[I"
Java Array of double type => "[D"
Java Array of java/lang/Object type => "[Ljava/lang/Object;"


舉例來說,以 Vector 的 public boolean add(E e) 函數為例:

jclass vectorClass = env->FindClass("java.util.Vector");
jmethodID vector_add = env->GetMethodID(vectorClass, "add", "(Ljava/lang/Object;)Z");


其中 "(Ljava/lang/Object;)Z" 代表此函式回傳為 boolean 型態,而需要傳遞一個 java/lang/Object 當作參數。

最後是比較冗長的操作:

Java code:

static {
System.loadLibrary("test")
}
public native boolean test(Vector<HashMap<String, String>> obj);

void usage() {

Vector<HashMap<String, String>> output = new Vector<HashMap<String, String>>();
boolean ret = test(output);
Log.i(tag, "Ret: "+ret+", output size: "+output.size());
}


build.gradle (Module:app):

android.ndk {
moduleName = "test"
ldLibs.addAll(["log"])
CFlags.add("-std=c++11")
stl = "c++_shared"
}


C/C++ code:

#include <jni.h>
#include <android/log.h>

#define LOG_TAG "JNI"
#define LOGV(...)__android_log_print( ANDROID_LOG_VERBOSE,LOG_TAG, __VA_ARGS__ )
#define LOGD(...)__android_log_print( ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__ )
#define LOGI(...)__android_log_print( ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__ )
#define LOGW(...)__android_log_print( ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__ )
#define LOGE(...)__android_log_print( ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__ )

extern "C" {


jboolean Java_YourPackageName_test(JNIEnv *env, jobject thiz, jobject vectorObject) {

jclass vectorClass = env->GetObjectClass(vectorObject);
jmethodID vector_size = env->GetMethodID(vectorClass, "size", "()I");
jmethodID vector_add = env->GetMethodID(vectorClass, "add", "(Ljava/lang/Object;)Z");
jmethodID vector_elementAt = env->GetMethodID(vectorClass, "elementAt", "(I)Ljava/lang/Object;");

jclass hashMapClass = env->FindClass("java/util/HashMap");
jmethodID hashMap_init = env->GetMethodID(hashMapClass, "<init>", "(I)V");
jmethodID hashMap_put = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jmethodID hashMap_get = env->GetMethodID(hashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
jmethodID hashMap_size = env->GetMethodID(hashMapClass, "size", "()I");

std::unordered_map<int, std::string> attrs;
attrs[0] = "Hello";
attrs[1] = "World";

if (vectorObject && vectorClass && hashMapClass) {
// init HashMap object
jobject dataHashMap = env->NewObject(hashMapClass, hashMap_init, 1);
for (auto it = attrs.begin(); it != attrs.end(); ++it) {
jstring key = env->NewStringUTF(std::to_string(it->first).c_str());
jstring value = env->NewStringUTF(it->second.c_str());
env->CallObjectMethod(dataHashMap, hashMap_put, key, value);
env->DeleteLocalRef(key);
env->DeleteLocalRef(value);
}
LOGI("hashmap size: %d", env->CallIntMethod(dataHashMap, hashMap_size));

LOGI("vector size: %d", env->CallIntMethod(vectorObject, vector_size));

// vector add
env->CallBooleanMethod(vectorObject, vector_add, dataHashMap);
env->DeleteLocalRef(dataHashMap);

// access vector
for (int i=0, cnt = env->CallIntMethod(vectorObject, vector_size) ; i<cnt; ++i) {
LOGI("get vector_elementAt: %d", i);
if (vector_elementAt) {
jobject dataInVector = env->CallObjectMethod(vectorObject, vector_elementAt, i);
jclass dataInVectorClass = env->GetObjectClass(dataInVector);
jmethodID dataInVector_get = env->GetMethodID(dataInVectorClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
if (dataInVector && dataInVector_get) {
jstring key = env->NewStringUTF(std::to_string(0).c_str());
jstring value = (jstring)env->CallObjectMethod(dataInVector, dataInVector_get, key);
LOGI("get vector_elementAt: %d, value: [%s]", i, value);
env->DeleteLocalRef(key);
}
}
}

}


return false;
}

}

2016年3月4日 星期五

Android 開發筆記 - 使用 Java Reflection 製作 Event Handler 流程控制

在 Android 上練習一些題目時,有碰到一連串 function call 的需求,特別是把每項工作做成 RISC 架構後,出現了重複使用的可能性,假設有 A, B, C, D 四個函式,有時任務是 A->B->D,有時是 B->C->A 的流程,這時就想找一個可以彈性設置任務流程的機制,有一點像 C/C++ 的 function pointer 或 virtual function 可以動態決定對象的機制,就隨意亂打關鍵字,想知道有沒可能得知 function name 後可以呼叫 function 做事,就找到 Java Reflection :http://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html

於是乎,在 Java 環境中透過 ArrayList 記錄此次工作依序將執行的任務,每次挑一個出來用,就達成任務了 :P

紀錄一下(此例各個函式在此例沒有傳遞參數):

class Job {
private String tag = "Job";
private List<String> mFunctionNameList = new ArrayList<String>();

void callFunctions() {
if (mFunctionNameList.size() > 0) {
String methodName = mFunctionNameList.get(0);
mFunctionNameList.remove(0);
try {
Job.this.getClass().getDeclaredMethod(methodName).invoke(this);
} catch (Exception e) {
}
}
}

// call A -> C -> D
void task1() {
mFunctionNameList.clear();
mFunctionNameList.add("A");
mFunctionNameList.add("C");
mFunctionNameList.add("D");
callFunctions();
}

// call B -> C -> A
void task2() {
mFunctionNameList.clear();
mFunctionNameList.add("B");
mFunctionNameList.add("C");
mFunctionNameList.add("A");
callFunctions();
}

void A() {
Log.d(tag, "call A");
}
void B() {
Log.d(tag, "call B");
}
void C() {
Log.d(tag, "call C");
}
void D() {
Log.d(tag, "call D");
}
}

Android 開發筆記 - 從 Java 端傳遞 HashMap 物件 Android NDK/ JNI C/C++ 操作變更內容

C/C++ Code:

#include <jni.h>

extern "C" {

jobject Java_YourPackageName_updateHashMap(JNIEnv *env, jobject thiz, jobject hashmap) {
    if (!hashmap)
        return hashmap;

    jclass mapClass = env->GetObjectClass(hashmap);
    jmethodID obj_put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    if (mapClass && obj_put) {
        jstring key = env->NewStringUTF("Haha");
        jstring value = env->NewStringUTF("Hehe");
        env->CallObjectMethod(hashmap, obj_put, key, value);
        env->DeleteLocalRef(key);
        env->DeleteLocalRef(value);
    }

    return hashmap;
}
}


Android Java code:

public native HashMap updateHashMap(HashMap<String, String>map);
static {
        System.loadLibrary("native");
}

void usage() {
HashMap<String, String> mHashMap = new HashMap<String, String>();

        Log.d(tag, "HashMap size: "+mHashMap.size());
updateHashMap(mHashMap);
        Log.d(tag, "HashMap size: "+mHashMap.size());

        Iterator it = mHashMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry)it.next();
            Log.d(tag, "HashMap key: ["+pair.getKey()+"], value: ["+pair.getValue().toString()+"]");
        }
}

Android 開發筆記 - 使用 Android NDK / JNI 回傳 HashMap 物件至 Java 端

C/C++ Code:

#include <jni.h>

extern "C" {

jobject Java_YourPackageName_getHashMap(JNIEnv *env, jobject thiz) {

    jclass mapClass = env->FindClass("java/util/HashMap");
    jmethodID obj_init = env->GetMethodID(mapClass, "<init>", "(I)V");
    jmethodID obj_put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
    jobject hashMap = env->NewObject(mapClass, obj_init, 1);

    /*
    const char * input_data = "HelloWorld";
    int input_data_length = strlen(input_data);

    jbyteArray data = env->NewByteArray(input_data_length);
    env->SetByteArrayRegion(data, 0, input_data_length, (const jbyte *)input_data);
    env->CallObjectMethod(hashMap, obj_put, (jbyteArray)data, (jbyteArray)data);
    env->DeleteLocalRef(data);
    */
    jstring key = env->NewStringUTF("Hello");
    jstring value = env->NewStringUTF("World");
    env->CallObjectMethod(hashMap, obj_put, key, value);
    env->DeleteLocalRef(key);
    env->DeleteLocalRef(value);

    //env->DeleteLocalRef(hashMap);
    return hashMap;
}

}


Android Java code:

public native HashMap getHashMap();
static {
        System.loadLibrary("native");
}

void usage() {
HashMap<String, String> mHashMap = getHashMap();

        Log.d(tag, "HashMap size: "+mHashMap.size());
        Log.d(tag, "HashMap data with key(Hello): "+mHashMap.get("Hello"));
        Iterator it = mHashMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry)it.next();
            Log.d(tag, "HashMap key: ["+pair.getKey()+"], value: ["+pair.getValue().toString()+"]");
        }
}