MR SHIH

必幸施

Docker系列3 - Container間的溝通

| Comments

這系列文章從建立Container,建立Volume,至此我們已經可以去建立提供不同的服務Micro Service了,而剩下的議題變是如何讓Micro Service之間彼此溝通。

不同Host間的Containers彼此溝通

如果Micro Service在不同的實體機器中,則可以透過run指令裡的-p來把Container與Host的特定給對應起來。

比如以下指令就是啟動一個Web Container並且把Host的Port 3000與Container的Port 80給串連起來

1
docker run -p 3000:80 web

那接下來就可以在Host用127.0.0.1:3000來訪問Web Container的Port 80。

同Host間的Containers彼此溝通

而當Containers都在同一個Host,比如一個是Web一個是DB,Web要如何連上DB呢?其實每個Container在run的時候預設都是走bridge模式,這裡就是docker會創建一個veth interfaces,讓該host的Containers都跑在同一個network。

只要用inspect指令就可以查到該Container的詳細資訊,而裡面的KeyIPAddress就會有IP資訊。

1
docker inspect [Container-ID]

在同個veth環境下,有了IP,Container之間就可以彼此溝通了。

FYI

https://docs.docker.com/engine/reference/run/#/network-settings

Docker系列2 - 運用Volume分離資料

| Comments

為什麼要用volume?

Docker的Container有個重要的原則是不儲存資料。為什麼呢?比如你做一個Web Service的Image,你把NPM,前端分流Nginx等軟體都安裝好了,這時候要把網頁的檔案放進去,一種做法是在Dockerfile裡面用COPY把網頁檔給複製進Image裡面,然後再Run起來。

可是這種把不斷變動的資料檔案存在Image裡的做法會帶來許多問題,我如果改一行Code我不就要重新Build或新Commit一個Image嗎?我如果有好多個網站要跑在不同的Container上,我不就要做好多個Image?如果是DB的Image,把DB File放在Image裡面,那不就隨著時間過去Image就要一直更新了?

所以在製作Image上,常見的best practice是把執行環境打包成Image,而時常會修改的檔案比如網頁檔案或資料庫檔案這些資料就另外掛載在Volume上,也就是Mount在Host機上的資料夾。這樣就可解決上面所提及的問題。

底下我們就以網頁服務常見的/var/www資料夾與資料庫常見的/var/lib/mysql為例,操作如何把這兩個存資料的地方給另外存在volume上。

製作data volume container

1
docker create -v absolute/path/to/host/dir:/var/lib/mysql --name dbdata mysql   //產生一個container, 掛載host資料夾(absolute/path/to/host/dir)到container的/var/lib/mysql

這裡的-v用法就是用來掛載volume的,去mount一個host資料夾為container的data volume。並且把這個container指名為dbdata,這種container又稱作為data volume container

這種container特別在於本身只是用來記載那個host資料夾對應mount到container的那個資料夾,所以container本身是不用啟動(run)的。

PS. 如果Host的dir沒有指定,則docker會在系統的某個陰暗角落建立一個新的資料夾。可以用docker inspect container-id指令回傳結果裡keyMounts的Dictionary裡找到那個陰暗角落。

使用data volume container

有了data volume container後,只要以後在run container時透過--volumes-from參數,就可以直接把指定的data volume container的volume給掛載進來。

1
docker run --name=mysqldb -d -p 3306:3306 --volumes-from= dbdata mysql

這時候新產生名為mysqldb的container,cd到/var/lib/mysql就會發現與host的absolute/path/to/host/dir資料夾是連動的,也就是mount再一起了。

舉個實用情境,假如資料庫版本要升級,只要在Dockerfile裡面升級資料庫版本,然後把新的container啟動時掛載dbdata,資料就還是一樣。而如果當初沒有把資料從Image分離出來,是要不原本DB的資料給匯出,再匯入新Image,非常麻煩,而不這麼做DB的紀錄就消失不見囉。

這裡的效果也就是官方介紹的volume主要用途,把資料與container分開,達到data persistence的目的。

