从需要上下文 (this) 的 JNI 调用 Java 方法来接收帐户名称

Calling Java method from JNI requiring context (this) for receiving the account name

基本问题,但需要复杂的代码:

我想主要使用 Android NDK 获取在 Android 设备上使用的帐户名。出于安全目的,应尽可能以本机代码完成。在分析 Android 的源代码中 *1(见下文)中建议的调用时,结果表明无法访问原始数据库文件,并且可能由于缺少权限而只能读出帐户名。

因此我的下一个方法是使用 *1 的代码(见下文)并将其放入单独的 java class "Account" 中的 src 文件夹中。不幸的是,它需要 activity 上下文,我不知道如何在本机代码中请求它,因为一切都应该从本机代码完成,而不包括主 activity。我假设它可能是 JNI 调用的第二个参数 thiz,但我对此非常不确定。

我当前的本机代码基于 *2 并且看起来像这样,但目前无法正常工作。您有任何提示或更好的解决方案吗?我可以直接从本机代码调用 AccountManager 吗?

// based on http://www.rgagnon.com/javadetails/java-0284.html
JNIEXPORT jstring JNICALL Java_com_example_nils_myapplication_MyNDK_getMyString(JNIEnv* env, jobject thiz){

const char *str;

jclass myclass_class =(jclass) env->NewGlobalRef
        (env->FindClass ("com/example/nils/myapplication/Account"));

jmethodID constructorID = env->GetMethodID
        (myclass_class, "", "()V");

jmethodID methodID = env->GetMethodID
        (myclass_class, "getUsername", "(Landroid/content/Context;)Ljava/lang/String;");

jobject myclass_object =  env->NewObject
        (myclass_class, constructorID);

jstring s = (jstring)  env->CallObjectMethod
        (myclass_object, methodID, thiz);
[...]

这里是Javaclass

public class Account {
    public Account(){

    };

    // based on 
        public static String getUsername(Context c) {
            AccountManager manager = AccountManager.get(c);
            android.accounts.Account[] accounts = manager.getAccountsByType("com.google");
            LinkedList<String> possibleEmails = new LinkedList<String>();

            for (android.accounts.Account account : accounts) {
                // TODO: Check possibleEmail against an email regex or treat
                // account.name as an email address only for certain account.type values.
                possibleEmails.add(account.name);
            }

            if (!possibleEmails.isEmpty() && possibleEmails.get(0) != null) {
                String email = possibleEmails.get(0);
                String[] parts = email.split("@");

                if (parts.length > 1)
                    return parts[0];
            }
            return null;
        }


    }

回复评论#1:当然,最初必须在某处调用本机代码。出于测试目的,我将该调用放在我的主要 activity.

的 onCreate() 方法中
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyNDK a = new MyNDK();
        Log.i("MyTag", a.getMyString());

回复第二条评论:MyNDK 只是一个简单的 class 加载本机库,同时定义 getMyString() 方法。

public class MyNDK {

    static{
        System.loadLibrary("MyLibrary");
    }
    public native String getMyString();
}

*1 How can I get the google username on Android?

*2 http://www.rgagnon.com/javadetails/java-0284.html

嗯,谢谢提示。我解决了。所以这是解决方案:

构造函数需要作为名称

jmethodID constructorID = env->GetMethodID
        (myclass_class, "<init>", "()V")

此外,我采纳了 Michael 的建议,只是将上下文作为参数添加到 java 和相应的 C 函数中。 thiz2 是我们想要的上下文,而 thiz 是 MyNDK 的实例,正如 Michael 上面已经提到的那样。

public native String getMyString(Context c);


JNIEXPORT jstring JNICALL Java_com_example_nils_myapplication_MyNDK_getMyString
        (JNIEnv * env, jobject thiz, jobject thiz2) {

嗯,还剩下什么?帐户名称不是 returned,但出于测试目的,我使用了固定的 return 字符串。这解决了实际的 JNI 问题并且应用程序运行良好,但在接收帐户名称的 java 实现中肯定仍然存在错误。

总之。妥妥的解决了。谢谢大家。