2014年3月31日 星期一

設計模式的解析與活用 (一) 物件導向範型

從例子開始說明吧!!

講師與學生,老師的責任就是讓學生知道下堂課要去哪上課
所以老師應該會
  1. 獲取聽課名單
  2. 針對每個人
  3. 找到每個人要去聽的課
  4. 找到聽課的地點
  5. 找到前往路徑
  6. 告訴他們
但是實際上真的是這樣嗎?

實際上應該是老師貼一張課表給學生自己去看吧
其實這個行為就是責任轉移,讓學生去負責自己的行為

這兩種作法的差別在哪?

第一種方法講師必須關注許多細節,因為所有的事情都由講師負責
第二種方法講師只負責告知,接著學生負責前往所屬課堂

第二種方法的好處在於

假設今天增加了助教研究生,助教需要在下堂課前收集本堂課的學生對課程的評價
對講師來說依然只需要告訴學生下堂課的位置,學生各自會負責該做的責任,研究生會蒐集評價並前往下堂課,普通學生會前往下堂課,翹課的學生會自行翹課….etc

各司其職(負責自己的責任)

第二種方法有以下三個方面不同

  1. 人們對自己的行為負責
  2. 講師將不同類型的人(普通學生&研究生),一視同仁,把他們都視為學生,並且告知他們必須前往下一堂課
  3. 講師不需要知道學生如何前往下一間教室
用術語來說明的話就是

概念-軟體要負責甚麼
規約-怎麼使用軟體
實作-軟體怎麼履行責任

講師要負責甚麼 ClassRoom  getnextClassRoom()  取得每個學生的課堂
學生必須做甚麼 gotoNextClassRoom(ClassRoom room) 
研究生必須收集資料並且前往下堂課
普通學生必須前往下堂課

對講師來說他只需要負責告知的動作
普通學生與研究生會負責自己該負責的責任


封裝的概念

講師不知道哪些是一般學生,哪些是研究生,對講師隱藏了學生的類別(也就式封裝了學生)
雖然學生都是前往下堂課堂,但是行為卻不同

封裝的好處
  • 使用者不需在操心實作的部分,使用者只需要知道想要做甚麼,剩下的交由被呼叫者去處理
  • 可以在不考慮呼叫者的情況下實作(testable)
  • 其他物件對該物件內部是未知的,例如講師呼叫gotonextClassRoom,但是講師並不知道普通學生、研究生實際做了哪些事情

其實這個例子用到了LSP,以及DIP

普通的學生和研究生都是學生,都擁有學生的行為,例如前往下堂課,姓名,學號之類的
LSP-繼承自學生的物件必定擁有學生的行為

講師告訴學生必須前往下堂課,但是他並不知道是告訴研究生還是普通學生
DIP-細節依賴於抽象,講師只呼叫gotoNextClassRoom,學生會自己依照他們的類別,做他們該做的事情,物件只在概念上耦合,在實踐並不耦合

最後,要稍為推薦一下設計模式的解析與活用這本書,雖然目前也只看了一個章節,不過真覺得獲益良多阿!







2014年3月29日 星期六

Git (九) a successful git branching model

在使用git的時候由於開branch非常方便,大部分的人都會非常隨性的開branch,並沒有一定的方式,網路上有人整理出一套流程叫做a successful git branching model,個人也覺得非常不錯所以就在這邊做介紹

首先先介紹一般的開發流程通常會有兩個主要的branch , develop 和 master,

develop:平常開發的branch,任何的功能或bug fix都在該branch上開發。

master:永遠處在production ready,master上的每個點都是一個版本,always可release,是master branch的特色,這麼做的好處是,假設是自動化的開發,通常會有一個server在build並且test,這個server就可以在master上面不斷的build and test。
除了上述提到的兩個主要的branch之外,還有三個支援型的branch,分別是feature branch, release branch, 以及hotfix branch, 

feature branch:開發新功能,從develop分支出來,完成後merge回develop 
release branch:準備release的版本,只修bug,從develop分支出來,完成後merge回 master and develop
hotfix:等不及release必須馬上修趕上線的情況,會從master分支出來,完成後merge回master 和 develop

