2011年7月25日 星期一

[PHP] 快速縮網址程式碼 (TinyURL API)

想說試用一下縮網址的功能,就找了 tinyurl.com 來測試。


一開始寫了冗長的 curl 程式碼:


<?php  
        $ch = curl_init();
        curl_setopt( $ch , CURLOPT_URL , 'http://tinyurl.com/create.php' );
        curl_setopt( $ch , CURLOPT_RETURNTRANSFER , true );
        curl_setopt( $ch , CURLOPT_POST , true );
        curl_setopt( $ch , CURLOPT_POSTFIELDS , array(
                'url' => 'http://www.google.com' ,
                'submit' => 'Make TinyURL!'
        ) );
        curl_setopt( $ch , CURLOPT_HTTPHEADER , array('Expect:') );
        echo curl_exec( $ch );
        curl_close( $ch );
?>


正準備用 preg 來撈資料時,看到一段訊息:


/////////////////////////////////////////////////////////////////////////////////
///// DON'T SCREEN-SCRAPE, PLEASE CONTACT US TO FIND OUT HOW TO USE OUR API /////
/////////////////////////////////////////////////////////////////////////////////


我才開使用 tinyurl.com + api 關鍵字問 Google 得知更簡單的用法:


<?php
        $url = 'http://www.google.com';
        echo file_get_contents( 'http://tinyurl.com/api-create.php?url='.urlencode($url) );
?>


有 API 不用,這樣對嗎 :p


2011年7月24日 星期日

Android 平民機是否快來臨?


來源:http://www.android.com/media/


今天跟許青蛙吃飯碰面,好像兩年沒碰面了?聊到了 Android 手機。經由周邊人士的使用心得,在智慧手機或行動平台方面,iOS (iPhone/iPad) 的使用者體驗還是遠高於 Android 系統,也就是說有錢人還是比較肯花錢敗一支 iPhone 手機,雖然 CP 值沒有 Android 高,但是文化潮流驅使大家比較想擁有 iOS 設備,甚至有的一開始用 Android 手機的,在接觸 iOS 設備後,就被拉走了。這是 OS 的設計發展、時程關係,整體上 iOS 還是比 Android 成熟許多,並且在封閉系統中,可以將硬體效能使用得更一致更好,軟硬體開發更有效率,一來以往,就會發現 Android 的缺陷,例如開發工具沒有制式美化的 UI 架構等,以至於產出的 app 都是很簡單的畫面,那種感覺就像早期寫 HTML 時,沒有美化的 TABLE 列表,而 iOS 就像有官方制式 CSS 套版,隨便做都比較好看。


目前的近況還是把 iOS 跟 Android 定位在智慧手機、比較有錢的人才會買的,雖然市佔率一直上升,但還沒到八、九成民眾都使用的階段。然而 Android 手機的價格一直往下掉了,現在就可以看到空機 4000 元左右(去外頭通訊行,可以用更低的價買到),那代表在不遠的將來(?)就會存在綁最低方案的 0 元的 Android 手機,就像中華電信月租費 183 元綁 18 or 24 個月!


這樣推估的方式是基於月租費 183元 (綁約18或24個月) 的 0 元手機,大概可以在一些賣場上看到空機賣 2000~2500 元左右,所以 Android 假設跌到這個價錢,就即將進入高市佔率的時代了! 如果綁上網方案(mCool/mPro),那可能性又增加了。


時代變遷:


單色手機 -> 彩色手機/和弦鈴聲 -> 手機拍照/大螢幕 -> 智慧手機/上網功能 (Android 手機?)


雖然 Android 進步要追到 iOS 還有段距離,但可以看到他有不斷地增加架構、運行效能等,並搭配硬體的升級,在不久的將來,應該更可以達到以前四、五年前 Nokia/Motorola 的時代,人人都可以擁有的大眾機。近期的問題大概是 Android 專利問題 Orz 希望能夠更快、更妥善地解決。


2011年7月19日 星期二

小筆電的效能不該只看數字

跟一位在 Acer 工作的同學閒聊,跟他說前幾天我接觸了一台 ASUS EEEPC 的小筆電,只是一台要價快兩萬,使用後覺得效能還不錯,問看看他的意見,結果就被推薦一款 Acer 小筆電,要價不到一萬五,並且說 Acer 這台小筆電是採用 Intel® Celeron® 處理器,效能應該比我指的那台還好,因為 ASUS 那台是 Intel® Atom™ 處理器。只是我很不解的是前者 Intel® Atom™ 處理器 D525 (1.8GHz) 且 2 cores, 4 threads 的,總覺得比後者 Intel® Celeron® 處理器 U3600 (1.2GHz) 之 2 cores, 2 threads 來的好吧?


