티스토리 툴바

cocoahttp 소스를 컴파일하여 테스트하는 중에는 아무런 문제가 없었다. 그런데 이것을 가지고 라이브러리를 만들어서 테스트 하던 중 전혀 문제가 없던 곳에서
[NSNumber parseString:intoUInt64:]: unrecognized selector sent to class

에러 가 발생한 경우, 이것은 DDNumber.h 와 DDNumber.m 파일에 선언, 정의된 NSNumber (DDNumber) 때문에 발생하는 것으로

NSNumber 에 선언된  parseString:intoUInt64: 라는 메쏘드를 찾지 못해

에러가 발생한 것 임.

해결 방법은 프로젝트 정보에서 Other Linker flag 항목에 -all_load 를 추가해주면 된다.

[참고]

http://blog.naver.com/heehow/140125329391

http://randomcsnippets.blogspot.com/2011/04/error-message-unrecognized-selector.html

저작자 표시 비영리 동일 조건 변경 허락

この文章はもともと韓国のアンドロイド・コミュニティで見つけたもので、原文は下のリンクをクリックすると確認できます。
http://www.androidpub.com/899711

「原文翻訳」
1.TabActivityの構成
まず、TabActivityが動作する簡単な原理をまとめてみると

1)TabActivityが生成されているTabHostを取る。

getTabHost();


2)TabHostを通じてTabSpecを生成しながらIndicatorとIntentを指定する。

Intent intent = new Intent().setClass(this, com.mk.counsel.group.ViewCounselGroup.class);

spec tabHost.newTabSpec(“tab01”).setIndicator(new TabView( thisR.drawable.tab1_selector“tab01”)).setContent(intent);


3)TabHostにTabSpecを登録する。

tabHost.addTab(spec);



上の過程でTabSpecに適用されるIntentが一つのAcitivityのみを処理する形と色んなActivityが一つのTab内で管理される場合(絵を参考)で構成されられますが、後の場合は問題が発生する恐れがあります。
Open in new window
単 純にActivityを変更する為にstartActivity()関数を使うとTabActivityが新しいActivityに変更されてしまって既 存のTab画面を維持できない問題が出るので、この問題を解決するために登場したのがActivityGroupです。

2.ActivityGroup
ActivityGroup は飽くまでActivity実行に対する管理と画面処理(View)を分けて管理する為のものだと思ったら問題ないと思います。Activity実行に対 する管理は全的にLocalActivityManagerに委任して、各種イベント処理に関してはActivityに任せて生成された最終画面 (View)のみを本人が持つように構成されています。(絵を参考)
問題は典型的なActivityGroupは全てのActivityの管理権 限をLocalActivityManagerに委任した状態で、Viewのみを貰うので、自体的にナビゲーション処理が出来ないということです。ここで ナビゲーションを管理する対象が決まります。それはActivityGroup が唯一に所有できるViewです。このViewらはActivityGroupが管理することでナビゲーションが可能になるということです。

3.実際適用
次のコードをご覧ください。

public class NavigationGroupActivity extends ActivityGroup {
      
ArrayList<Viewhistory// Viewを管理する為のList
     
NavigationGroupActivity group// Activityが接近する為のGroup
      
@Override
     
protected void onCreate(Bundle savedInstanceState) {
           
// TODO Auto-generated method stub
           
super.onCreate(savedInstanceState);
           
history = new ArrayList<View>();
           
group this;
     }
      public 
void changeView(View v)  { // 同じLevelの Activityを他のActivityに変更する場合

           
history.remove(history.size()-1);
           
history.add(v);
           
setContentView(v);
     }
     public 
void replaceView(View v) {   // 新しいLevelのActivityを追加する場合
           
Log.d("MK","REPLACE VIEW...");
           
history.add(v);  
           
setContentView(v);
     }  
     public 
void back() { // Back Keyが押された場合の処理
           
if(history.size() > 1) {  
               
history.remove(history.size()-1);  
               
setContentView(history.get(history.size()-1));
           }
          else {  
               
finish(); // 最上位Levelの場合TabActvityを終了しなければならない。
           
}  
     }
     @
Override
     
public void onBackPressed() { // Back Keyに対するEvent Handler
           
group.back();  
           return;
     }
}