用開發的流程來說明的話就是.....

假設我們在開發一支app,然後希望下個版本會有登入/登出的功能,撥放影片功能以及產品介紹頁面,

這時候開發人員就會從develop開始開發,然後分別創建三個feature branch
feature/login、feature/playvideo、feature/productIntro
三個branch,當這些功能差不多開發完成時,就會將這些feature branch merge回develop branch
接著就會準備進測試階段,接著就會將develop branch 切換到 release branch,

在release branch上就只會修bug,不再增加新的功能,當bug修正到一定的程度已經可以release出去了就會切到master branch並且加上tag 1.0,如果沒有意外一個周期的開發流程就已經結束,

但是如果在切到master branch之後,又發現了緊急的bug (例如app crash)這時候並不會直接在master上面做修正,而是切到hotfix這個branch,並且在這個branch將bug fix 並且驗證完成後再merge回master  



在使用這套理論的時候我有個習慣,所有的feature branch在merge回develop之前都是使用 rebase
這可以讓我們git的線型看起來比較乾淨,有興趣了解rebase的人可以去Git 常用指令 (二) Rebase vs Merge看看,在這裡要再一次強調千萬不要rebase已經在push到server端的branch,由於rebase是會改變歷史紀錄的,這會造成其他已經pull該branch的人遇到一些問題。

還有一個值得注意的地方是,當使用rebase將自己的feature測試完並且確認沒有問題之後,想要將feature merge回develop時,記得要使用 merge --no-ff (no fast-ward的意思)
這會讓我們merge回develop時會出現合併的節點而不是單純的一條線



有興趣的人可以去看看我最近做的ppt,如果有疑問也歡迎大家一起討論。

最後附上ppt的網址 http://www.slideshare.net/ssuser324e5a/git-32879866

2014年3月26日 星期三

Android 開發 (三十六) GreenDao DB library

由於之前在android上使用sqlite的經驗不是很好,大量的table大量的insert/query method
在管理上面很麻煩,而且相關程式不是很好維護(也可能是程式沒寫好啦XD),
基於上述原因,在一次機會碰到了greenDao,剛好有時間,所以就花了點時間來研究

以下是簡單的使用說明

greendao 的相關sourceCode可以在https://github.com/greenrobot/greenDAO 下載
假設有freemarker錯誤可以再下載 http://freemarker.org/freemarkerdownload.html 最新版本
在解掉相關的build error之後

首先先看看DaoExampleGenerator
這是個方便我們建立DB相關class的project


以上是自定義的table,不需要像以前建table需要龐大的程式碼,而且寫好之後只需要build該project他就會將相關的class build到 src-gen的資料夾底下

接著在我們的project裡面只需要做簡單的設定


    private SQLiteDatabase db;
    private DaoMaster daoMaster;
    private DaoSession daoSession;
    private MyDBDao mydbDao;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
        db = helper.getWritableDatabase();
        daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
        mydbDao = daoSession.getMyDBDao();

這樣db建立就完成了,接著示範簡單的insert 和 query


       String noteText = editText.getText().toString();
       double id = Double.parseDouble(noteText);
       String noteText2 = editText2.getText().toString();
       double id2 = Double.parseDouble(noteText2);
          editText.setText("");
          editText2.setText("");
          MyDB note = new MyDB(null, id, id2, Math.pow(id, 2)+Math.pow(id2, 2));
          mydbDao.insert(note);

這樣可以很輕鬆的insert data,如果是要query的話也有提供簡單的method

    for(MyDB db :mydbDao.queryBuilder().list())
    {
     Log.d("Ted", db.getLatitude()+" "+db.getLongitude()+" "+db.getDistance());
    }
    Log.d("Ted"," compare");
    for(MyDB db :mydbDao.queryBuilder().orderDesc(Properties.Distance).list())
    {
     Log.d("Ted", db.getLatitude()+" "+db.getLongitude()+" "+db.getDistance());
    }

GreenDao是蠻方便的,就連我這個sqlite新手也都輕鬆上手,對於單純存資料,做簡單sort動作算是很足夠了。

最後附上sample Code

2014年3月24日 星期一