後來在網路上也找到評論,大部分都說 U3600 比 D252 好,於是終於找到一個網站還滿豐富的 cpu benchmarks 資料:


PassMark - CPU Benchmarks - List of Benchmarked CPUs


可以查到 U3600 的分數比 D252 來的高 XD 算是稍微有點數據來證明,只是嚴格的話應該還是要去考量測試的方式及環境等等。


經過這次的討論,讓我想起計算機組織的教授曾提過的事,當你買一台車的時候,不太會用多少馬力來評估,那為何要用 CPU 的時脈來評估效能呢?並且查看了 spec 後,覺得 CPU 的規劃的確都很不一樣:


Intel® Atom™ processor D525 (1M Cache, 1.80 GHz)


Max TDP:13 W


Intel® Celeron® Processor U3600 (2M Cache, 1.20 GHz)


Max TDP:18 W


Atom 應該是著重在省電吧?還有超省電的:


Intel® Atom™ Processor N270 (512K Cache, 1.60 GHz, 533 MHz FSB)


Max TDP:2.5 W


至於要買哪一台比較好,大概還是要試用過才知道 :P 現在對 ASUS 那台算用過,所以稍微有好感,雖然真的貴很多 XD 至於兩者 CPU 差了 5W 的部分,不曉得是不是可以用 W=IV 公式來計算可以多撐多久?只是省電又不只 CPU 的問題 XD 看看別人的實測資料吧:


Nvidia ION 的 Asus 1215N 筆電評測


一台高階小筆電--宏碁AspireOne AO753小筆電開箱  (可以找到一些大陸的測試,但在此就不列出了)


2011年7月17日 星期日

Android 開發筆記 - 設定 Windows 之 Android NDK (Native Development Kit) 開發環境

之前的學習環境都是 Ubuntu 桌機為主,無聊在 ASUS Eee PC 1215N - Intel(R) Atom(TM) CPU D525 / 1.80GHz / 4GB RAM / Windows 7 32-bit 上,安裝 Eclipse 和 ADT 後,發現單獨啟動 Android 2.3.3 模擬器竟然只要 5 分鐘左右耶,這真是神奇了!(這個速度對這類裝備已經算很驚奇了),隨後我便開始著手看看在 Windows 使用 Android NDK 的環境了。


關於 NDK 的使用,採用 Cgywin 環境,如此一來就可以透過 ndk-build 進行編譯的動作了。


安裝項目:



  • Java Platform (JDK) - jdk-6u26-windows-i586.exe (76.8MB)

  • Eclipse Classic - eclipse-SDK-3.7-win32.zip (174MB)

  • Android SDK - installer_r12-windows.exe (34.8MB)

  • Android NDK - android-ndk-r6-windows.zip (64.5MB)

  • Cgywin - 安裝完大小約 225MB


    • 來源選 ftp://ftp.ntu.edu.tw (不行再改用 http://ftp.ntu.edu.tw)

    • 安裝項目:


      • 必要項目 Devel 分類:


        • gcc: C compiler upgrade helper

        • gcc-core: C compiler

        • gcc-g++: C++ compiler

        • gcc-mingw-core: Mingw32 support headers and libraries for GCC

        • gcc-mingw-g++: Mingw32 support headers and libraries for GCC C

        • make: The GNU version of the 'make' utility

        • mingw-runtime: MinGW Runtime



      • 非必要:

        • Net > openssh: The OpenSSH server and client programs

        • Editors > vim: ViIMproved - enhanced vi editor

        • Utils > screen: Run separate screens on a single terminal





    • 小問題:

      • 路徑上有空白的問題

      • 以 Eclipse 的 workspace 來說,預設位置是 C:\Users\username\workspace ,但在 cgywin 上也可以從 c:\Documents and Settings\username\workspace 進入,但從後者來說,路徑上有空白將導致 ndk-build 過程會出錯,請避開使用,若使用者帳號有中文或空白的話,那也盡量把 workspace 移去其他地方吧






上述軟體在 Windows 上的安裝路徑:


C:\Program Files\Java
C:\cygwin
C:\eclipse
C:\Android\android-sdk
C:\Android\android-ndk-r6


隨後在 Eclipse 建立一個 Project,此例為 MyNDK:


Project name: MyNDK
Build Target: Android 2.3.3/Android Open Source Project/2.3.3/10
Application name: MyNDK
Package name: com.example.ndk
Create Activity: MyNDK
Min SDK Version: 3


