Thursday, December 22, 2011

淺談OAuth in Android -- 以Plurk為例子

OAuth是目前比較熱門的認證機制之一,他有個最大的好處就在於完全不需要在client端輸入賬號密碼,這部分完全會由web端完成。不過,他機制非常的複雜,有些地方做的讓人覺得很龜毛,所以我們在這篇只要淺談就好,其他的部分我會介紹一個函式庫來把它完成。

這邊有一篇非常不錯的OAuth機制介紹,有興趣的朋友可以看看。太細節部分的工作原理我們就不涉足了,我們專心看看最基本的認證步驟

  1. app開發者跟網站註冊取得API Key跟API Secret
  2. 開始認證的時候,用這組key/secret/callback(*1)跟網站要一個Request Token(*2)
  3. 用這組Request Token, callback組合成一個網址,讓使用者進入該網址認證
  4. 認證成功以後會傳回一個oauth_verifier,通常是一個很好閱讀的字串或者數字
  5. app端把這組verifier,以及request key跟網站取得半永久性的Access Token(*2)
  6. 往後對這個網站任何要求(比方說,plurk來講,取得時間軸)使用這個Token即可,app把它存起來備用,就不需要重新跟網站申請認證了。

這幾個步驟其實相當的繁瑣,尤其是對網站要求每次都要做一個全面性的Parameter Sign的動作,裡面牽涉的東西對一個初學者來講真是煩人到爆炸。當然,就會有人會寫一組library來解決這問題:我們今天介紹的就是oauth-signpost library

基本的取得jar,放進project,設定buildpath這些基礎到爆的東西我們就不討論了。首先,我們要先去Plurk申請一組API Key來作為我們的開發用途。所以我們就有了API Key/Secret了。接下來,回到Plurk API介紹的頁面,他提供了幾個網址供OAuth使用。基本上這些分別是幹嘛的我們就不予深究了,我們只要知道她是要怎麼用。

signpost我們會用到根OAuth有關的一共有兩個部分:OAuthConsumer代表的是我們APP端的所有資料,包含Access Token等等(不過她好像沒幫你存,你要自己存),而OAuthProvider則是代表網站的OAuth認證部分。

static final String PLURK_REQUEST_URL = "http://www.plurk.com/OAuth/request_token";

static final String PLURK_AUTHORIZATION_URL = "http://www.plurk.com/m/authorize";

static final String PLURK_ACCESS_URL = "http://www.plurk.com/OAuth/access_token";

static final String PLURK_CALLBACK_URL = "myplurk:///";

static final String PLURK_CONSUMER_KEY = "你的API Key";

static final String PLURK_CONSUMER_SECRET = "你的API Secret";


我們先把這些東西設定變數,等等會用到。接下來我們設定Provider跟Consumer

mainConsumer = new DefaultOAuthConsumer(PLURK_CONSUMER_KEY, PLURK_CONSUMER_SECRET);

mainProvider = new DefaultOAuthProvider(PLURK_REQUEST_URL, PLURK_ACCESS_URL, PLURK_AUTHORIZATION_URL);

ok,那所有東西差不多就完成了。我們要讓使用者認證的時候,只要開一個url,載入provider提供的url :

String url = mainProvider.retrieveRequestToken(mainConsumer, PLURK_CALLBACK_URL);

利用webview打開這個以後,把webview設定一個WebViewClient,然後override它的link click