上で言ったとおりActivityGroupが管理できる唯一なリソースがViewなのでViewを保存するList を一つ生成します。既存には Activityが変更されるたびにLocalActivityManagerが渡すViewをActivityGroupが貰って画面処理だけしたのを 少し能動的にViewを直接管理することに変わりました。つまりBack Keyに対するイベントが発生すると、これを処理するハンドラーがActivityGroupにView Listで画面転換処理を直接求めることです。
では、Back Key処理は分かるが、新しいActivityを追加する場合はどこで誰が呼び出してくれるかが知りたくなると思います。次のコードをご覧ください。

public class NavigationActivity extends Activity {
      public 
void goNextHistory(String id,Intent intent)  { //次の画面に移す処理
              
NavigationGroupActivity parent = ((NavigationGroupActivity)getParent());
              
View view parent.group.getLocalActivityManager()
                   .
startActivity(id,intent)  
                   .
getDecorView();  
                
parent.group.replaceView(view);
     }
     
      @
Override
     
public void onBackPressed() { //前の画面に戻る処理
                
NavigationGroupActivity parent = ((NavigationGroupActivity)getParent());
                
parent.back();
     }
}


上のコードを見ると該当Activityを所有しているActivityGroupをgetParent()関数で求めた後に実際のActivityの実行をLocalActivityManagerに任せています。
LocalActivityManager で実行された(startActivity()関数を呼び出し)ActivityのViewをgetDecorderView()関数を通じて得た後、こ のViewをActivityGroupのViewに適用する姿が見られます。逆にBack Keyが発生するとParentのback関数を呼び出してActivityGroup内のViewを調節してナビゲーション処理をするように求めます。
では、今からはこの二つのクラスを相続してもらった実際のActivityGroupとActivityがどういうふうに適用されるかを見てみます。
下のコードをご覧ください。

public class ViewCounselGroup extends NavigationGroupActivity {
     @
Override
     
protected void onCreate(Bundle savedInstanceState) {
           
// TODO Auto-generated method stub
           
super.onCreate(savedInstanceState);
           
Intent intent = new Intent(this,ViewCounselMainActivity.class);
           
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |Intent.FLAG_ACTIVITY_SINGLE_TOP);
           
View view getLocalActivityManager().startActivity("CounselMainActivity",intent)
              .
getDecorView();  
          
replaceView(view);  
     }

     @
Override
     
public void onBackPressed() { // Back Keyに対するリクエスト
           
super.onBackPressed();
     }
}


ViewCounselGroup というActivityGroupではLocalActivityManagerを通じて最初に実行するActivityの ViewCounselMainActivityというクラスをIntentで実行します。この時Intentに適当なFlagを設定しなければ LocalActivityManagerが管理するStackにActivityがViewの保存構造と違う形にたまられますのでナビゲーションが同期 化できないです。従って必ず上のIntent Flagを設定してからActivityとViewの順序が同期化されます。なおBack Keyに関する処理は全てのActivityで処理されることではなく、ActivityGroupで実行した最初のActivityでばかりイベントを 貰えるということを必ず覚えてください。
Intentに関するFlagの説明は次のアドレスを参考してください。
http://chihun80.springnote.com/pages/6423199
Intent が設定されるとLocalActivityManagerを通じてActivityを実行するし、この時に生成されたViewを ActivityGroupに適用してくれなければならないですが、ただ適用したら出来ないのでreplace()という関数を通じてViewを管理する Listに登録した後から画面に適用するべきです。

public class ViewCounselMainActivity extends NavigationActivity
implements OnItemClickListener{

      
……省略……
     
@Override
     
protected void onCreate(Bundle savedInstanceState) {
           
// TODO Auto-generated method stub
           
super.onCreate(savedInstanceState);
           
setContentView(R.layout.view_counsel_list);
           
adapter = new CounselAdapter(this,items);
           
ListView view = (ListView)findViewById(R.id.counsel_list);
           
view.setAdapter(adapter);
           
view.setOnItemClickListener(this);
     }

     @
Override
     
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
           Intent intent = new Intent(ViewCounselMainActivity.this,
                                           com.mk.counsel.ViewCounselDetailActivity.class);
           intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |Intent.FLAG_ACTIVITY_SINGLE_TOP);
           intent.putExtra("id", items.get(position).id);
           goNextHistory("ViewCounselMainActivity",intent);
     }
}


動作に対するイベント処理などは実行されたActivity内で管理がされると言ったとおり GroupActivityで最初に実行された ViewCounselMainActivity内で特定のアイテムが選択された場合にItemClickハンドラーを処理してから次のLevelの Activityに移動しようとしたらgoNextHistory()を通じて移したいIntentを渡せばGroupActivity内部で LocalActivityManagerを通じてIntentを実行して、ViewをGroupActivityのViewListに追加して画面を転 換する作業をしてくれます。

4.最後に
TapSpecにIntentを設定する時、GroupActivityをまず登録してからGroupActivity内で最初に呼び出すActivityをLocalActivityManagerを通じて実行してViewを得る過程がポイントです。
こ れでTabActivityとActivityGroupの間の関係整理とActivityGroupでLocalActivityManagerを通じ てActivityとViewを管理するメカニズムを見てみました。まず、絵を見て全体的な流れを理解した後から該当のソースを見たらもっと分かりやすい と思われます。
…...<省略>.....

上の処理はActivityのライフサイクルに従っていることではなく、 画面の状態をキャッシュしてウェブブラウザみたいにViewを変える処理です。もし Activityのライフサイクルを従うのが欲しい方はhistory内にViewの代わりにActivityを実行するIntentを保存した後、移動 可否によって該当のIntentをLocalActivityMangerを通じて実行すれば良いです。
このようにしたら次のようなライフサイクルになります。

// A Activity実行
11-11 09:34:20.449: DEBUG/USilver(21719): (A):Lifecycle is OnStart
11-11 09:34:20.454: DEBUG/USilver(21719): (A):Lifecycle is OnRestart
// B Activity実行
11-11 09:34:29.174: DEBUG/USilver(21719): (A):Lifecycle is OnPause
11-11 09:34:29.184: DEBUG/USilver(21719): (B):Lifecycle is OnStart
11-11 09:34:29.184: DEBUG/USilver(21719): (B):Lifecycle is OnResume
// B -> A ActivityでBack処理
11-11 09:34:49.419: DEBUG/USilver(21719): (B):Lifecycle is OnPause
11-11 09:34:49.424: DEBUG/USilver(21719): (A):Lifecycle is OnNewIntent
11-11 09:34:49.424: DEBUG/USilver(21719): (A):Lifecycle is OnRestart
// A->B Activity実行
11-11 09:40:57.024: DEBUG/USilver(21719): A:Lifecycle is OnPause
11-11 09:40:57.024: DEBUG/USilver(21719): B:Lifecycle is OnNewIntent

11-11 09:40:57.024: DEBUG/USilver(21719): B:Lifecycle is OnResume

이 글은 스프링노트에서 작성되었습니다.

'개발 > Android' 카테고리의 다른 글

TabActivity内でActivityGroupを使う場合のNavigation処理  (0) 2011/01/31

이 글은 아래에 링크 걸려있는 블로그의 내용을 번역한 내용입니다.

この文書は下のリンク先のブログの内容を翻訳したものです。

http://niw.at/articles/2009/02/06/how-to-enable-the-popup-window-on-uiwebview/ja

번역이 매끄럽지 못한 점, 죄송합니다..;; 


UIWebView는 iPhone SDK에서 꽤 중요한 UIKit 클래스입니다. 알고 계신대로 사파리가 통째로 들어있지만, 윈도우를 열거나 팝업 등의 이벤트는 처리되지 않도록 되어 있습니다.

<a href="somehere" target="_blank" />Open this link in new window</a>

예를 들면, 이러한 링크가 UIWebView 안에 표시되어 유저가 클릭했다고 해도 아무런 동작을 하지 않습니다. 물론, UIWebView는 상당히 고도로 추상화되어 있고, 몇가지 메소드를 호출하는 것은 가능합니다.

그래서 완벽하지는 않지만 어느정도 이 문제를 해결 할 방법을 제공합니다. 열쇠가 되는 메소드는 다음과 같습니다.

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest)request navigationType:(UIWebViewNavigationType)navigationType

-(void)webViewDidFinishLoad:(UIWebView *)webView

-(NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script

 

모든 팝업 윈도우를 가로채기

먼저 표시하고 있는 웹페이지의 모든 윈도우를 여는 이벤트를 가로챕니다. 방법은 JavaScript로 해결합니다. 먼저 모든 새 윈도우 열기 이벤트를 가로채서 열어야하는 URL을 특정화 하고, 그곳에 Objective-C 쪽에서 그 특정화 한 URL을 재처리하여 자신이 윈도우를 엽니다.

-(void)webViewDidFinishLoad:(UIWebView *)webView {

[webView stringByEvaluatingJavaScriptFromString:/* JavaScript가 들어간 문자열 */]

}

다음의 JavaScript를 stringByEvaluatingJavaScriptFromString에 넘깁니다.

var tags = document.getElementsByTagName("a");

for(var i=0; i < tags.length; i++) {

var tag = tags[i];

var t = tag.getAttribute("target");

var h = tag.getAttribute("href");

if(/* target과 href를 확인 */) {

tag.setAttribute("target", "");

tag.setAttribute("href", /* href 속성으로부터 특정한 URL을 작성 */);

}

}

webViewDidFinishLoad가 호출되었을 때에 JavaScript를 샐행하여 모든 anchor 태그를 가로채어 타겟 속성을 삭제하고 href에 특정화 시킨 URL으로 치환합니다. 다시 폼과 JavaScript로부터 이벤트를 가로챕니다.

var tags = document.getElementsByTagname("form");

for(var i=0; i < tags.length; i++) {

var tag = tags[i];

var submit = tag.submit;

tag.submit = function() {

var t = tag.target;

var a = tag.action;

if(/* target과 action을 확인 */) {

tag.target = "";

tag.action = /* action속성으로부터 특정한 URL을 작성 */

}

return submit.apply(this, arguments);

};

}

이 코드는 상당히 이상한 코드입니다. window.open이 호출 될 때에 화면 뒤 쪽에서 보이지 않는 anchor 태그를 만들어 그것에 특별한 URL을 설정하여 HTTP Request를 발생시킵니다. 이 Request를 Objective-C 쪽에서 받습니다.

 

팝업 이벤트를 Objective-C에서 받기

이것으로 Objective-C 쪽에서 이벤트를 받을 수 있게 되었습니다. UIWebView의 delegate에서 팝업 이벤트를 받아 처리합니다.

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {

if(navigationType == UIWebViewNavigationTypeLinkClicked || navigationType == UIWebViewNavigationTypeFormSubmitted) {

NSURL *url = [request URL];

if(/* URL이 특별한 건지 확인 */) {

NSString *urlstr = /* 특정한 URL으로부터 보통의 URL을 반환 */

NSMutableURLRequest *req = [request mutableCopyWithZone:nil];

[req setURL:[NSURL URLWithString:urlstr]];

if(/* 팝업이 열려있는지 확인 */) {

/* 팝업을 연다 */

}

[popupWebView loadRequest:req];

return NO;

}

}

return YES;

}

이 아주 긴 이름의 delegate로 모든 HTTP Request를 취득할 수 있습니다. 먼저 Request의 URL가 JavaScript로 만든 특정화 시킨 것인가를 체크하여 만약 그렇다면 보통의 URL을 반환하여 NSURLRequest를 작성하고, 팝업으로 띄울 UIWebView로 넘깁니다. 이 들 코드에 의해서 UIWebView로 팝업을 구현할 수 있습니다.

 

특별한 URL을 작성

이 문제는 조금 어렵습니다. 왜냐하면, HTTP Request의 베이스 URL을 보유하고 있을 필요가 있기 때문입니다. URL을 바꿔 쓰더라도 안전한 부분, 예를 들면 스키마나 호스트명 등입니다만, 이것들을 바꿔쓰면 베이스 URL을 잃어버려 webView:shouldStartLoadWithRequest:navigationType에서 팝업을 위한 URL을 작성할 수 없게 되버립니다. 그렇기 때문에 한가지 대안으로 URL의 마지막에 특별한 Hash를 부여하는 것으로 일단 해결해보겠습니다. 그다지 안전하지는 않지만 대부분의 상황에서는 안전합니다.

function makeSpecialURL(url) {

return url + (url.match(/#/) ? "_open" : "#open");

}

그리고 webView:shouldStartLoadWithRequest:navigationType에서 추가한 Hash를 삭제합니다.

if([[url fragment] hasSuffix:@"open"]) {

NSString *urlstr = [[url absoluteString] substringToIndex:[[url absoluteString] length] - 5];

...

}

 

그 밖의 방법에 대해서

이 문제는 아마도 문서에 포함되지 않은 API나 delegate(webView:createWebViewWithRequest:)로 해결 할 수 있을겁니다. 하지만 iPhone SDK에서는 이 JavaScript를 사용하는 방법 이외에는 해결 방법이 없겠지요. 별로 좋은 구현방법은 아니지만, iPhone 어플리케이션 제작에 도움이 되면 좋겠다고 생각합니다.

이 글은 스프링노트에서 작성되었습니다.