其他細節請參考 Android 開發教學筆記 - 使用 Android NDK (Native Development Kit) ,除此之外要留意該篇文章的軟體版本或路徑是不一樣的,僅供參考。


透過 Cygwin 測試 ndk-build:


user@pc ~
$ cd /cygdrive/c/Users/username/workspace/MyNDK/jni

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ ls
Android.mk  my-jni.c

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ cat Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := my-jni
LOCAL_SRC_FILES := my-jni.c
include $(BUILD_SHARED_LIBRARY)

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ cat my-jni.c
#include <string.h>
#include <jni.h>
jstring Java_com_example_ndk_MyNDK_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from My JNI !");
}

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ cd ..

user@pc /cygdrive/c/Users/username/workspace/MyNDK
$ /cygdrive/c/Android/android-ndk-r6/ndk-build
Compile thumb  : my-jni <= my-jni.c
SharedLibrary  : libmy-jni.so
Install        : libmy-jni.so => libs/armeabi/libmy-jni.so


接著可以設定 Eclipse 讓它可以自動編譯 JNI 的部分,在 Project > Properties > New > Program :


Location: C:\cygwin\bin\bash.exe
Working Directory: ${workspace_loc:${project_path}} (不設定也行,在此沒用到)
Arguments: --login -c "cd /cygdrive/c/Users/username/workspace/${project_path} && /cygdrive/c/Android/android-ndk-r6/ndk-build"


ndk-build-eclipse-cygwin


別忘了設定 Build options,例如 Run the builder 裡勾選 After a clean / During a manual builds 等,就可以透過 clean 來啟動 ndk build 動作。


如此一來,就可以透過 cgywin 裡頭,透過 vim 編輯器撰寫 jni 的程式部分,也可以在裡面呼叫 ndk-build 來測試,最後再切換到 Eclipse 以便測試整合囉。關於這次的 Windows Android NDK 的使用,原先以為多安裝個 cygwin 很麻煩,但仔細想一下我在 Ubuntu 上的使用,一樣也是透過開啟一個 terminal 去編輯 jni 的部分,那轉換在 Windows 上的動作,其實也沒有比較多。


相關文章:



2011年7月14日 星期四

Android 開發筆記 - 使用 NDK / JNI 實作從底層呼叫上層 (C call Java)

一般使用 JNI 的情境,不外呼從 Java 呼叫 C ,接著在 C (底層)運算完後,把數值透過 return 的方式傳回給 Java (上層)端,這在官方 NDK 教學或是之前的筆記都可以看到簡單的範例:Android 開發教學筆記 - 使用 Android NDK (Native Development Kit)。然而,如果要從 Native C 去呼叫 Java function 的話,就不是那麼直觀,例如在 Unix 系統上的程式開發,則是需取得一個門牌號碼(process id),接著才跟他溝通,或是直接透過執行新的程式,去指定運行某個 function 的這種架構等。在此使用的環境為 Ubuntu 10.04 desktop 64-bit,提供簡易 C call Java 的範例。


一般 C call Java 的話,則需要建立一個 Java 運行的 JVM 通道,接著才開始找尋物件、函數並開始運作:


@ c_to_java.c


#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

int main()
{
JNIEnv *env;
JavaVM *jvm;

JavaVMInitArgs vm_args;
JavaVMOption options[1];

// launch Jvm
options[0].optionString = "-Djava.class.path=."; // add user classes
vm_args.version = JNI_VERSION_1_6; //JDK version.
vm_args.options = options;
vm_args.nOptions = 1;

if( JNI_CreateJavaVM(&jvm, (void*) &env, &vm_args) < 0 )
{
fprintf( stderr , "Launch JVM Error\n" );
exit(1);
}

// find the obj & method
jclass my_class;
jmethodID my_main;

if( !( my_class = (*env)->FindClass( env, "MyJavaClass") ) )
{
fprintf( stderr , "'Class' Not Found\n" );
exit(1);
}

if( !( my_main = (*env)->GetStaticMethodID( env, my_class , "main" ,  "([Ljava/lang/String;)V" ) ) )
fprintf( stderr , "'main' Not Found\n" );
else// Call main function
(*env)->CallStaticVoidMethod( env, my_class, my_main, NULL);

// finish
(*jvm)->DestroyJavaVM(jvm);

return 0;
}


@ MyJavaClass.java


class MyJavaClass
{
public static void main( String []arg)
{
System.out.println("Hello World");
}
}


@ Makefile