private class AuthClient extends WebViewClient {

@Override

public void onPageStarted(WebView view, String url, Bitmap favicon) {

super.onPageStarted(view, url, favicon);

}

@Override

public void onPageFinished(WebView view, String url) {

super.onPageFinished(view, url);

}

@Override

public boolean shouldOverrideUrlLoading(WebView view, String urlString) {

Log.d("SubPlurkV2", "url : " + urlString);

if(urlString.contains("subplurkv2")) {

Uri url = Uri.parse(urlString);

String verifier = url.getQueryParameter("oauth_verifier");

try {

SystemManager.getInst().getAuthManager().aquireAccessToken(verifier);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

finish();

}

return super.shouldOverrideUrlLoading(view, urlString);

}

然後我們可以取得verifier,利用這個取得Access Token

mainProvider.retrieveAccessToken(mainConsumer, verifier);

Log.d("SubPlurkV2", "Token = " + mainConsumer.getToken() + " and secret = " + mainConsumer.getTokenSecret());


把這祖Token記錄起來,以後會用到。最後,我們來試試看最簡單地拿取時間軸吧

mainConsumer.setAccessToken("U5GNYyH6wVGS", "aCtJ9XVgNTeIpigq3qxsLi70Sv0HbA6h");


 

URL url = new URL("http://www.plurk.com/APP/Timeline/getPlurks");

HttpURLConnection request = (HttpURLConnection) url.openConnection();

request.setDoOutput(true);

request.setRequestMethod("POST");

mainConsumer.sign(request);

request.connect();

String context = StreamUtil.InputStreamToString(request.getInputStream());

Log.d("SubPlurkV2", "" + context);


have fun! 參考plunk api list繼續實做其他的api吧!

Monday, December 19, 2011

幾個Android裡面Dependency的設定差異 (1)

 

Android裡面,設定Dependency有幾種方法,不過都各自有一些問題。

官方的做法是把其中一個設定成Lib Project,然後我們就能在Host專案裡面選取這個Lib Project,剩下的ADT會自動幫你搞定。

Select

Select

Check

 

然後,在要使用這個Library的專案,同樣的地方,下面的checkbox應該就會出現可以選擇的lib名稱,選Add加入就好

Screen Shot 2011 12 19 at 11 07 10 AM

 

這個方法是Android內建的方法,他有些問題跟限制。1. 他會被「Close not related project」關掉,這個簡單,大不了在打開 2. 他在clean build會出錯,要重復clean個幾次。3 被設定成lib的project就不能跑了,所以不能相依於另外一個完整地執行模組(手動開開關關lib開關是可以啦,只是麻煩)。

 

Wednesday, December 14, 2011

解決static library(.a)在IB以及某些category的靈異狀況。

Interface Builder是個很神奇的東西,裡面很多神秘的機制。其中最為讓人難以裡解的就是他跟dependency的恩怨情仇。對,android的java有更嚴重的問題(在UI上,android甚至沒辦法access到dependency的resource),不過這個Bug完全是因為Interface的linking機制是等到obj編譯完以後才開始找的關係(所以這個bug基本上只會出現在自定的widget class)。

這個bug大概會以這種形式發作:我們的.a(static靜態函示庫)裡面有某個widget class,而引用端的Interface Builder會引用這個class, 於是在某種情況下就出錯了

2011-12-14 13:58:50.999 TestPlatform[294:707] Unknown class MyClassBtn in Interface Builder file.

2011-12-14 13:58:51.010 TestPlatform[294:707] Unknown class MyClass2Btn in Interface Builder file.


這出錯的原因很簡單。因為Interface Builder是從.obj(compile from .a)裡面去獲得class的名字以及資訊,但是他找不到(廢話)。為什麼找不到?因為.obj裡面真的沒有這個symbol。所以IB只好很哀怨地把他設定回預設的class,比方說UIButton。當然,當你真的invoke起這個button又用了只有MyClassBtn才有的method,他就會理所當然地吐一個unregconized selector的runtime exception給你。

所以問題出在linker,自作主張地把這個.a裡面的symbol沖倒馬桶去沒有編譯進去.obj了。linker在編譯.a編譯成.obj的時候,為了一些效能以及檔案大小上的理由,會主動地去尋找其他的.obj裡面到底有沒有真的用到某個symbol,要是他在其他obj裡面找不到,那自然就會理所當然地把它扔了。

然後IB自然就沒辦法在編譯出來的.a找到symbol,一切合情合理。

解決的方法有兩個,我先聊聊第一個比較簡單的方法。為了防止linker愚蠢的(這樣將不太公平,天曉得這東西IB會用到?)把不該丟掉的symbol給沖到馬桶去,所以我們必須要呼叫他的static method來欺騙linker,這東西是有用的,不准丟掉。link show了一個比較hack的方法,你做一個static method,但是什麼都不做,然後在host呼叫他,linker就會被欺騙而不會把.a裡面的symbol丟掉才編成.obj。

其實下面也提到一個更簡單的方法,事實上你呼叫class靜態方法也可以,比方說[MyClassBtn class]。

後面會提到第二種方法,同樣可以解決這問題。不過再看第二種方法以前,我們先看看另外一個靈異現象。

Category也有類似的問題,不過起因不太一樣。當你去Categorize一個Native class(最常見的,恩,NSString)在編譯的時候會被錯誤的當作沒用到的丟棄了--這起因應該主要是因為linker的objc旗標buggy,我猜的--所以同樣的在使用static library時,library裡面native class的category都會無作用(很妙的是,自動完成使用的資料庫是.h,所以自動完成會自動地幫你找到method,但是runtime會因為找不到這個selector炸掉,真悲)。

解決的方法就是前面提過的第二種方法 -- 強制linker「你什麼symbol都不准丟掉」。在宿主的build setting裡面,找到other linker flags,加上-all_load或者-force_load(老實講前後兩者不太一樣,但是我沒有去追究他們到底有什麼細微的不同)。這同樣也可以解決IB找不到symbol的問題。

所以我們可以替這兩種方法做一個總結。第一種方法是藉由對linker額外的提示(但是code會混亂不堪),明確地告訴linker那些東西要額外地放進來。所以.a->.obj並不會include過多的symbol,不會造成.obj又肥又大的問題。當然缺點就是code要用一些非常莫名的東西來提示linker--當然IBOutlet/IBAction都用了,不在乎再多個幾行莫名其妙的code。我們其實可以藉由#define來稍為美化他一下,至少讓他[MyClass class];這行看起來不會那麼奇怪。另外,他對於category造成的問題其實是無解的。

第二種方法則沒有以上的所有缺點,但是會造成.a->.obj非常大--尤其當你的.a包山包海無所不包的時候(比方說我個人非常愛的Utility classes)--然後大通常伴隨的就是慢。不過,在普通的輕量級class裡面,這算是一個可以接受的效能損失。當然,category造成的問題,這是唯一解的樣子。聽說-force_load效能會比-all_load效能好,不過我沒比過。

static library問題多,還是用source code編譯最好(遠目)

Tuesday, December 13, 2011

iOS底下幾個NS Debug用的旗標

Screen Shot 2011 12 13 at 11 06 16 AM

首先,先Edit Scheme叫出下面這個畫面

Debug Arguments

 

這些Enviroment Varibles分別代表這些意思 :

 

NSZombieEnabled : 除錯BAD_ACCESS_ERROR用的,可以追蹤過度retain的時候的一些多於資訊

 

MallocStackLogging : 可以enable一些unhandled exception的call stack資訊。

 

NSDebugEnabled :設定除錯旗標,可以讓你在程式裡面判斷,比方說

#include <unistd.h>

int main (int argc, char ** argv, char ** envp)
{
#ifdef DEBUGENVIRON
 
if (!getenv("NSDebugEnabled"))
 
{
    setenv
("NSDebugEnabled","1",1);
   
... set the other variables ...
   
// Maybe this will complain about an autorelease pool.
   
char * executablePath = [[[NSBundle mainBundle] executablePath] filesystemRepresentation];
    execve
(executablePath, argv, environ); abort();
 
}
#endif
 
... do what you normally do in main() ...
}

但是老實講除此之外我也不知道他能幹嗎
Ref : 

http://stackoverflow.com/questions/3816136/condtional-environment-varialbes-in-xcode

http://www.yifeiyang.net/iphone-development-skills-of-debugging-articles-3-crash-after-debugging-skills-program/

Monday, December 12, 2011

iOS底下啓動Code Trace Stack的方法

XCode4以後,以往的Code Stack變成了一堆address map,所以當Unhandled exception被Throw, 就會出現象如下圖一般的悲慘畫面

Origin

 

這些Trace顯然對追bug幾乎一點幫助都沒有,inspect crashed thread又只能看到一堆assembly,以前那些親切的Exception Stack跑哪去了呢?

其實Stack trace在exception裡面一直都是存在的,我們要做的只是讓unhandled exception被印出來而已。為了達到這個目的,所以我們必須做個小小的手腳。

首先先介紹一個基本的觀念,在C裡面,所有的unhandled exception都有一個最終處理函數,這個函數是可以經由指標切換的。原始預設的函數就是印出一堆有的沒有的以後,執行exit()。我們要做的就是寫一個函數取代掉它

 

void uncaughtExceptionHandler(NSException *exception) {

NSLog(@"CRASH: %@", exception);

NSLog(@"Stack Trace: %@", [exception callStackSymbols]);

// Internal error reporting

}

 

接下來我們必須要告訴系統,預設的處理unhandled exception handler函數更改為這個函數

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

// Override point for customization after application launch.

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

 

return YES;

}

 

當然,我們有很多地方可以放NSSetUncaughtExceptionHandler,不過我們選擇放在application:didFinishLaunchWithOptions:裡面

 

然後我們就可以得到下面的結果 :

XCode4_ST_Fixed

 

看,是不是好除錯多了嗎? 至少可以知道exception是從哪裡丟出來的了

Thursday, July 28, 2011

Pure electronic music fits me.

As far as early 1999, pure electronic stepped down from main stage in Asia. From that time, Techno category in muzie(http://www.muzie.co.jp, it's almost largest free-musician sharing hub even in nowadays) were slowly "infested" by Trance, and even mixed in Euro style.

What's T-E-C-H-N-O?

It represents a music excluding any emotional elements, similar to Heavy Trance. It doesn't exclude vocal, but it only used/served as a sound resource, or, more exactly, an instrument. All elements in Techno represents a pure electronic, pure digital, and HEAVY BEAT in this music in category.

If you are appreciating in more emotional ones, you will more likely find your sound in Euro or Rave.

Time is changing, since pure electronic techno fades in west Asia.

Sunday, July 03, 2011

Suppression on Depression

I just post a term in some social network, and I deleted in 30 sec. What I have posted is not quite important.

I can never deny that I put it in my heart.

Since the departure continues, I remembered somebody just said to me : "don't you wanna change? change the situation that you always keep your relationship with your friend so thin and far". Commission is my only way to keep it, and it will be gone since communicated is gone or broken.

Quite fit the situation I have met, I must say I have put somebody in my mind.

But enough murmuring, I have my own way to pave. Such one is no doubt a significant loss to me, but I have my own way to go, my won thing to do. Things will be much simpler since I keep distance with her, all keeps in line, and transfer to my team to handle this.

Way to go.

Sunday, June 12, 2011

Demolished

冷氣壞了,躺在床上聽著Speed of the Flow發呆

說真的,這個鬼地方還真的不比監獄隔間大多少,唯一值得慶幸的似乎只有我不用在這個白色的監獄中多劃一塊出來塞個馬桶進去...雖然在外面跟人共用的雅房盥洗室好像好不到哪裡去就是。

還有13個小時...

上次遠行似乎已經是好擠個世紀以前的事情了。對著不算小的行李拖行箱裡面巨大的空間,我努力的回想著其他人到底是怎樣把他填滿的,尤其當我看著床上整理出來的belongings... 恩,扣掉那三本該死的書的話,一個中型小背包就全部搞定了阿。

該死,還是得帶上那三本書 orz..

而且在填滿著行李箱的時候,我還要努力的讓自己的大腦,不被過往的回憶填滿...

Saturday, April 23, 2011

解決Android in Eclipse下,在.xml下按下run卻亂編出.out.xml的方法

這方法起因是因為一個元件衝突:XSL Editor.
解決方法其實很簡單,就把它關掉就可以了



從此王子跟公主...真的能因為這種事情過著幸福快樂的日子嗎(抱頭)

Friday, April 08, 2011

How to set automatic debug flag in Android for Logging

In my old method, I prefer to copy/paste follow code into each module.

static public boolean DEBUG_MODE = true; static public String DEBUG_MODEL_NAME = "your_module_name"; static public String DEBUG_TAG = "your_debug_tag"; static public void logD(String message){ if(DEBUG_MODE == false) return; Log.d(DEBUG_TAG, DEBUG_MODEL_NAME + " : " + message); } static public void logI(String message) { if(DEBUG_MODE == false) return; Log.i(DEBUG_TAG, DEBUG_MODEL_NAME + " : " + message); } static public void logE(String message) { if(DEBUG_MODE == false) return; Log.e(DEBUG_TAG, DEBUG_MODEL_NAME + " : " + message); }

This method is sharp, and you can debug each method. When you want to release, simply replace all "static public boolean DEBUG_MODE = true" to "static public boolean DEBUG_MODE = false" by Eclipse.



However, if your logging message is not so much, you can control logger flag in this way, more conveniently and efficiently.







PackageInfo packageInfo = ... // get package info for your context

int flags = packageInfo.applicationInfo.flags;

boolean DEBUG_MODE = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;




now you can control debug flag in AndroidManifest.xml's "Debuggable" flag, automatically.


Sunday, January 02, 2011

How To : 利用Time Capsule/Airport Extreme Base Station以及MobileMe在網 路 上使用AirDisk









AirPlay_Shared



Apple的Time Capsule/Airport Extreme Base Station(分別簡稱TC/AEBS)有一項主打的功能,是讓你能夠在遠端利用MobileMe的帳號存取你連接在TC/AEBS上面的儲存裝置。奇怪的是,這項功能雖然被寫在網頁上,可是說明書上並沒有詳細的使用方法(好歹我買AEBS跟MobileMe有一半的理由是為了他阿 orz)。這篇文章就來談談怎麼讓這個功能「啓動」。

MobileMe有一個功能稱為「Back To My Mac」,這是一個遠端remote自家產品的方法(包含iMac, MB系列....事實上也包含TC/AEBS)。首先,我們必須先讓這個功能啓動一下。







201101020224.jpg

這功能位置在System Preference->MobileMe->Back to My Mac。如果你還沒啓動過這個功能的話,把他給啓動一次。成功的啓動完畢以後,看你要關掉他還是怎樣都可以。啓動一次的目的在於讓他跟MobileMe的Server溝通,取得密碼。















接下來我們啓動Keychain Access。







Screen shot 2011-01-02 at 2.27.17 AM.png











先在畫面左邊點選System,然後在Name裡面找出.mac.com(某些人的MobileMe賬號產生的會是.me.com,這些以下同,不再贅述)。然後請右鍵(或者雙指壓觸控板)後選擇「Copy password to clipboard」,把密碼拷貝起來吧!(他會跟你要管理者權限),等等會用到。











到這邊,目前準備工作已經就緒了。現在讓我們打開我們的AirPort Utility。







讓我們開始設定AEBS吧(TC其實以下同)!先進入Advanced->MobileMe加入你的MobileMe帳號(這應該不用附圖吧冏)







再進入AirPort->Base Station 按下Edit











Screen shot 2011-01-02 at 2.49.11 AM.png











Screen shot 2011-01-02 at 2.49.52 AM.png











最重要的一點就是Password不是你的MobileMe賬號,很多人都搞錯了再跟我哀 冏...











然後進去AirDisk把裡面分享打開Screen shot 2011-01-02 at 2.49.32 AM.png











請先確定一下你區網內可以正常Access AirDisk!AirDisk只能是HPS+(MacOS的檔案格式)/FAT32/exFAT而已,他無法讀取NTFS,如果你在區網下都讀取不到AirDisk了,那怎麼可能在Internet下就可以了呢?請務必先確認這個(按下Disks標簽進去看一下吧)











最後,不能免俗的,update我們的AEBS吧,AEBS重開機以後應該就沒問題了。











OK,接下來我們來談談測試的方法跟Back to My Mac的原理。事實上MobileMe有一組隱藏的(使用者也不知道)的動態DNS服務,他的DNS Host在(你的使用者名稱).members.mac.com,帳號就是剛剛那一組,密碼則是Server會產生一組給你。所以只要我們能夠有這組賬號密碼,我們就能在Internet上取得我們AEBS的網路位置。而這組位置是能ping的喔,所以我們要測試的話並不需要離開區網,只要打開terminal去ping他看看...











Screen shot 2011-01-02 at 3.13.57 AM.png











只要能夠ping到這組位置,其實我們就可以確定他在internet下能夠正常作業了。Finder上的Shared標簽中的AirDisk,即使你離開區域網路環境,也可以毫無問題的正常使用喔。