Data volumes are designed to persist data, independent of the container’s life cycle.

同理可證的Web Service

一但建構好基本環境,比如NPM, PM2, Nginx…etc,打包成一個Image後創建Container,以後Run這個Container只要搭配以下參數,就可以在本地修改,然後在container裡面實際測試了。

1
-v absolute/path/to/host/web/dir/project:/var/www/project

比如以下指令就是啟動一個已經安裝所需運行環境的image(npm-ready),然後指定Host資料夾(/Users/shih/Desktop/lab/PSControlCenter)掛載到Container的/var/www上。

1
docker run -i -t --name center -p 9453:3001 -v /Users/shih/Desktop/lab/PSControlCenter:/var/www npm-ready

FYI

Docker系列1 - 從Image到Service

| Comments

本身工作偏開發,為何要了解Docker?

最近Micro service火紅,大型服務被切成一個一個小模組,減少彼此耦合,個別跑在各自container上,方便重複部署,且彼此互相用Rest API或socket溝通。這樣帶來的好處在於可以在開發上減少後續迭代的困難,減少維運的難度,增加系統穩定性。

但要獲得這些好處,軟體在最初開始設計架構的階段就要拆分服務,一開始設計不好之後就算有docker工具那也只是把所有服務丟到同一個Container上,沒有辦法發揮docker切割服務的優勢。

所以想成為架構師,當然不能不了解這個工具。而從程式開發上來看,能夠把CI Keep in mind,有新Commit Push到develop branch,CI Server就自動執行下面步驟

  1. 打包新的Image
  2. 產生Container
  3. 執行Service
  4. Run Test Code…

能夠協助開發者確保最終程式碼在Production環境是可以正常運行,有問題可以立即發現與處理。

Docker對比傳統VM優點在於所消耗資源很小,啟動速度很快,並且兼具傳統VM打包成Image擋可以Anywhere都快速建立起一樣的Service等優點。

這篇文章主要就以上面幾點依序從包含如何製作Image,產生Container,利用Container啟動我們要的Service,最後如何匯出部署到另一台機器等一系列記錄下來。

Dockerfile與Base Image

要導入的Service是由三個部分組成

  1. Node.js專案,用Express作為Route Framework,為業務邏輯層負責API JSON的輸出
  2. Node.js專案,用Express作為Route Framework,使用Bootstrap來包含View的後台操作介面
  3. MySQL Instance

之前做法是把Node.js專案都放在同台Server,但因爲HA需要,所以也有一台Hot standby的實體機並排在HAProxy下。這裡帶來第一個問題是當要重新build環境時靠Install Guide手動安裝很不方便,解決方法便是用Dockerfile,把Install Guide所需的環境與步驟打包成一個Image。

1&2專案相同部分是Node.js專案,用Express作為Route Framework,所以可以Build一個Base Image來當基底環境,之後只要分別把/var/www置換成對應的專案檔即可。

1
2
3
4
5
6
7
8
FROM centos:7 // 指定CentOS
MAINTAINER daan.shih@gmail.com
RUN yum install -y epel-release // -y避免互動輸入
RUN yum install -y nodejs
RUN yum install -y npm
RUN npm install pm2 -g
RUN mkdir /var/www/
WORKDIR /var/www/

有了Dockerfile後就可以Build Image了

1
docker build -f /path/to/dockerfile ./

之後會得到一個Image ID,因為是只安裝npm基底就取名為npm-ready

1
docker tag [Image ID] npm-ready

現在打docker images應該可以看到類似npm-ready latest 43c671f848fd 22 hours ago 405.6 MB的資訊

以Base Image為底,加入express專案檔

這時候我們已經有一個Image檔,包含npm與必須的tools,只缺express專案檔。所以接下來就以FROM指令,指定以npm-ready:latest為底,用COPY指令把我們需要的專案檔複製到Image中。

這裡要注意第三行的PSControlCenter/這個目錄必須與Dockerfile同一個目錄(docker build後面會加上的path我們是打./),不然會出現找不到此目錄。

1
2
3
FROM npm-ready:latest
MAINTAINER daan.shih@gmail.com
COPY ControlCenter/ /var/www/