CC=gcc
INCLUDE=-I/usr/lib/jvm/java-6-sun/include/ -I/usr/lib/jvm/java-6-sun/include/linux/
LIB=/usr/lib/jvm/java-6-sun/jre/lib/amd64/server/libjvm.so

all:
        javac MyJavaClass.java
        $(CC) $(INCLUDE) $(LIB) c_to_java.c     
clean:
        rm -f ./a.out  ./*.class


@  /etc/ld.so.conf.d/jvm.conf 


/usr/lib/jvm/java-6-sun/jre/lib
/usr/lib/jvm/java-6-sun/jre/lib/amd64
/usr/lib/jvm/java-6-sun/jre/lib/amd64/server


執行結果:


$ make
$ ./a.out 
Hello World


由於我是在 Ubuntu 10.04 desktop 64-bit 環境,安裝 Sun Java6,所以 gcc 編譯要透過 -I 指定 header file 和 libjvm.so 檔案外,在執行 a.out 的時候也會出現找不到 share library 的情,這時候要去設定 /etc/ld.so.conf,在此是透過建立 /etc/ld.so.conf.d/jvm.conf 並再用 sudo ldconfig -v 進行 loading ,之後在執行 a.out 就不會出錯了


接著是 Android 端之 JNI 從底層 C 呼叫上層 Java 端的部分,稍稍不一樣,整個程式運作流程:



  1. 啟動 Android app 時,進入程式內的 OnCreate 時,將本身 app 的資訊丟進 C 底層,並使用全域變數紀錄。

  2. 由於 C 帶有 Java 端相關資訊,因此 C 可以呼叫 Java 端的相關函數,完成了底層往上層呼叫的動作。


建構於Android 開發教學筆記 - 使用 Android NDK (Native Development Kit) 的範例:


請留意 NDK 版本,此例是用 android-ndk-r5c ,用新版的話,相關路徑要記得更新


目錄結構:


~/android-ndk-r5c
~/workspace/MyNDK
~/workspace/MyNDK/jni
~/workspace/MyNDK/jni/my-jni.c
~/workspace/MyNDK/jni/Android.mk
~/workspace/MyNDK/jni/Makefile  (非必要)


@ MyNDK.java


package com.example.ndk;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.lang.ref.WeakReference;

public class MyNDK extends Activity {
    /** Called when the activity is first created. */
static int count;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        TextView tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
        count = 0;
       
        initMyJNI(new WeakReference<MyNDK>(this));
    }

    private static void callback( int pid )
    {
        Log.i("MyNDK","Pid:"+pid+",Count:"+count);
    count++;
    }


    private native void initMyJNI( Object weak_this );

    public native String  stringFromJNI();

    static {
        System.loadLibrary("my-jni");
    }

}


@ my_jni.c


#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <pthread.h>
#include <android/log.h>

#define LOG_TAG"MyJNI"
#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__ )

// --- Header Begin ---
#defineJAVA_CALLBACK_FUNCTION_NAME"callback"
#defineJAVA_CALLBACK_FUNCTION_ARGS_TYPE"(I)V"

JNIEXPORT void JNICALL Java_com_example_ndk_MyNDK_initMyJNI( JNIEnv* env, jobject thiz, jobject weak_this );
void * threadJobRun();