Android 開發 (三十五) reflection 判斷繼承關係

在使用物件導向開發時我們常常會使用A instanceof B
代表 A為B的子類,例如ArrayList instanceof List

假設今天必須實做的是class<?> type呢
我查詢了許多方法,目前找到比較適合的方式是把所有的superclass都列出來直到找到最後一個為止(通常是Object)

例如 MainActivity.class.getsuperclass

   Class<?> superClass = MainActivity.class.getSuperclass();
   while(superClass.getName().indexOf("Object")<0)
   {
   
    Log.d("Ted", superClass.getName());
    superClass = superClass.getSuperclass();
   }
這樣就可以判別是否為正確的物件,當然判斷的時候必須包含package name,
例如 com.example.myactivity 之類的

Android 開發 (三十四) Geofence

甚麼是Geofence?

當使用者進入或離開某個目標範圍時,我可以對該使用者做特定事情,例如: 在兒童手機安裝監視app,當兒童帶著手機離開某個區域時我可以發訊息通知父母。或者是在老闆手機裡面偷裝app,當老闆進到公司範圍時會跟部門所有人發通知 XD

geofence是 location service 所以要使用geofence就必須安裝google play service apk可以使用
GooglePlayServicesUtil.isGooglePlayServicesAvailable() 來判斷是否有安裝
至於該如何使用geofence
 mLocationClient.addGeofences(
                        mCurrentGeofences, pendingIntent, this);
如上的指令,其中pendingIntent為當事件發生時要做何反應,例如發broadcast,或者是開activity
如下有指令的介紹
至於mCurrentGeofences可以使用Geofence.Builder來創造

在這邊值得注意的是,我一直以為geofence一旦被註冊之後就會不斷的被觸發,直到我取消,
但是在實作之後發現,該pendingintent只會被觸發一次之後就不再觸發,也就是說,假設我今天想要做一個背景監視使用者是否有進入或離開某個範圍的app,當使用者第一次進入該範圍之後,我就再也無法監聽到使用者之後的行為,除非我再一次的註冊geofence。

所以想要達到上面的功能,就必須在每次geofence被觸發之後,重新connect locationclient,並且重新註冊geofence,但是如果不斷的註冊geofence並且觸發,這又會對電池造成極大的負擔,要如何達到平衡,這就必須經過多方討論取捨才能得到結果了。

2014年3月20日 星期四

Android 開發 (三十三) 遠端 Service AIDL 的使用

假設今天有兩個app要互相傳遞資料

例如

我有兩個書本定閱app ,這兩個app的書單必須同步,要如何做到?

可以使用AIDL(Android Interface Definition Language),這可以讓我們同時access同一個service,
所以接下來我想做的事情是
1. 建立兩支app存取的interface 以及model
2. 建立 兩支app
3. 建立 一個service


首先必須先建立interface,這邊可以使用android內建的方法,我們只需要創建好.aidl檔之後
android會幫我們建好.java檔,


可以看到在上圖的interface裡面,我有使用自己建立的Book , aidl 可以import 繼承自parceable的物件,不過繼承的物件也必須有自己的aidl檔
如下圖 book.aidl


接著我們必須建立兩支app ,然後使用bindservice將interface取回

  conn = new MyServiceConnection();
  bindService(intent, conn, Context.BIND_AUTO_CREATE);

在ServiceConnection中我們可以使用 asInterface將我們需要的interface 取回

 class MyServiceConnection implements ServiceConnection{

  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   manager = BookListManager.Stub.asInterface(service);
   
  }

  @Override
  public void onServiceDisconnected(ComponentName name) {
   manager = null;
   
  }
  
 }

在取得interface之後,我們就可以使用相關的method


在建立好兩支app之後,接著就只需將Service建立完成即可
如下圖在binder裡將interface.stub回傳回去,使mainactivity可以取得service的interface



這樣就完成了,只需要在一開始startservice之後就可以共享資料了。

在這邊必須注意一點,
就是兩個app的aidl檔必須完全一致,包含package name
如下圖

否則會出現
SecurityException: Binder invocation to an incorrect interface 這個錯誤

接著附上sampleCode

2014年3月8日 星期六

crashlytics crash分析工具

在開發專案時常常會聽到一句話
                                           
                                                   "在我那邊是好的阿"

相信上面這句話道出了很多開發人員的心聲,而我也常常身受其害,
有很多BUG在自己的機台上就是不會發生,可是每次到了使用者或老闆手上就特別容易發生,我相信他們身上一定有一些奇怪的磁場?!

回歸到正題,如果無法複製問題那就無法解決問題,但是問題總是要解決的,如果沒有合適的工具那要怎麼解決,以前只能瞎子摸象,他們說這樣會crash阿,我就手動壓力測試,測個30分鐘,沒問題只好說沒看到,希望有人可以給出個複製步驟,如果沒有就只好祈禱不要再出現,現在想想當時還真的很厲害,瞎子摸象還真的讓我摸出一隻動物出來....。

最近使用了crashlytics,他可以用來分析crash發生的頻率、版本以及發生exception的位置,
這對開發人員來說實在是太棒的消息了,試想...我release出一個版本給龐大的QA使用者們
測試使用,他們還會回報BUG在哪(如果這樣做我相信明天老闆就會叫我走人....)。


要使用crashlytics其實很簡單,只需要前往他們的網站,然後根據他們的步驟


完成安裝之後,接著只要照著他的步驟設定就可以了,如果一直卡在連結app那邊可以考慮使用firefox瀏覽器來run。


之後就可以等著廣大的QA使用者們把crash結果一個一個回報過來囉~~
下圖是一個簡單的範例,其中發生exception的method 由於我們使用proguard加密過app所以無法直接看出是哪個function name,不過我們可以透過mapping.txt對照出相對的名稱。

有了這個工具,開發人員就不再有藉口說我這邊都是好的了......
不過,工具不是萬能的,這個工具也只能告訴我們crash發生的點,multi-thread才會出現的bug,還是需要想點辦法才能複製出來。


2014年3月3日 星期一

Git(八) 還原被修改的檔案

今天要談的是如何還原被修改的檔案,檔案被修改之後有分成兩種情況

  1. 修改的檔案尚未被push上remote端
  2. 修改的檔案已經被push上remote端

修改的檔案尚未被push上Remote端




如上圖,我在local端做了一些修改做了3個commit ha, hb, hc
但是我尚未push 到remote端,這時候我發現hb這個commit 改了我不希望被更動的東西,我希望remove hb這個commit ,這時候可以使用 git rebase -i  HEAD_SHA  (其中 HEAD_SHA = origin/HEAD的 SHA)

接著會出現如上圖,這時候只需要將 hb這行刪除就可以了。
然後就會看到
hb這個commit 已經消失了。


如果只是想要移除上一個commit 例如  我commit 了ha hc  ,然後我希望將hc這個commit 移除
這時候也可以直接使用 git reset --hard HEAD^ 這樣就會將版本退回ha commit 
對reset這個command有興趣的話可以參考

Git 常用指令 (一) git reset

Git 常用指令 (五) reset 應用 merge 和 rebase之後的倒回


上面的方法很方便,但是這些方法僅限於使用在commit 尚未被push到remote端之前
假設commit 已經被push到remote端這就麻煩了,就只能只用下面的方式來修改。


修改的檔案已經被push上Remote端

如下圖,假設我commit並push了 ha 到remote端上

在我commit之後我希望reverse這個commit ,這時候可以使用
git revert  ha_SHA  (其中 ha_SHA為 ha這個commit 的SHA)
這樣就可以將 ha這個commit reverse 。


假設我今天merge 了一個branch helloo,但是在merge之後後悔了,希望將這個merge revert
如下圖

這時候我必須使用 git revert -m 1 Merge_Branch_helloo_SHA
(其中 Merge_Branch_helloo_SHA的 SHA為 merge branch helloo的SHA)

假設未來希望將revert merge的版本還原,就必須再一次revert版本(merge branch將不會work),
這樣就能將原本的版本覆原。


使用git 還原的時候要確切的知道自己在幹嘛,如果不是很確定的話,記得做備份的動作,否則一不小心可能會將寫好的檔案revert掉。