2016年3月4日 星期五

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()+"]");
        }
}

設定 Nginx 透過 proxy_pass 存取 Apache web server + SVN + DAV 服務 @ Ubuntu 14.04

這個故事是這樣的,當所有的 server deploy 都用預設用 nginx 後,有個成員跳出來說要用 SVN over HTTP 服務時,就發現必須架設 Apache Web server ,因為 SVN over HTTP 的相關模組就只有 Apache Web server 才有,所以就變成此況的窘境了。

總之,架設 Apache web server 很容易,而安裝 mod_dav_svn 也差不多:

$ sudo apt-get install subversion libapache2-mod-svn apache2
$ sudo vim /etc/apache2/ports.conf
從 Listen 80 改成 Listen 8000,避開跟 Nginx 預設 80 port 衝到
$ sudo vim /etc/apache2/sites-available/000-default.conf
從 <VirtualHost *:80> 改成 <VirtualHost *:8000>
$ sudo a2enmod dav_svn authz_svn
$ sudo vim /etc/apache2/conf-available/svn.conf
<Location /svn>
        DAV svn
        SVNParentPath /path/svn
        AuthzSVNAccessFile /path/svn_access
        AuthType Basic
        AuthName "Web svn"
        AuthUserFile /path/svn_auth
        Require valid-user
</Location>
$ sudo service apache2 restart


接著設定 Nginx:

location /svn/ {
proxy_pass http://127.0.0.1:8000/svn/;
}

# 更新:假設 svn repo 裡有 .ht 開頭的檔案,可能會被 nginx 條件擋下
location ~ /svn/.*\.ht {
proxy_pass http://127.0.0.1:8000/;
}


如此一來即可搞定。

2016年2月29日 星期一

關閉 Android Transfer 自動開啟服務 @ Mac OS X 10.11.3

好一陣子,每次手機插上 Mac OS X Usb 後,就會自動彈跳 Android Transfer ,有些不方便 :P 解法就是關閉相關程式,例如重新命名:

$ kill `pgrep "Android File Transfer Agent"`
$ cd $HOME/Library/Application\ Support/Google/Android\ File\ Transfer && mv Android\ File\ Transfer\ Agent.app/ Android\ File\ Transfer\ Agent-DISABLE.app/
$ cd /Applications/Android\ File\ Transfer.app/Contents/Resources/ && mv Android\ File\ Transfer\ Agent.app/ Android\ File\ Transfer\ Agent-DISABLE.app/


之後,有需要 Android Transfer 時,就自行去點擊開啟他吧!

2016年2月27日 星期六

Android 開發筆記 - 在 Android Studio 環境上,使用 Google Analytics SDK / Admob SDK

使用 Android Studio 1.5.1 版,原先參考 Google 官方文件,結果一直爆問題 :P 其中 Google Analytics SDK 跟 Admob SDK 都會需要 Google Service 的環境,而碰到 Google Service 的引入不斷出錯,以下單純記錄修正流程。

參考 https://developers.google.com/analytics/devguides/collection/android/v4/https://developers.google.com/admob/android/app 資訊,更新 AndroidManifest.xml 位置,添加:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>


接著在 build.gradle (Project) 添加:

classpath 'com.google.gms:google-services:2.0.0-alpha6'

在 build.gradle (app) 添加:

dependencies {
//...
compile 'com.google.android.gms:play-services-analytics:8.4.0'
}
apply plugin: 'com.google.gms.google-services'


結果一直出錯:Gradle sync failed: Unable to load class 'com.android.sdklib.repository.FullRevision'. 這種很 general 的訊息。

甚至跑去看看 googlesample 檔案,如:
分別查看:build.gradleapp/build.gradle 和 gradle/wrapper/gradle-wrapper.properties 三個檔案。

改採用:

dependencies {
        //classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'com.android.tools.build:gradle-experimental:0.4.0'

        classpath 'com.android.tools.build:gradle:2.0.0-beta5'
        classpath 'com.google.gms:google-services:2.0.0-beta5'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
}


仍是一樣的現象,最後靠直覺,嘗試亂改 classpath 'com.google.gms:google-services:1.0.0+' 後,會看到錯誤訊息引導說,可以查看 metadata 得知有哪些版本可用:

https://jcenter.bintray.com/com/google/gms/google-services/maven-metadata.xml

就這樣,直接挑沒有 beta/alpha 字眼:

dependencies {
        classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'com.android.tools.build:gradle-experimental:0.4.0'
        classpath 'com.google.gms:google-services:1.5.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
}


以及 gradle/wrapper/gradle-wrapper.properties 使用:

#distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip

如此一來,終於解掉編譯問題。接續就可以參考 Google Analytics SDK/Admob SDK 的文件,例如產生 google-services.json 以及 AnalyticsApplication extends Application 等用法等。

最後,我自己的用法:

AndroidManifest.xml:
...
<application
        android:name=".MyApplication"
        ...
>
...
</application>


MyApplication.java:
import android.app.Application;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.Tracker;
public class MyApplication extends Application {
        private Tracker mTracker;
        synchronized public Tracker getDefaultTracker() {
               if (mTracker == null) {
                      GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
                      // To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG
                      //mTracker = analytics.newTracker(R.xml.global_tracker);
                      mTracker = analytics.newTracker("UA-XXXXXXX-XX");
               }
               return mTracker;
        }
}


build.gradle (app):
dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'

        compile 'com.android.support:appcompat-v7:23.2.0'
        compile 'com.android.support:design:23.2.0'

        // Google Analytics & Admob
        compile 'com.google.android.gms:play-services:8.4.0'
        compile 'com.google.android.gms:play-services-ads:8.4.0'
        compile 'com.google.android.gms:play-services-analytics:8.4.0'
}


build.gradle (Project):
dependencies {
        classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'com.android.tools.build:gradle-experimental:0.4.0'

        classpath 'com.google.gms:google-services:1.5.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
}


MainActivity.java:
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyApplication application = (MyApplication) getApplication();
        Tracker mTracker = application.getDefaultTracker();
        mTracker.setScreenName("main");
        mTracker.send(new HitBuilders.ScreenViewBuilder().build());
        //...
}