2016年2月1日 星期一

在 Android 中檢查網路連線是否暢通

在 Android 中檢查網路連線是否暢通

假設今天我們要做一個網路連線的請求, 如果使用者按下 button 你的 app 不管三七二十一就直接去做連線,這樣是很糟的~

運氣好一點你的 function return false 或者 catch exception,歹運的話就等等等等等等到 timeout,為了避免這種運氣差的狀況,你需要在開始連線前先檢查一下使用者的裝置到底有沒有連上網路。

我們可以使用 ConnectivityManager 取得 NetworkInfo 來查查目前裝置的網路狀態,包含目前是使用何種連線方式、網路是否連接、是否漫遊中……等等,應有盡有,簡單一點的話用 NetworkInfo.isConnectedOrConnecting() 就妥當了,以下是範例。

public boolean isOK() {
    ConnectivityManager cm =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo ni = cm.getActiveNetworkInfo();
    return ni != null && ni.isConnectedOrConnecting();
}

而要使用這個功能呢,需要在 manifest 加入以下 permisiion

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Reference:
How to check internet access on Android? InetAddress never timeouts…

Written with StackEdit.

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.