得到新Image ID後幫它補上名字方便識別

1
docker tag [Image ID] control-center

Image to Container

至此我們已經有一個包含全部所需要檔案的Image檔control-center了。現在只需要利用Image去產生一個Container,並且執行這個Container就行了。

本來產生create與執行start是兩個指令,這裡可以用run來代表連續執行上面兩個指令。類似git裡面pull是連續執行fetchmerge

1
docker run -t -i -p 9453:3000 [Image ID] /bin/bash

這裡要注意的是-p,每個運行中的container的網路環境與host是完全分離的,所以我們要從外部去訪問container,就要用這個指令來指定port的相依關係。

這個例子裡我們在host輸入127.0.0.1:9453就會訪問到container的3000 port。

建立完container後,這時可以透過下面指令拿到Container ID

1
docker ps -a // -a會列出所有container(包含停止)

運行container後,想再得到運行狀態中的container的bash shell可執行

1
docker exec -it [container-id] bash

exec指令就是對該運行中的container下達指令。

Run Service

到這裡我們已經利用Image去產生Container,並起啟動了Container,指定了Port的映射關係,最後取得該Container的Shell。這時環境已經建立起來,我們就可利用PM2把service給run起來:

1
pm2 start ./bin/www --watch --name "www"

然後在Host瀏覽器訪問127.0.0.9453,就可以看到服務啟動了。

部署在另一台機器上

把control-center這個Image用save指令匯出成.tar檔

1
docker save control-center > ./control-center.tar

接著在另一台的Dokcer instance上用load指令,指定tar檔路徑匯入成image

1
docker load < /path/to/control-center.tar

再來用docker images來確認是否有成功匯入Imagecontrol-center

之後我們便可以把該Imagecontrol-center打包匯出到其他機器,短短的兩行幾秒內就把服務給建立起來,非常輕鬆寫意的部屬方式。

1
2
docker run -t -i -p 9453:3000 [Image ID] /bin/bash
pm2 start ./bin/www --watch --name "www"

Next

下次介紹另一個Docker的重要概念:Volume。Container與Image有個很重要原則是裡面不要存Data,也就是不要把MySQL的資料給存在Image裡,所以在資料庫相關的Container裡有關Data的部分會採用另外掛載的方式。

參考FYI

期末報告如何才能拿最高分?讓教授幫你做報告

| Comments

image

大家都有這樣的經驗:辛辛苦苦絞盡腦汁,經歷無數夜晚暴肝打word,期末滿懷信心交出報告後,換來的是又一次的失望。

不斷更新

如果說一般考試把時間與心力砸下去結果就會好,皇天不負苦心人,那期末報告真的是一件令人頭大的事,時間心力砸下去只怕教授也不領情。那有沒有什麼方法保證最高分?有!很簡單,就是你不能只是把報告做完,你要做到一半就把報告拿給教授看,甚至要拿最高分你有必要寫個一兩頁就傳給教授看。

一開始比如說:「我想要從這家公司他們幾個創辦人的背景開始介紹,你覺得好不好?」,教授可能會說:「我覺得不好,因為xxx是個無趣的人,你不需要介紹他….」,這時候你就改麻,然後再問他:「這樣好不好,那你覺得我哪裡可以再加強?」,教授如果說:「那我覺得你可以再focus在這上面…」,那就再回去加加加,增加篇幅之後再問…這樣一路反覆執行,到學期末,保證你絕對最高分!但是為什麼呢?

不是教你諂媚

注意!答案不是”最常跟教授接觸”,那比較像是諂媚,這裡絕對不是教你諂媚。這背後有一個原因為什麼會最高分,因為你這個報告等於是老師幫你做的阿,如果你真的定期跟老師update一些東西,這東西根本就是老師幫你做的阿。那讓老師幫你做這件事情是什麼意思?

剛舉的例子,老師就是那個消費者,你一直去推這個產品、一直去測試消費者,一直修改,直到消費者買單為止麻!你可以一直測試一直測試、一直修改一直修改,不要悶著頭一直寫、不要怕被指出錯誤,這個過程,不管是做報告也好,或是做事情也好,是你最該擁有得一種態度。

不要把所有的事情都堵住到一擲上面,如果你在最後一個禮拜要發表的時候才讓教授知道你的全部想法,看到你的全部東西,你仔細想想,這不是一件在賭樂透的事情嗎?你之前完全都沒有回應耶!也都完全不知道教授對你報告的想法,猜牌算牌都不想做,就在最後一刻突然翻出你的底牌,你根本就是在賭樂透。

跟老師溝通,肯定你的作品,這中間要懂得清楚跟老師表達你做了什麼、清楚理解並記下老師希望你做些什麼、配合老師時間、反覆溝通記錄、鍥而不捨的修改、抓到老師口味、持續的update…這些都很難耶,這也都是你真正的價值。這些東西比你待在家裡、不用接觸老師、照著自己意思做,那樣最簡單!

不只”完成報告”,運用這個來回溝通的方法”完成老師滿意的報告”。而出了社會以後,面對上頭交辦下來的事情,別人埋頭苦幹而你會善加利用這點完成事情,成果老闆上司都滿意,你跟別人自然就立分高下。

iOS Search應用

| Comments

iOS 8之後搭配UISearchController,只要把屬性searchResultsUpdater指定物件在updateSearchResultsForSearchController這個方法中實作filtering and updating就可以了。非常方便。

Fetch data

從Server抓取資料,這裡我們用originalDatas這個instace variable存JSON陣列資料。注意Retain Cycle即可。

1
2
3
4
5
6
7
8
9
10
11
12
// Feth data from server and reload.
__weak MemberListTableViewController *weakSelf = self;
[TNUserManager logInAccountInBackgroundWithAccount:@"andy" password:@"12344321" success:^{
    [TNUserManager getContactbookWithSuccess:^(NSDictionary *responseObject) {
        weakSelf.originalDatas = (NSArray*)responseObject;
        [weakSelf.tableView reloadData];
    } failure:^(ResultInfo *resultInfo) {
        NSLog(@"get data fail");
    }];
} failure:^(ResultInfo *resultInfo) {
    NSLog(@"login fail");
}];

Filtering and Updating

利用NSPredicate制定陣列搜尋規則,這裡是指定陣列內有包涵searchString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/** Filtering **/
// 搜尋規則
NSPredicate *preicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[c] %@",searchString];

// 搜尋
NSMutableArray *reslut = [[NSMutableArray alloc]init];
// loop each people
for (NSDictionary *each in self.originalDatas) {
    Boolean isMatch = false;
    
    // list all value of people
    NSMutableArray *values = [NSMutableArray arrayWithArray:[each allValues]];
    
    // 把property的屬性為array的提取出來
    for (id proerty in values) {
        if ([proerty isKindOfClass:[NSArray class]]) {
            if ([proerty filteredArrayUsingPredicate:preicate].count > 0) {
                // show
                isMatch = true;
            }
        }
    }
    
    // 找剩下property的屬性為string的
    if (isMatch == false) {
        if ([values filteredArrayUsingPredicate:preicate].count > 0) {
            // show
            isMatch = true;
        }
    }
    
    if (isMatch) {
        [reslut addObject:each];
    }
}

/** Updateing **/
self.showDatas = [reslut mutableCopy];
[self.tableView reloadData];

後續優化idea

這個APP是做公司內部通訊錄的,而如果陣列裡面包涵員工ID,或到職日期等數字資料,就可以在NSPredicate字串比對完之後,搭配Sorting來對數字做排序。

潮一點的搞個數據應用,可以把搜尋歷史用SQLite記錄下來,把搜尋後而且點擊這個元素加入到Sorting的條件中。這樣一來使用者越長搜尋的排序就會越前面囉。

最後也可以做成讓iOS系統搜尋Bar也可以找到APP內的資訊,不過這樣就要把通訊錄存在本地端DB,而不能只是In-Memory JSON處理了。

為何愛成吉思汗健身房

| Comments

image

成吉思汗的館長說他開館就是相信台灣應該擁有更好健身房,所以要持續的提供台灣一個高水準的健身聖地,提升台灣人健身運動的專業風氣。討厭看到市場上充斥的都是直銷與只想賺錢的商業健身房與似是而非的健身觀念。

之前去過其他健身房,別的可能就是放很養眼的美女帥男圖,可能也會放勵志標語,比如今天得努力是為了什麼。但我特別喜歡成吉思汗該怎麼做就怎麼做的實幹精神,健身就是會苦,有苦才有收穫。每個人來這裡都是在跟自己比賽,而不是只是來打發時間。我們選擇在此時此刻來到這個健身房,是要互相見證彼此在痛苦中成長,我們能互相體會別人在力竭時努力的叫喊,猙獰的臉龐,而不會阿呦,這是在幹嘛。在這裡大家都在玩真的,真得都在健身,很有熱情想法的在看待這件事!沒有再跟你害羞,沒有再跟你玩半套。在這裡許多地方標語就是no pain no gain。

在這裡密集的上教練課,與教練聊天中知道這裡教練對於定期的考試大家都十分重視,不只是玩玩只求過就好,大家都追求好成績,互相幫忙當捕手教學對方不足之處,這裡教練是真心在追求身體上的進化與突破,跟著這樣的教練才能有學不完的東西,而我相信能夠不斷自我要求的環境這就是好的環境。

成吉思汗直接把台灣制霸印在門口,器材真多超多超齊全,妥善率也都很好,深蹲架排開就是六台,而全年保證不休息,想練隨時都可以來練,就連年過年除夕都不打烊,教練師資真的也專業又敬業。

為何寫這篇?

之所以打這一篇是因為跟一位熱愛運動但在考慮上健身房的朋友聊天,偶然聊到為什麼這麼多健身房我選擇了在三重沒有離我家很近的成吉思汗,我突然發現儘管我十分認同喜歡這裡,但我卻完全沒有辦法引起別人的共鳴。後來思考,這應該跟Simon Sinek那個演講想表達的是一樣。

我都是從WHAT跟HOW來開始跟別人講解,提陳吉思汗優點從有大量專業的器材,很壯的教練,口碑也都不錯的點切入,但我從WHAT跟HOW切入,結果就是聽的人不能感受成吉思汗特別得地方。我試著從WHY這個角度切入,也希望下次我再推坑其他的人的時候能更引起別人共鳴,推坑成功。

超級智能時代-人工智慧的不可擋與半世紀影響

| Comments

image

談一本重要的書,超級智能時代,這本書裡談的議題對每一位讀者都絕對切身相關。人類剛經過一波的資訊革命,最近幾年資訊技術的成熟發展,包含移動網路與終端設備如手機的成熟,帶來資訊大爆炸。資訊大爆炸後有了足夠的數據量,收集起來嘗試找出某些規則,並運用這些規則來使人類生活更方便,也賺取了大把財富。

但從工業時代以來的每一波革命都是先造福一小部分群眾,過了許多年後全人類才能享有革命帶來的好處。這一波大數據與人工智慧的革命帶來的影響與衝擊非常之廣,人工智慧就是想解決以往人類才能解決的問題,會取代許多既有的勞動力,再疊加上一波資訊革命帶來的影響,根據歷史可以想見需要長達半世紀來消化過剩勞動力帶來的負面衝擊。

從因果法到逆推法

以往人類學習的方式是習慣先假設某種道理,之後再去累積數據去證明出這種道理;我們習慣先假設因,才去驗證結果。上幾個世紀人類都習慣這套思考模式,稱之為工業時代思考模式。而當時間來到現在,容易尋找的真理幾乎都被發現完了,我們要面對的是不確定性越來越高的問題,往往我們連問題該怎麼定義都沒有頭緒,所以藉由累積數據來說故事,從資料裡發現道理,便是為了回答現代越發複雜,且不確定性極高的智慧型問題的新思考模式。

現代人工智慧

其實大數據不是用來解決人工智慧的唯一方法。最早其實科學家是想模仿人類思考模式來製造人工智慧,但發現難以解決不確性高的問題。而隨著硬體如儲存技術得成熟,連結技術如移動網路的成熟,處理能力如資料中心的成熟,這三者技術同時準備好了,使得人類發現與其讓機器造著人類的想法走,不如餵給機器一堆資料,讓它從中自己試著回答問題,結果比之前的方法就結果來說都更好。

逆推方方法論變成資訊時代的方法論。

順著這個思考模式可以發現。掌握數據,更嚴格來講是大數據的人在未來才有可能回答現代社會中越發複雜的問題。大數據顧命思議就是大量的資料,而大量的數據加上多維度,便能從中去試著找出規律,總結成智慧。

所以從人工智慧方向往大數據的逆推方法論走後,『人類在人工智慧領域的成就,其實就是把各種智慧問題轉化為消除不確定性的問題,然後再找到能消除相應不確定性的資訊』。

下圍棋人類依靠經驗與眾多棋譜來學習如何取勝,而只要能把棋譜轉換成電腦可懂的規則,電腦就可以大量的去讀取歷史棋譜,能比任何人類一輩子讀的都還要多,最後轉換成數學模式,採用依照大量數據訓練加上統計知識所做出來的模型來回答問題。雖然棋的變化是無窮的,但機器總能找出勝率較高的步法。

AlphaGo在比賽中走出許多令人類專家跌破眼鏡的走法,但最後又能贏。這裡重點在於機器不必像人一樣思考,只要能夠解決人的問題就好了。在大數據中得出的智慧是數據加上統計的及果,是有可能出乎人意料外的,而且能做的更好。能不照常規行事其實就是超級智慧的表現。

而AlphaGo知道自己在下棋嗎?當然不知道。只不過把智慧問題轉換成數據問題。用統計方法搭配大數據解決智慧問題後機器從此無敵。

所以對於行事有特定SOP的技能,也就是非創造性工作的從業人員,大數據加上人工智慧是大有取代的勢頭。比如基金操盤手、醫師、會計師、司機、護士、農夫等。

智能革命的影響

在未來掌握大數據與運算能力的少數巨頭,目前來看如Google, FB, Apple, Amazon, Netflex和大陸的bat,這些公司如果和公權力結合,那就是全面監控了。CIA要來台灣抓人其實不用跟台灣警察打交道,透過你使用的手機說不定還比台灣警察更知道你的所在地。免費的服務往往是用自由換來的。你提供時間與數據,巨頭提供免費服務。在未來隱私的保護是重要議題。

技術帶來的各命常常是憂喜參半,喜的是改善人類生活,讓一些處在浪尖的人發揮更大的作用。但因為技術進步而被取代的人力,往往沒有辦法在學習新技術,所以只好『養著』,耗上兩代就解決問題了。

這次的超級智能最重要的影響是人類會發現自己能做得比機器好的事情不多了,很多人會在技術進步的途中發現自己被拋下了,新技術的發展不是每個人的機會都會變多,許多人反而是機會變少,如全自動化工廠之於生產線工人,計程車司機之於自動駕駛技術。這些大量勞動力該如何解決?書中其實沒有提出解決辦法,只能接受接在未來的幾10年會有越來越多事人類贏不了機器人。而之後所做的決定也都要依照這個大前提去行動。

對於我個人

看完這本書心中是興奮與恐懼,興奮的是對未來人類生活充滿正面積極的想像,恐懼的是害怕自己是對這未來至少50年影響人類最深遠的領域一無所知,最終被拋下,在自己的工作生涯毫不自豪。幸好我是相關科系相關產業,也還年輕肯學有興趣,往後推10年的努力都要與人工智能領域有關,投入在相關領域是我對自己未來的規劃。

以下是Youtube連結,台大教授林軒田老師的機器學習基石 FYI。

AirPods個人使用評測:『能夠預知下一步的真智慧型耳機』

| Comments

image

說到無線藍芽耳機我也是重度愛好者,早在前年就花了5位數大洋買了Sony的藍芽耳罩耳機MDR-1RBTMK2,那時候最麻煩的地方在於配對的不穩定和麻煩,都要先按鈕換醒耳機等待手機配對,有時找不到還要自己去連線。還有麥克風根本是雞肋,完全不能拿來講電話收音非常差,一有電話打來常常是手忙腳亂拔耳機選擇本機的Mic等等…好在上述的問題在AirPods都被完美的解決了。

方便到一切成自然的拔掉單邊耳機

常常戴耳機遇到一個情況是別人突然來找你講話,這時候拿掉一邊耳機另一邊都還播著歌,有時還是要手動把音樂關掉,而AirPods你只要拿掉其中一邊耳機,手機立即就會停止播放歌曲,真的是很實用的功能。

再來重點當你講完話,把耳機再放回耳朵時,音樂又會聰明的自動開始播放了!一切真的就是方便到成自然。想像你帶著耳機在電影,突然有人打擾!你帥氣的拿下單邊耳機電影立刻自動暫停,講完之後再戴回去電影立刻繼續播放!Magic~

你的下一個動作都預先被做好了,對我來說這就是智慧型產品,就叫方便!

兩邊耳機都拿掉

以前我用過的藍牙耳機當你兩邊都拿掉之後,你可能就是回到家不需要他們了,這時還要去手機裡面把輸出模式切換為原本的喇叭,多此一舉。而AirPods會自動偵測到你把兩邊耳機都拿掉了,iOS Device會自動把輸出切換回本機的模式。日常使用起來也是非常方便。

麥克風

真的很神奇,明明麥克風不在你嘴邊是在耳朵旁,可是你講出來的話還是很清楚的被收音了。使用AirPods後講了很多電話沒有人跟我反應不清楚。最後如果特別有問都會說我這邊收音很清楚很清晰。不管在捷運上或大馬路或安靜辦公室裡都OK的。

連線穩定度

搭配iOS Device很穩,就是穩。我想這是基本的啦。

音質

高音感受與有線一樣,低音感受比有線好蠻多的,至少我有感啦。音質很主觀,很在意的還是親耳聽聽吧。如果可以接受原廠有線的,那AirPods這個應該沒問題。

電力與充電

AirPods本身支援快充,你聽到剩4%快沒電了,放回AirPods case(就上面那個方形小盒子),約不到15分鐘就充超過一半了。充滿一次可以聽大概5個小時。實際使用上跟官方給的數據都很相近。自己用起來完全不擔心缺電。

外觀

這個就不用太在意吧,音樂是自己在聽的,而且也不是什麼新設計,就是之前有線的長相呀,只是沒有線。真的很在意的就等多一點人戴再買吧,哈哈。按照官網購買要排到6個禮拜後的情況,我想以後路上會很常看到的。

會不會掉?醒著的時候都不會掉

什麼意思勒?我帶著AirPods慢步,重訓(有時需要躺著),出門坐捷運加上走一大段路,尖峰時刻搭捷運有時難免跟路人碰撞,以上情況都沒有掉過!有線的反而常常搭捷運太擠線勾到還會被扯掉。

唯獨有一次搭客運睡著頭往一邊肩膀倒,然後客運晃阿晃抖阿抖的,那邊的AirPods就掉了…嚇出一身冷汗,還好那天穿帽T醒來之後AirPods還在我肩膀上。所以,如果你快睡著又剛好身體會晃來晃去就要注意一點了。

快速跨多裝置使用超方便

如果你同時有Macbook, iPhone, iPad,而且都使用同一個有線耳機,常常插拔真的頗麻煩。AidPods只要Device都使用同一個Apple ID,每個裝置都不用再額外配對就可以很快速的連接上你的AirPods。

我常常很賭爛Macbook上的耳機孔,真的很難拔,常常只是要上個廁所我就要拔下來再插上手機,回到座位上又要Repeat這個動作。有了AirPods之後就超方便的,只要手機帶著就自動會有AirPods選項讓你選,點一下就切換到手機上了。

缺點

使用下來真的沒有感受到什麼缺點,對比有線耳機只有更方便更智慧的感受。真要說的話就是AirPods上面沒有按鈕可以按,也就是說沒有傳統有線上的上下首與大小聲按鈕。AirPods現在只能感應點兩下這個動作,點兩下來叫出Siri或是點兩下暫停/播放。

雖然說是缺點但我的習慣是出門手上都會拿著耳機,基本上手機不離身,所以要上下一首或調音量,把畫面喚醒就可以直接操作了,實際使用上其實也沒有感覺到不便。

總結

毆對了!還有價錢。5000多大洋就是考驗每個人用錢價值觀的時刻了。上面舉了那些優點還有缺點,我只能說差不多的錢就功能上還有方便性智慧性等,目前市面上我認為還沒有競爭對手。

對我來說,耳機是我每天必用到的東西,如果每天都能少那麼一點煩惱,比如花時間解開那一坨線, 插拔的動作, 有時會扯到, 原廠有線用久其實真的很容易接觸不良還是要買新的等等,加上上面提的優點,OK 我買單。

我不算盲目的果粉,近期Apple的產品包含iPhone 7跟新Macbook如果有人詢問我意見我都會叫他們再等等,但這個AirPods真的很推,Apple近期難得好作品,預算夠我都會推薦買Der~。

從Youtube使用Tabbar討論逐漸被棄用的側邊欄

| Comments

側邊欄逐漸被棄用的原因

iOS版YouTube把Tabbar加上去了。前陣子流行的各種想取代Tabbar導航方式的方案又改回來了。

image

Tabbar是固定出現在下面的(除了播放影片時)。而看了些文章,最主要還是用戶真的太太太太懶,多一丁點步驟都懶得按,所以就乾脆固定在那裡。這樣努力做的各種功能的使用率比較高!

不過Uber是個有趣的例子!Uber用漢堡是因為其他功能的點擊率高不是goal,所以可以用Hamburger menu。

如何決定要用哪種設計?A/B Testing

Youtube跟Spotify決定從原本側邊欄轉移到Tabbar都是因為測過A/B Testing,用結果數字來說話。

這其實也牽扯到如何設計A/B Testing?一個APP最關鍵的不是他介面看起來炫不炫,而是最終用戶能不能持續地用你想讓他用的功能。記住If ,Then ,Because___.

如果你的變因錯誤的把畫面好看當成目標,那你甚至會覺得隨著畫面滾動把Tabbar隱藏起來是好的。那這樣一來或許畫面變好看,可是面積變多,但你APP整體功能使用率是下降的。

Binary search與Obj-C

| Comments

Binary search概念

Binary search概念其實很簡單,recursive版本實際寫起來很像Quick sort in-place版本前面divide的味道。每次的遞迴都把排序後的數列對切一半然後去其中一半來繼續處理,詳細可以看Wiki的介紹。

Binary search概念轉換成Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * @return Retrun index of number in array.
 * If number not found in array return false.
 */
- (NSNumber *)binarySearchIndexInArray:(NSArray <NSNumber *>*)array forNumber:(NSNumber *)number leftRange:(NSInteger)left rightRange:(NSInteger)right {
    
    if (left > right) {
        return [NSNumber numberWithBool:false];
    }
    
    NSInteger midIndex = (left+right)/2;
    NSNumber* mid = array[midIndex];
    
    if (number.floatValue > mid.floatValue) { // 對切的右邊
        return [self binarySearchIndexInArray:array forNumber:number leftRange:midIndex+1 rightRange:right];
    }else if (number.floatValue < mid.floatValue){ // 對切的左邊
        return [self binarySearchIndexInArray:array forNumber:number leftRange:left rightRange:midIndex-1];
    }else{ // equal,找到了
        return  [NSNumber numberWithInteger:midIndex];
    }
}

關於搜尋

這個搜尋法的前提是你的數列要幾經排序好了。這個前置條件才讓我發現我之前一直以為搜尋是有什麼神奇的方法,能夠在一個大數列裡面找到target的位置,現在接觸演算法就理解現實運作方法是先排序之後再用方法搜尋的。