2015年12月14日 星期一

Android 開發心得整理

Android 開發心得整理

Application

  • 只要有該 App 的任何元件(Activity、Service、BroadcastReceiver)執行,Application 就會存在。

Service

  • 簡單一點的話就用 IntentService

  • 同一個 Intent 的 Service 不會同時間執行(尚未測試不同 Intent 的 Service),若執行了兩個相同的
    Service 在 S1 還沒執行完時不會執行 S2,S2 會在S1 執行完後立刻執行,並且 S1 不會執行 onDestroy 而 Service2 會。

  • 使用 AlarmManager 註冊 Service 預計執行 Service,在時間到達前可以重新註冊(update)達成延後或提 前執行或取消註冊。當有兩個相同的 Service 到達執行時間時,僅有 S1 會執行,S2 處於 ready 狀態,ready 狀態的 Service 無法更新、取消註冊。

  • 已經向 AlarmManager 註冊的 Service 就算重新開機,一樣會執行,可以理解為是向 OS 註冊。

  • 當 Service 執行到一半,以 Android task 關閉方式 Kill 該 Service 所屬 App,Service 會被腰斬(沒有執行完),若使用 startForeground 則不會被腰斬,但不會執行 onDestroy。

EventBus

  • 注意 onEvent 的 Thread 問題

  • 想保留該 event 的話可以使用 postSticky

Realm

  • 在 Thread1 撈出來的 RealmObject 無法在 Thread2 存取(讀取也不行!)。

  • 只有在 Main Thread(UI Thread) 撈出來的物件會自動更新。

  • 在非 Main Thread 撈資料前,先使用 refresh,確保資料是最新的狀態。

其他 Lib

Written with StackEdit.

2015年12月10日 星期四

Android 在 gradle 中加入 RecyclerView dependency 的錯誤

Android 在 gradle 中加入 RecyclerView dependency 的錯誤

今天要來寫 Android 的 RecyclerView,我在 app.gradle 中的 dependencies 中加入了

compile 'com.android.support:recyclerview-v7:23.1.0'

也就順便把 appcompate-v7 改成一樣的版本

compile 'com.android.support:appcompat-v7:23.1.0'

結果出現了這兩個錯誤

Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'.
Error retrieving parent for item: No resource found that matches the given name 'android:Widget.Material.Button.Colored'.

而這兩個東西是在這個檔案裡的

{專案目錄}\app\build\intermediates\exploded-aar\com.android.support\appcompat-v7\23.1.0\res\values-v23\values-v23.xml

想想應該是還有哪裡的版本也要改,估狗一下馬上找到原來是 compileSdlVersion 也要設定一樣的版本,再把它改成 23 後就可以正常使用了。

整個 app.gradle 大概是長這樣

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23

    .
    .
    .
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.1.0'
    compile 'com.android.support:recyclerview-v7:23.1.0'

    .
    .
    .
}

Written with StackEdit.

2015年12月7日 星期一

在 Android 中判斷該 Service 有無被註冊喚醒的方法

在 Android 中判斷該 Service 有無被註冊喚醒的方法

我的程式有些批次的事情要做,大概是每五分鐘要去檢查有沒有任務要做,有的話就做,沒的話就等下一個五分鐘,我的任務是有可能會失敗的(例如傳送資料),需要在下一次 Service 內再去嘗試執行,而且要在背景執行。講到這裡聰明的你可能會想到要用 Service 搭配 AlarmManager 來做,就給他設定每隔五分鐘呼叫一次,我也是這樣想的,但遇到一個問題,這樣做非常非常非常非常耗電,就算沒任務也要把 Service 叫起來這可不行。

所以我把程式改為在 ServiceonDestroy 時去判斷還有沒有任務沒完成的,如果有就設定五分鐘後再次執行,若沒有就不設定了,有新任務加入時再去把 Service 叫起來。

這次的重點就在這!

  • 假設上一次的 Service 結束時已經沒有任務要做了,就不會有下一次的自動執行,這時候有新的任務加入,我把 Service 叫起來然後執行,OK!沒問題。
     
  • 假設上一次的 Service 結束時還有任務要做,所以五分鐘後會自動執行,在執行之前有新的任務加入,這時候我該叫起 Service 嗎?叫起的話就破壞了我每五分鐘執行一次的規律生活了。

這樣說起來就是我需要知道目前這個 Service 有沒有被註冊自動執行囉?

拜請 Google 大神的結果是,沒辦法知道 AlarmManager 目前註冊了哪些 PendingIntent,所以我無法透過 AlarmManager 知道這個 Service 有沒有可以自起爬起來工作。

好吧從另一個目標下手,PendingIntent

他的 getService 第四個參數若設定為 PendingIntent.FLAG_NO_CREATE ,可以用來查詢這個 Intent 有沒有存在 PendingIntent,若存在會回傳該 PendingIntent ,反之會回傳 null

if (null == PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE)) {
    // register service
}

然後在 Service 執行時立馬把這個 PendingIntent 刪掉。

@Override
public void onCreate() {
    PendingIntent pi = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_NO_CREATE);
    if (pi != null)
        pi.cancel();
}

如此便完成我的需求

「當該 Service 已經註冊下次喚醒時間時,不再重覆註冊或直接喚醒以避免破壞其規律的生理時鐘」

Written with StackEdit.

2015年11月23日 星期一

在 Android Studio 加入 assets 資料夾

在 Android Studio 加入 assets 資料夾

在 Android Stdudio 中開心的專案,預設是沒有 assets 這個資料夾的,想要就要自己來。

首先將專案目錄那邊切換到 Project 檢視模式,然後在 app/src/main 目錄下按右鍵 New > Directory,名稱打上 assets,完成!

假設 assets 目錄下有個 OOO 資料夾,裡面有個 QQQ.txt,想要讀取的話就是 …

getAssets().open(getString(OOO/QQQ.txt))

會以 InputStream 回傳

Reference
Where to place Assets folder in Android Studio

Written with StackEdit.

Git 操作常見問題集

Git 操作常見問題集
  • 我加入了遠端的 repo,想要更新他的 branch 清單

    git remote update <remote name> --prune

  • 我現在有一個本地 branch 我想要拉一個遠端的 branch 下來合併

    切換到要合併的 branch
    git checkout <local branch name>
    然後拉下來
    git pull <remote name> <remote branch name>
    然後你就會看到很多 conflict XDDDDDDDDD

  • 想把現在的 branch 推上去 remote

    git push <remote name> <local branch name>

  • 已經修改了某些檔案,突然想切換 branch(或者原本忘了切換)但又不想 commit

    這是發生在我身上的真實案例,我想開發一個新功能,結果忘了先把 branch 切到 new_feature_xxx,在 master 下就直接開始寫 code 了,這真的很慘。這時候就需要 stash !它可以將目前的修改暫存起來,讓目前的 git 是上一次 commit 的狀態。

    暫存當前狀態
    git stash
    切換到別的 branch
    git checkout <local branch name>
    把剛剛修改的東西較叫回來
    git stash pop

  • 替 branch 改名

    git branch -m <old name> <new name>

Reference:
How to update remote branch list on local machine?

Written with StackEdit.

2015年11月16日 星期一

透過 Javascript 存取 cookie 的方法 (用 rails 的話超簡單!)

透過 Javascript 存取 cookie 的方法 (用 rails 的話超簡單!)

今天我是一個 rails 的使用者,請教 google 大神到底要怎麼在 javascript 存取 cookie,goo 了很久,一堆人都說阿就 $.cookie('name'),看起來是 jQuery 的東西,我也在 rails 中裝了 jQuery 但就是顯示 $.cookie is not a function

後來才發現,幹原來是要另外裝 jquery-cookie 才能用。然後這個 project 還搬家變成 js-cookie 了,看名字應該是擺脫 jQuery 的束縛了。

如果是 rails 的話直接用 gem 裝 jquery-cookie-railsjs_cookie_rails 就可以了。用後者的話基本上就是在 Gemfile 加上

gem 'js_cookie_rails'

然後 bundle install 再去 app/assets/javascripts/application.js 裡面加上

//= require js.cookie

就可以開始用他的指令去存取 cookie 了,下面簡單的介紹基本的,要注意的是生命期限預設單位是天。

// 設定 cookie
Cookies.set('name', 'value');

// 設定 cookie 生命期限 7 天
Cookies.set('name', 'value', { expires: 7 });

// 設定 cookie 生命期限 30 秒
var date = new Date();
date.setTime( date.getTime() + ( 30 * 1000 ));
$.cookie( "hoge", "30 seconds", { expires: date });

// 取得 cookie,若無此 cookie 會回傳 null
Cookies.get('name');

// 刪除 cookie
Cookies.remove('name');

Reference
jquery.cookie.jsの使い方とCOOKIEの寿命(保存期間)を秒・分・時間で指定する方法

Written with StackEdit.

2015年11月15日 星期日

在 chrome 中檢視各網站 cookie 的方法

在 chrome 中檢視各網站 cookie 的方法

只要在網址的地方輸入下面這行即可

chrome://settings/cookies

Reference:
How to View Cookies

Written with StackEdit.

2015年11月14日 星期六

Android Studio 編譯錯誤 unmappable character for encoding MS950

Android Studio 編譯錯誤 unmappable character for encoding MS950

前陣子開始跳槽,把一些 project 從 Eclipse 改到 Android Studio 去做遇到了編碼的問題,明明就都用 UTF-8,Android Studio 的 File Encodings 中 IDE Encoding、Project Encoding、Default encoding for properties files 也是設定 UTF-8 但還是出現錯誤訊息。

Gradle: error: unmappable character for encoding MS950

沒辦法,實在是不會 gradle。上網爬了一下後找到答案,在該 module 的 build.gradle 中加入下面這一段就妥當了。

tasks.withType(JavaCompile) {
     options.encoding = "UTF-8"
} 

Reference:
Android开发常见问题

Written with StackEdit.

C# 將 console 的內容轉至 textbox

C# 將 console 的內容轉至 textbox

平常寫程式總會做一些狀態的輸出,例如現在完成了什麼、某個數值是多少(總不可能永遠都用偵錯模式看嘛),若沒用一些專門處理 log 的 library 大概就是用 Console.write 最簡單了。

當寫好的程式被用在視窗程式裡,要看到這些 console 可以用 kernel32 的 AllocConsole,但我今天就是想要顯示在 form 的 textbox 裡!

這個 Console 其實是可以設定 out 去哪裡的,呼叫 Console.SetOut(TextWriter),然後只要自己寫好一個繼承自 TextWriter 的東西就可以丟給他了,至於要 out 去哪裡就是看 TextWriter 的 Write 和 WriteLine 怎麼寫了。

在此獻上一段簡單的 code

public class ConsoleTextBoxWriter : TextWriter
{
    private TextBox textBox;

    public ConsoleTextBoxWriter(TextBox textBox)
    {
        Console.SetOut(this);
        this.textBox = textBox;
    }
    public override Encoding Encoding { get { return Encoding.UTF8; } }

    public override void Write(string value)
    {
        WriteImp(value);
    }

    public override void WriteLine(string value)
    {
        WriteImp(value + Environment.NewLine);
    }

    private void WriteImp(string value)
    {
        if (this.textBox.InvokeRequired)
            this.textBox.Invoke(new MethodInvoker(delegate()
            {
                textBox.AppendText(value);
            }));
        else
            textBox.AppendText(value);
    }
}

Written with StackEdit.

使用 Dockerfile 安裝 rails 心得

使用 Dockerfile 安裝 rails 心得

最近又再次來碰碰 rails,因為不會 linux 所以覺得光是安裝開發環境就很痛苦,用 vm 做一個開發環境的保存覺得太肥大了,所以來試試看紅很久的 docker

不管是 rails、linux 還是 docker 我都是菜鳥,光是要完成這份 dockerfile 就花了好幾天的時間,遇到的問題有很多

  • 以為 CMDRUN 是差不多的東西

    看官方的說明,以為 CMD 可以真的執行指令,結果只是在 container 運行後自動執行的指令

  • 用 root 跟用 user 路徑會不同

    一開始都直接用 root 去安裝設定東西,但這樣一來跟網路上教學文章所教的路徑就都不一樣了,沒辦法,我就不會 linux 只好建一個 user 來繼續

  • 在 docker 中用 rvm 安裝 ruby 2.2.3 會無法安裝 rails 4.2.0

    真的找不到原因,只好改用 rbenv 惹

  • 在 dockerfile 中一直無法使用 rbenv、gem 等程式,但是實際進入 container 就可以

    一直覺得是這些東西沒有被 include 的感覺,試了很久真的跟我想的一樣,每次的 RUN 貌似都是用不同的 bash 去執行,也就是說設定的變數並不會保留到下一次的 RUN,所以呢,每次的 RUN 都要把要用到的東西的 PATH 寫進去

以下就是我這次的 rails 環境,也不知道要用什麼 base 就用 ubuntu 了

# === Reference ===
# Rails install tutorial: https://gorails.com/setup/ubuntu/14.04

FROM ubuntu:14.04
MAINTAINER aiueoH

RUN useradd -m railsuser && \
    echo "railsuser ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers

USER railsuser

RUN sudo apt-get update && \
    sudo apt-get install -y \
    python-software-properties \
    software-properties-common

RUN sudo add-apt-repository ppa:chris-lea/node.js && \
    sudo apt-get update && \
    sudo apt-get install -y \
    git-core \
    curl \
    zlib1g-dev \
    build-essential \
    libssl-dev \
    libreadline-dev \
    libyaml-dev \
    libsqlite3-dev \
    sqlite3 \
    libxml2-dev \
    libxslt1-dev \
    libcurl4-openssl-dev \
    libffi-dev \
    nodejs

RUN cd && \
    git clone git://github.com/sstephenson/rbenv.git .rbenv && \
    echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc && \
    echo 'eval "$(rbenv init -)"' >> ~/.bashrc && \
    exec $SHELL

RUN git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build &&\
    echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc && \
    exec $SHELL

RUN git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash

RUN export PATH="$HOME/.rbenv/bin:$PATH" && \
    rbenv install 2.2.3 && \
    rbenv global 2.2.3

RUN echo "gem: --no-ri --no-rdoc" > ~/.gemrc && \
    export PATH="$HOME/.rbenv/shims:$HOME/.rbenv/bin:$PATH" && \
    gem install bundler && \
    gem install rails -v 4.2.0 && \
    rbenv rehash

# Dependency for ruby gem - CarrierWave
RUN sudo apt-get install -y libmagickwand-dev

USER root

拜託各位先進多多指出錯誤或不妥的地方,感謝!

Written with StackEdit.

asp.net 4.0 ReportViewer IE9 報表欄位寬度顯示問題

asp.net 4.0 ReportViewer IE9 報表欄位寬度顯示問題

在 ASP.NET4.0 使用 ReportViewer 在 IE9 瀏覽時,報表內的表格欄寬,不會依照設計時所設定的寬度顯示。

在檢視 ReportViewer 所產生的 iframe 內,發現有一 <td width="100%" height="0/">,在某些瀏覽器下,該 <td> 會占據大量版面,導致原有的報表表格受到擠壓。

解決方法很簡單,只要在網頁內加上少許 CSS,讓該 <td> 無效即可。如下。

<style type="text/css">
    td[id*='oReportCell'] {width:100%; !important;}
</style>

並在 ReportViewer 屬性加上 AsyncRendering="false" 使 iframe 套用外層的 CSS 屬性

<rsweb:ReportViewer ID="ReportViewer1" runat="server" Font-Names="Verdana"
    Font-Size="8pt" Height="0px" ProcessingMode="Remote" Width="100%" AsyncRendering="false">
</rsweb:ReportViewer>

本文參考
ReportViewer doesn’t take full width in Internet Explorer

Written with StackEdit.

2015年9月12日 星期六

C# Socket 傳送訊息、指令、檔案筆記

目標


設計兩支程式,一支安裝在我想「觀看」的電腦,一支放在我的電腦。

在對方電腦開機後可以隨時看到對方的畫面

可以複製對方電腦的檔案

對方電腦執行特定程式時可以通知我

這支程式叫做 FiveGhost


Before 2015/9/12


Server 端的 Socket 需要 Listen 才可讓 Client 開始連線。

Server 端的 Socket 在  Accept 後會拿到一個專門處理該 Client 的新 Socket,該開一個新的 Thread 來處理他才能繼續 Accept 其他的 Client。


2015/9/12


網路傳輸這東西是非同步的,一次的 Receive 可能不會收到完整的資料,因此需要透過結尾符號來判斷,並加上 Timeout 機制來斷定對方是否還有在傳資料。

在 Server 與 Client 建立 Socket 連線後,兩邊都可以透過這條線傳送資料的。

Socket 本身並沒有所謂的斷線,儘管某一邊已經關閉 Socket,還是可以從另一邊 Send。要檢查這條線有沒有斷掉,估計需要透過不斷的詢問對方來得知,Socket 本身的 Connected 屬性不知道有啥用。

原本想說所有傳送的資料都用設計為一個指令,用 Class 包裝為物件再以序列化傳送,後來想想不太行,假設今天已經把程式植入對方電腦,過了一陣子程式有更新,可能稍微修改了 Class,監控端的電腦使用新的版本,但沒空將新版的程式去重新植入對方電腦,可能會導致反序列化錯誤。


2015/9/14


平常只要一個 Class 裡面東西太多,就會想辦法封裝,讓一個 Class 保持清爽。今天在看別人的範例時發現一個問題,把一些簡單的 API 封裝會讓看 Code 的人看得有點痛苦,不易快速讀懂 Code。日後在撰寫範例時需考慮到這點。


2015/10/2


大致上完成了,傳送畫面、檔案都 OK。

寫到向 server 要求檔案這部分,檔案傳回來後希望彈出 dialog 詢問要存在哪兒,在這裡遇到非 UI 執行緒的問題,這沒什麼以前常寫,C# 就是用個 Invoke 呼叫 delegate,但遇到一個小問題,我要委派的 function 要帶參數(檔案),我馬上想到以前寫另開 thread 執行帶參數的方法,開一個 class 把要用的東西先塞進去,再委派物件裡邊不用餐數的 function 執行就好了,確實這樣寫沒問題,但又想到,現在 C# 都有可以帶參數的 ThreadStart 了,這個 Invoke 應該也有辦法帶參數才是,果然 Invoke 有另一個 overload,第一個參數是 delegate,第二個參數是要傳過去 function 的參數,用 object 陣列包裝起來,嗯!

2015年9月11日 星期五

Visual Studio C# 加入 namespace (using) 的快捷鍵

狀況


最近跳回來寫 C#,每次用了新 namespace 的 class 要加入 using 時,都要將滑鼠移過去點選指定的 namespace,手要離開鍵盤去動滑鼠實在有點麻煩,想說之前寫 Java 都可用 ctrl + enter,這應該也可以,於是乎找了一下就找到方法了。


將閃標移到未知的名稱再按下 「ctrl + .」 就可以跳出那個選單啦!

Reference


[TechDays]課程心得分享 : Visual Studio 開發工具之使用經驗與功能設定技巧分享

2015年9月10日 星期四

C# 取得自己的 IP

Code


IPAddress GetIP()
{
    foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
        if (ip.AddressFamily == AddressFamily.InterNetwork)
            return ip;
    return null;
}


Reference


How to get the IP address of the server on which my C# application is running on?

2015年5月26日 星期二

Ubuntu 中開發 Android 錯誤訊息: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory

錯誤訊息


[2015-05-26 21:19:42 - tmp] /home/myubuntu/android-sdks/build-tools/22.0.1/aapt: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory



輸入下列指令,安裝 lib32stdc++6、lib32z1、lib32z1-dev 這幾個 lib。
sudo apt-get install lib32stdc++6 lib32z1 lib32z1-dev


Reference


ADT - libstdc++.so.6: cannot open shared object file

2015年4月6日 星期一

讓 sublime text 2 照順序切換文件 (switch tabs)

在 Preferences > Key Bindings - User 中加入

[
{ "keys": ["ctrl+pagedown"], "command": "next_view_in_stack" },
{ "keys": ["ctrl+pageup"], "command": "prev_view_in_stack" },

{ "keys": ["ctrl+tab"], "command": "next_view" },
{ "keys": ["ctrl+shift+tab"], "command": "prev_view" }
]


Reference


http://www.sublimetext.com/forum/viewtopic.php?f=2&t=2553