static const char *classPathName = "com/example/ndk/MyNDK";
static JNINativeMethod methods[] = {
{
"initMyJNI" , 
"(Ljava/lang/Object;)V" , 
(void *)Java_com_example_ndk_MyNDK_initMyJNI

};

static JavaVM *jvm;// global for JVM init Info
static jobject mObject;// get the object
static jclass mClass;// get the class
static jmethodID mMethodID;// get the method

static pthread_t thread;

// --- End Of Header ---


static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;

if( !( clazz = (*env)->FindClass( env, className ) ) )
{
LOGE( "Unable to get the Class at registerNativeMethods" );
return JNI_FALSE;
}

if( ( (*env)->RegisterNatives( env, clazz, gMethods, numMethods ) ) < 0 )
{
LOGE( "Unable to register the methods at registerNativeMethods" );
return JNI_FALSE;
}
return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGI("IN JNI_OnLoad");
JNIEnv *env;
if( ( (*vm)->GetEnv( vm, (void **)&env, JNI_VERSION_1_6 ) ) != JNI_OK )
{
LOGE( "Unable to get the env at JNI_OnLoad" );
return -1;
}
if (!registerNativeMethods( env, classPathName, methods, sizeof(methods) / sizeof(methods[0])))
{
LOGE( "Unable to register native methods at JNI_OnLoad" );
return -1;
}
jvm = vm;
return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL Java_com_example_ndk_MyNDK_initMyJNI( JNIEnv* env, jobject thiz, jobject weak_this )
{
jclass clazz;

mClass = NULL;
mObject = NULL;
mMethodID = NULL;

if( !( clazz = (*env)->GetObjectClass( env, thiz ) ) )
{
LOGE( "Unable to get the object class" );
return;
}
if( !( mClass = (*env)->NewGlobalRef( env, clazz ) ) )
{
LOGE( "Unable to get the class ref" );
return;
}
if( !( mObject = (*env)->NewGlobalRef( env, weak_this ) ) )
{
LOGE( "Unable to get the object ref" );
return;
}

if( !( mMethodID = (*env)->GetStaticMethodID( env, mClass, JAVA_CALLBACK_FUNCTION_NAME, JAVA_CALLBACK_FUNCTION_ARGS_TYPE ) ) )
{
LOGE( "Unable to get the method ref" );
return;
}

LOGI("IN initMyJNI");

if( mClass && mMethodID )// first call
(*env)->CallStaticVoidMethod( env, mClass , mMethodID , (int)getpid() );

// thread
pthread_create( &thread, NULL, threadJobRun, NULL);
}
void * threadJobRun()
{
JNIEnv *env;
int isAttached;

isAttached = 0;
env = NULL;
LOGI("IN threadJobRun");
if( jvm )
{
LOGI("IN threadJobRun with JVM");

if( ( (*jvm)->GetEnv( jvm, (void**) &env, JNI_VERSION_1_6 ) ) < 0 )
{
LOGI( "Unable to get env at threadJobRun" );
if( ( (*jvm)->AttachCurrentThread( jvm, &env, NULL) ) < 0 )
{
LOGE( "Unalbe to attach current thread at threadJobRun" );
return NULL;
}
isAttached = 1;
}

while( 1 )
{
sleep(1);

// --- jobs begin
if( mClass && mMethodID )
{
LOGI( "call mClass & mMehtodID" );
(*env)->CallStaticVoidMethod( env, mClass , mMethodID , (int)getpid() );
}
// --- end of jobs
}

if( isAttached )
{
(*jvm)->DetachCurrentThread( jvm );
}
}
return NULL;
}

jstring Java_com_example_ndk_MyNDK_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from My JNI !");
}


@ Android.mk


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := my-jni
LOCAL_SRC_FILES := my-jni.c

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)


@ Makefile (非必要)


all:
cd ../ && sh ~/android-ndk-r5c/ndk-build


其中 Makefile 是我自己工作上使用的,我習慣用 vim + makefile 開發,所以會需要這個檔案,所以此檔非必要,其他檔案如同 Android 開發教學筆記 - 使用 Android NDK (Native Development Kit) 敘述。比較重要的是 my-jni.c 裡頭,有一個 JNI_OnLoad 和 initMyJNI 函數,在 app 一開始執行後,會先透過 JNI_OnLoad 將相關的資訊紀錄在 global variables 中,如 JVM 以及相關動作等,接著在 Java 呼叫 initMyJNI 時,才正式將 Java Object 紀錄起來,例如 Class 、Method 等,另外,在 initMyJNI 時,已經算是做到 C call Java 之 callback 的動作了(first call),只是我的目標是在 C 層跑一個 thread ,等到有事件時去更新 Java 層,所以有多一個 thread 的運行,在 threadJobRun 中則是 thread 的動作囉。


程式運作過程中,我有透過 Logcat 印出點訊息,可以看得清楚:


DDMS_MyJNI


其中 tag 為 MyJNI 是指 C 那層的動作,可以看到流程是 IN JNI_OnLoad、IN initMyJNI,接著就是第一次 c call java,在 Java 層印出 Pid:853, Count:0 的訊息,接著才進入 Thread ,然後每秒印出一次 Pid:853, Count:number 等資訊。


其他比較重要的是 C 裡頭也能可以呼叫 logcat ,記得在 Android.mk 中要加入 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog 才不會編譯失敗(擺放的位置也很重要,擺在上頭會失敗);而 c call java 的部分,在 Java 那邊定義的函數必須是 static 的,此例為 private static void callback( int pid ),不然也會出錯(因為 C 裡頭是用 GetStaticMethodID ),我有去找一下其他 open source (Kwaak3 / KwaakJNI.java),寫法也是這樣。


最後一提,上述 C 程式存在很多地方沒寫好,例如資源的釋放等,在此僅供用來了解流程用途。


參考資料: