2012年12月28日金曜日

[安藤]android 如何讓你的service永不停止

在android的世界裡面,service的設計的幾個難關:

  1. static的參考不見的時候該怎麼辦
  2. 傳進來的intent是null的時候該怎麼辦
  3. service被系統kill掉之後該怎麼辦
下面的範例程式碼來說明。
public class ChachaService extends Service  {
    private Context mContext;
    private ContentResolver mResolver;
    private SharedPreferences mPref;
    private String savedParameters;
    public void onCreate() {
    // TODO Auto-generated method stub
        super.onCreate();
        mContext = getApplicationContext();  //系統資料是安全的. 
        mResolver = getContentResolver();  //系統資料是安全的. 所以在這裡也可以存取database.
        mPref = getSharedPreferences("Prefs", 0);  //安全. 建議在onDestroy裡面實作儲存parameter的動作,並在這裡回復。
        savedParameters = mPref.getString("savedparameter", "aaaa");

        IntentFilter filter = new IntentFilter();
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        registerReceiver(mReceiver, filter);    //broadcast receiver 也安全。
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        super.onStartCommand(intent, flags, startId);
        //注意! 被系統low memory killed 並重啓的時候, intent為null!
        //! boolean bool = intent.getBooleanExtra("xxxx", false);
        return START_STICKY;  //回傳此值才能讓系統幫你在資源允許的時候自動回復service。
    }

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
       ...
    }

    public void onDestroy() {
    // TODO Auto-generated method stub
        super.onDestroy();
        if (mReceiver != null)
            unregisterReceiver(mReceiver);
        Editor editor = mPref.edit();
        editor.putString("savedparameter", savedParameters);
        editor.commit();
    }

}

2012年12月24日月曜日

[安藤]android 3G跟wifi ip address的取得(2014/11/17 改寫)


之前一直以為只要列舉NetworkInterface即可,結果在HTㄈ的手機上面踢到鐵板。 該死的HTㄈ的部分機種在3G數據關閉的時候還是會給我3G的ip,又讓我更討厭了HTㄈ一點。
查了網路才知道還有另一個獲得wifi的ip位址的方法,在這邊紀錄一下。

2014/11/17 追記: 因為有部分的手機會自己偷偷加一個ipv4地址給usb用(如小米),
必須找出一個可以確認ip真的是可用ip的方法。
經過觀察,3g的ip也會有ipv6的位址,所以可以使用此法判斷。
本來想使用network interface name判斷,但是發現emulator的interfce name不是標準的「rmnet_usbX」,
無法使用此法。

try {
        //added by chacha 20121217 for wifi address should use this method to get.
            WifiManager wifiMan = (WifiManager) (AppFocused.getCurrentContext().getSystemService(Context.WIFI_SERVICE));
            WifiInfo wifiInf = wifiMan.getConnectionInfo();
            long ip = wifiInf.getIpAddress();
            if( ip != 0 )
                   return String.format( "%d.%d.%d.%d",
                          (ip & 0xff),
                          (ip >> 8 & 0xff),
                          (ip >> 16 & 0xff),
                          (ip >> 24 & 0xff));
            //added by chacha 20121217 for wifi address should use this method to get.
            
            for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();

                List addrs = Collections.list(intf.getInetAddresses());
                {
                String strIPV4Addr = "";
                boolean hasIPV6 = false;
                    for (InetAddress addr : addrs) 
                    {    
                        if (!addr.isLoopbackAddress()) 
                        {
                        String sAddr = addr.getHostAddress();
                        if (addr instanceof Inet4Address)
                        strIPV4Addr = sAddr;
                        else if (addr instanceof Inet6Address) 
                        hasIPV6 = true;
                        }
                    }
                    if (hasIPV6 && (strIPV4Addr.length() > 0))
                    {
                if (BuildConfig.DEBUG) Log.d("chacha", "inet addr= " + strIPV4Addr);
                return strIPV4Addr;
                    }
                }
            }
    }
    catch (Exception e)
    {
    return null;
    }
      return null; 

[安藤]android Fragment+tabhost 實作各個tab都有自己的view切換history

Fragment是android 3.0新加入的一個view界面。
出現了這個界面之後,總算可以在一個activity上面做任意的view切換。
原本是用來針對較大畫面的tablet機種使用,不過早期的tabhost元件也因為一次只能卡一個view,造成要在每個tab上面做view切換的時候需要實做一些旁枝末節的東西。

有了fragment之後,這些問題終於得到解決。
在這裡要介紹如何使用fragment達到使用tabhost,並在每個單獨tab裡面擁有activity的切換效果。

首先第一步,你需要使用api level 13+的package。若是還在使用舊的api包的話請更新。
之後在ADT的Menu Bar上面的project-->properties裡面更換Target。

接著建立一個fragment_layout.xml放在res/layout下面。(因為下面會介紹的fragment切換必須用到。)
<framelayout android:id="@+id/fl_fc" android:layout_height="fill_parent" android:layout_width="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">


</framelayout>

再繼承一個FragmentActivity。要給Tabhost使用的。

public class MyFragmentActivity extends FragmentActivity {

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  if (savedInstanceState == null)  //可以判斷是不是第一次產生
  {
    setContentView(R.layout.fragmentlayout);
    FrameLayout frameLayout  = (FrameLayout)findViewById(R.id.fl_fc);
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.fl_fc, new MyFragment());
        ft.commit();
    }

  }
}


再來一個基本的Fragment:
(之前的設計後來會出現無法使用popBackStack跳轉到指定的Fragment的問題,
所以把frame tracsaction 獨立出來為一個function, 更新為可以泛用的設計。)
public class MyFragment extends Fragment implements View.OnClickListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {  //補充:據說savedInstanceState這東西永遠不會出現... 另外onCreateView會一直被call,就算是從上一層回來,你並沒有去destroy它...   
        super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate( R.layout.myfragmentlayout, container, false);    //attachToRoot 必須為 false!
        RelativeLayout rl = (RelativeLayout)view.findViewById(R.id.rl);  //這只是demo該怎麼取layout上面的view object
        view.setFocusableInTouchMode(true);  //這行是為了要讓back鍵可以作用在fragment上面而加的。
        view.requestFocus();   //這行是為了要讓back鍵可以作用在fragment上面而加的。        
        return view;
    }

    //按鈕換頁的動作。
    public void onClick(View arg0) {
        Bundle args = new Bundle();
        args.putInt("data", 0);

        //下面的這個method, 使用在FragmentActivity class上面的時候,用「getSupportFragmentManager」,使用在Fragment class上面,用「getFragmentManager()」
        FragmentTran(MyFragment.class, getSupportFragmentManager(), null);

    }
}
以下是更換Fragment的動作。當作Static Method用。
public static void FragmentTran(Class fragment, FragmentManager fm, Bundle bundle)
{
        FragmentTransaction ft = fm.beginTransaction();
        try {
            Fragment newFragment = (Fragment) fragment.newInstance();
            if (bundle != null)
                newFragment.setArguments(bundle);
            ft.replace(R.id.fl_fragmentcontainer, newFragment, fragment.getName());
            ft.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);
            ft.addToBackStack(fragment.getName());  //此為讓popBackStack可以回到指定fragment的關鍵。
            ft.commit();
} catch (InstantiationException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
} catch (IllegalAccessException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

}
可以用這個架構如法炮製。然後tabhost裡面setcontent的動作:
tabHost.addTab(tabHost.newTabSpec("0")
.setIndicator(xxxView).setContent(new Intent(this, MyFragmentActivity.class)));

下面是一些註解:

fragment_layout.xml只是一個簡單的layout,當做容納fragment的container view。
可不可以只用程式產生不用xml指定? 解決ft.replace的第一個參數要帶resource的問題的話應該就可以,自己試看看吧?


FragmentTransaction是一個重要的動作。相當於intent的作用。能夠切換不同的fragment。使用的流程: new, 設定fragment的動作,commit()。 commit一定要在最後做。

newFragment.setArguments(args);  相當於intent的putExtra.
在你的下一個Fragment裡面使用getArguments();來取得傳來的Bundle。

ft.setTransition可以指定fragment切換的時候的換頁淡入淡出效果。

ft.addToBackStack(fragment.getName()); 可以將這次的整個切換流程記錄在stack裡面,方便按下back按鈕的時候恢復成之前的狀態。
手動pop的方法:

    getActivity().getSupportFragmentManager().popBackStack();


要操作context的時候用 getActivity() 來替代。

view.setFocusableInTouchMode(true);
view.requestFocus();
這兩個動作是為了保證這個fragment一定會在focus狀態,不會出現按了back鍵結果整個
FragmentActivity被關掉。(為了找這問題花了點時間...)

inflate的時候使用View.inflate(getActivity)的方法會出問題。有興趣的話也可以試看看。






簡述transaction的各個動作:






add(int containerid, Fragment, String tag)
新增 fragment 到container(通常是一個Layout)上面. 可重複加,顯示階層是後面疊前面。
remove(Fragment)
移除 fragment. 不須指定在哪個container. 此動作會消滅所指定的fragment.
replace(mContainerId, Fragment)
移除contriner上面舊的 fragment, 並更換為新的. 舊的fragment只會被pause不會被消滅.
hide()
隱藏fragment。 跟設定visibility為invisible的動作類似。
show()
隱藏fragment。 跟設定visibility為visible的動作類似。
detach() (API 13)
attach() (API 13)
        這兩個還沒研究到。不解釋。





參考連結:
    http://marakana.com/s/post/1250/android_fragments_tutorial
    針對fragment的系統面的解釋比官方文件還要簡單易懂。
    
    http://givemepass.blogspot.tw/2012/07/tabactivity.html
    中文的fragment+tab解說。一開始先採用了文中的方法,後來才發現backstack
    只能有一組,回頭來改用此文的設計方式,達到在每個activity裡面都可以有不同的
    fragment stack紀錄。

2012年12月17日月曜日

[日本旅遊]2012/08/18 十一日目/西川口-橫川-輕井澤-小諸-別所溫泉(1)

18日的旅遊行程:西川口→橫川→輕井澤→小諸→別所溫泉。

今年剛好是おねがいティーチャー十週年紀念。在木崎湖有17歲教主的talk show。
還有致敬作品「あの夏で待ってる」把前兩作的景點又再挖了一次,加上小諸,
所以輕井澤/小諸/木崎湖成為必選。考量到隔天要跑木崎湖,住宿地點不能離太遠。
本來想住上田,可是,心裡一直在os:想住溫泉旅館!!!ok,來查看看...

在查資料的時候又不小心查到別所溫泉有低價溫泉宿可以住。
加上鐵道娘場景在這裡,信越+別所溫泉的區間票也可以得到最有效的利用,於是就決定住在別所溫泉,行程確定。


早上5點的西川口。想要省錢就只好早起。估算一下時間,鈍行到輕井澤大約要3hr,費用剛好是2300日幣,青春18一天的費用。順道一提,新幹線上野到佐久平要6k日幣。


忘記把買早餐的時間算進去,結果有點趕。中間沒餘裕拍照。到浦和之後總算有點空檔。

在這邊轉搭5點32分的高崎線到高崎。



接下來的90分鐘要在這台車上度過。



今日早餐。米哭一色。本來是連肉包都有買的...

ミクまんに頼みたら、にくまんになりました...
發音很像,大概是我發音不標準... orz
不過不會發生米哭被吃到一半的恐怖景象所以也還ok啦。話說我拿出來正要吃的時候,旁邊有位仁兄很用力的跺了腳之後走開。回來查了一下才知道橫長席的列車不適合吃東西。唉唉...
只好等到快到高崎,身旁都沒人之後再開始吃。

到了高崎,準備轉搭信越本線。在二號月台。在第四月台,有三分鐘的時間可以換車,還不至於太趕。

信越本線的車輛。因為沒看過只好再確認一下目的地的牌子。

看來沒錯了。上車。

信越本線的安中站。廣告打很兇的新幹線避暑別墅區---安中榛名就在這個區域。

磯部站。候車室還蠻有feel的。

經過30分左右就到了終點站---橫川。在新幹線還沒開通之前,要到輕井澤得通過有名的碓冰峠(對頭d有接觸的人一定很熟悉)。這裡有著當時全日本最陡的66.7/1000坡度軌道。

三條線(原本有四條線)述說著之前的繁榮(等車?)景象。雖然後來就因為技術進步只需要停3分鐘了...

看到這座山就知道真的很陡。技巧不好,拍不出該有的遠近感跟壓迫感。

終點站才能看得到的景色---鐵軌跟電線的終點。

列車在這裡得把餐車切離,並且在客車前後端加上高拉力的車頭,還有輔助軌才推得動。
這個就是輔助軌的示意圖。

橫川站內的狀況。

時刻表。拍下來以防萬一。

清澈的流水。有魚在裡面游。

留下來的一段象徵性鐵道。後期的橫輕段其實是雙軌的。前方是碓氷峠鉄道文化むら
日本的地區鐵道有時會看到上下行的鐵軌不在同一條線上。最有名的是土合駅。上下行的月台距離462段階梯。

在這邊更換車頭是件勞師動眾又花時間的事情。等待的時間沒事做,剛好可以吃飯,成了很適合賣便當的地方。おぎのや原本是只賣飯糰的店,在開發新商品的時候,一直在想要怎樣才能做出好吃的便當。突然想到陶鍋有著保溫,不沾的優點(對顧客來說還有多一個好用的容器的優點),於是新產品改用陶瓷鍋當容器,打破一般便當的方形盒的常識。加上連續劇置入行銷之後大賣。就這樣一直到現在還是用一樣的容器。

早上八點前,只有公路側的大型賣店有開。(車站側的要到8點半,來不及坐車)




 帶領這家店成為名店的設計---陶鍋。


便當的sample。

買完之後趕回候車亭。說遠不遠,也要走個8~10分鐘左右。回程才發現停車場有條小路可以下來,走公路側就多繞了點遠路。

往輕井澤的公車牌。沒搭到0830的話就是1000... 行程會大幅Delay。

距離公車開車還有一段時間,就到鐵道文化村的門前拍了幾張。這張是重要的參考資料。下次有打算排碓冰鐵道文化村一日遊。遊步道4hr+園內1HR,然後衝到土合!(累死你...)


測試一下3倍變焦。

大約8:15左右公車就已經在這邊準備了。

本次遊記裡面唯一的一段巴士!本來想搭一輪木崎湖的教主廣播巡迴巴士,無奈時間不夠,加上上車地點無法配合(其實是因為太晚決定,結果教主event之前的那班車座位賣光了...)

從巴士內往鐵道村一拍。鏡頭跟玻璃有點角度,導致有點失焦。100KM的時速以下用1/1000的快門還夠用。

大約30分鐘左右到達輕井澤。路邊真的都是別墅... 這天剛好有熱氣球活動的樣子。高級娛樂啊...

到了。傳說中的有錢人避暑勝地!還真的風吹來是涼的,大太陽下也不覺得熱。
公車在這裡下車。
以前的輕井澤站的月台。現在已經廢掉了。

聖地巡禮廣告。之前是summer wars的上田。現在加上あの夏的小諸。輕井澤可以往前拉到おねがい☆ツインズ。(還沒看過...)
這張海報之後在小諸有賣。A1 size!

第一個目的地到了,總算是能夠安定下來。剛好候車室有板凳,坐下來吃之前買的便當。
其實這裡也有賣www
在橫川花時間買就顯得有點多餘。

去掉固定繩之後的樣子。陶製容器!

內容物。飯也有用淡淡的醬油調味,吃起來不會覺得單調。而且是很健康新鮮的味道。
只要900日幣,很讚。

輕井澤站的另一側。一整個渡假村的感覺。

接著就是往取材地點---輕井澤商店街移動。之前查過地圖,從車站走過去估計要30分左右,有點遠。還好這邊要租腳踏車並不難。站在車站正門觀察,看到了這家兩小時800,寄行李不用錢的。本來還想找看看有沒有更便宜的,想說反正投幣行李箱也要300,算一下2小時只要500,
還ok啦。就租了下去。

出發。然後在第一個路口馬上就被打槍... orz
這邊一天只要500隻羊!看過這篇的人就不會被貴到了。希望大家以後不要被貴到...

被貴到的部份就當做寄物費吧。總之出發。

看到這個路口,已經不遠了。從右前方繼續前進。

看到這棟建築物,你就到了。這一天剛好是週六,人真的很多,而且大塞車。觀光景點
不管是台灣還是日本都一樣...

有地圖總之先拍下來以免迷路。看到左下角的洗手間的MARK了嗎? 這裡跟歐洲一樣,
很多地方的洗手間都要收錢。所以先找好免錢的洗手間也是重要事項。

不過我沒去找www。 先探查地形要緊。你要是移動到這個地方,代表已經過頭了。
商店街沒有很長。

大致了解範圍之後,開始找停車的地方。 是的。腳踏車也要找停車位...
這裡人很多,店門前都是禁止駐輪的。在我還不知道腳踏車可以停在哪之前,
只好邊騎邊拍。總之先來第一張。Church street對面的建築物。基本上我只留了2hr,所以有點趕。角度不對就別挑了。

church street的樓梯。這時的擺設已經跟取材當時的差蠻多。

接下來因為要進到church street拍攝,非得找停車場不可了。想說先繞到後面看看。
結果在這裡發現了停車點!

接著就進商店街裡面拍。在這時剛好遇到另一組巡禮的同好。剛好可以跟著拍。XD
所以這邊的照片就多了很多。先來張第三話的重要場景的全圖。






草長得真高www










找這張圖的時候花了不少功夫。因為Church street的字已經沒了,比對難度大增...



被推倒的重要場景(爆)。這地點因為是民家所以不便公開。

Church Street這附近拍完之後,看到時間只剩下30分鐘有點緊,於是啟程回車站。
這次有注意到賣冰的地方了。原來離路口不遠。


也順便買了一隻來試看看。真的很好吃。味道夠,而且很清爽的感覺。




回到車站歸還腳踏車,準備往小諸前進。

因為沒規劃好(沒想到腳踏車租金那麼高),多出了約20分的空檔。於是到站前的土產店看看...買了這東東。意外的還蠻貴的... 350一罐的樣子。 蠻好喝。有點像台啤18天。

準備離開這裡。漏了很多地點的照片,也許之後還會再來一趟吧?


再拍拍附近的樣子。橋上的視野真的不錯。

塞車塞車塞車...

停滿車的停車場。難怪人多到爆... 暑假期間啊。

剛好有一扇窗開著,可以從軌道上方拍。


配合今天行程的一張區間無限票。其實在排這行程之前沒想到有這東東...咕狗大神教我的。
序號...應該不會是發行以來只有520個人用過吧!?(汗)

看到這個告示,覺得自己運氣真的很好。要是早一天來的話,行程可能就要泡湯了...

紀念車票的廣告。被聖地原力影響,也買了一份...

這邊也擺了一台EF632的紀念車。(後來才知道原來這裡也有鐵道紀念館。)

日本電動車頭的始祖---10000型。鐵道博物館看不到的喔。


しなの鐵道運輸路線圖。小海線乙女站的停靠班次太少,打算放棄巡禮。

然後我竟然忘記要拍月台了... 間接告白的重要場景啊!orz

往小諸的列車進站。一人列車的上下車的方法跟都會區的一般列車不太一樣,不過拿無限制車票的話就不用擔心。下車的時候給車長看一下即可。

一般的流程可以歸納為從無人站上車時,看車站是不是有起站證明出票機,沒有的話就在上車時拿起站證明票(有的列車會規定只有固定的上車入口),下車的時候也是無人站的話就投到車上的機器裡面付錢,有人站的話是出站時付。

輕井澤的部份就到這裡。下一篇是小諸/乙女兩個點。一樣也是場景漏得亂七八糟的巡禮... XD