Android概念篇(四)Intent

Intent 在Android中,不只是扮演著地址而已
而是實際上的"意圖"
因為它除了常被用來導向Activity外
還可以啟動Service、送出廣播、"發出要求"

Activity、Service、Boadcast
1.
在activity中,intent會帶有context + 下一個目的地activity
而進一步透過startActivity( )、startActivityForResult( )方法
啟動到達新的activity
當需要夾帶資料時,透過bundle來傳遞
而參考概念篇(二)Activity中的activity stack描述
我們可以在intent上加入flag,設定activity的開啟模式

-FLAG_ACTIVITY_NEW_TASK
-FLAG_ACTIVITY_SINGLE_TOP
-FLAG_ACTIVITY_CLEAR_TOP

2.
同樣道理,可以透過startService( )、bindService( )
來啟動service
只要告訴intent ,你要啟動的service是什麼
利用context透過方法就能啟動
同理,透過sendBroadcast( )、sendOrderdBroadcast( )、sendStickyBroadCast( )
可用來發出廣播


Implicit Intent
隱含的Intent,就是對整個Android系統
發出一種請求意圖,例如開網頁、開影片、打電話....
當系統拿到此Intent請求的"行為"跟"屬性"、"資料型態"時
會進行過濾跟匹配,叫出相關App中的Activity來處理
而我們要在哪裡設定Activity的匹配條件呢?
就在manifest檔案中
每當宣告一個activity時
可以加上intent-filter(這個filter就是用來過濾intent的)
例如:

<intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LUNCHER"/>
</intent-filter>

以上這段你一定看過,因為它就是每個APP中main activity
所預設的,意思是main activity是第一個載入的活動
並且要在桌面顯示
而我們當然可以在一個activity中
設定多個action跟category的過濾條件

1.Action
Action就是行為的過濾
我們可以設定Intent中的Action Type
而有設定要過濾這個Action Type的activity
就會呼應啟動
常見的Action Type有:

Intent.ACTION_VIEW
Intent.ACTION_DIAL
Intent.ACTION_SENDTO
Intent.ACTION_SEND
Intent.ACTION_WEB_SEARCH

限制是,intent-filter中一定要有一個action條件
而只要activity中有一條action符合
就能回應該intent

2.Category
類似Action的匹配模式
但限制是要全部條件都符合
才能回應該intent

3.Data
在intent-filter中
可以設定資料型態
代表我們這個activity,可以處理該資料(文件、圖片、影片...)
使用上與action跟category相似,如(取自官方範例):

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

名叫"ShareActivity",可以處理別人SEND型態的Action下
且格式是文件的檔案
限制是,intent-filter中不一定要有
而只要有一條匹配符合,就能回應該intent


小結
intent可以做的事遠比想像中多
除了能主動要求開啟activity、service、broadcast外
更能請系統去找到相關App來回應自己
所以這就手機操作上,時常出現的"請選擇開啟方式"
然後跳出相關、對應的App應用程式
了解intent後,更對手機的系統運作
有更深一層的認識囉

最後附上官方說明
跟民間高手整理的intent開啟用法




Android概念篇(三)Service淺解

看完Activity後,再來當然是談Android的另外一個支柱:Service
Service被允許在背景中長時間執行,沒有UI介面
但可以持續跟App互動,進行IPC(Inter-ProcessCommunication)

Service生命週期
有兩種方式可以啟動Service
1. startService( ),2. bindService( )
兩者的生命週期圖如下:

startService( ) 從上圖可知,此方法啟動的service,是一條龍走到底
沒有任何callBack回來,做完就可以結束了
所以常用於download文件之類的一次性service
但是"如果未主動呼叫stopService( ),service便不會結束"
除非系統發現並回收掉

bindService( ) 可以被綁定於activity中,並能同時被多個綁定
綁定後可以用connection物件取得binder連結
透過此binder連結,便能調用service內的功能
粗略運作流程如下圖:

由於可以隨時被綁定並使用
適用於持續性的後端服務,同時可以有其他操作
使用情境例如背景音樂播放等功能。
但此被綁定的service,在綁定的activity結束時
會自動執行onUnBind(解除綁定)
直到沒有activity綁定此service時,service會自動finish( )掉

Service技巧
1.
上述的兩種方式都是"後端服務"
但不管下載文件、音樂撥放,
卻往往都能在通知欄看見一個bar?
這是使用了notification跳出通知在上頭
並以startForeground( )持續在前景顯示

2.
service是跑在mainThread中的,所以在service的生命週期中,
onCreate( )只會被執行一次,除非發生configChange(如螢幕旋轉)
而onStart()、stop()、onBind()、onUnBind()
則是可以被多次執行的

3.
承上一段,在mainThread中調用service時
如果等候回應太長時,超過五秒便會使App Crash
(即ANR, Application Not Responding)
所以通常會再new一個thread去bindService
利用非同步的機制,避免等候service回傳等到沒有回應
再以callBack的方式,得到執行結果

由於複雜耗時的執行放在service中,不易被系統回收
加上非同步執行緒,不占用mainThread
便能保證activity中的功能流暢執行,提高使用者體驗


4.
由於service有時會有被系統回收的可能
因此我們可以透過flag,來設定service的"生存模式"

START_STICKY
被殺死後會不斷重生

START_NOT_STICKY
被殺死後不會重生

START_REDELIVER_INTENT
被殺死後會不斷重生
並且殺死前會記錄狀態,重生後繼續進行

Service與Android 8
然而Android8.0之後,限制了背景service的持續時間
避免service看不到,卻佔用過多資源、又耗電
這時就需要改用JobScheduler(搭配JobInfo、JobService)
變成排時間表、週期性有規律地、特定情況下
才來執行service工作


小結
service可說是一個app的後勤部隊
而activity在前線作戰時,與後勤的溝通便相對重要
其中Handler、AsyncTask 觀念更是不可少
妥善使用service,免得app變成後台吃資源怪獸


Android概念篇(二)Activity淺解

活動?
繼Context之後,我們接著來看到Activity是什麼
Activity 在App中,可視為一個"頁面"
但Android中,卻偏偏要叫它是一個"活動"
為甚麼呢?
我們可以這麼來看
因為它拿了資源(Context),負責要舉辦活動(Activity)
而舉辦一場活動要做些什麼?

1.分配資源 2.布置場地 3.主持會場 4.安排節目 5.回應觀眾

沒錯,以上可是看作是:

ResourceUIMain ThreadLife CycleEvent

這些的確都是Activity內要負責的
所以也不難理解,為何Android要把每個跳出來的頁面
稱作"活動",因為它不只是提供看而已
它還真的處理了不少雜事。

狀態與生命週期
回歸到實際面
Activity被產生時,便會在系統內被Activity Stack所儲存

一個Activity的生命

共有三段 : EntireVisibleForeground

四個狀態 : ActivitePausedStoppedDead

由七個函數控制 : onCreateonStartonResumeonPause

onRestartonStoponDestory

具體運作流程如下圖


只要override以上七個函數,便能在不同階段
做不同的處理,進入不同狀態。

而當新的Activity一直被產生時,會在Stack中不斷堆疊
要是我們再去呼叫其他App的Activity時
便會被以一個task包住(仍然在stack中)
目的是為了按返回鍵時
還能回到前一個App的Activity,產生無縫接軌的感覺。

如圖(來源):


不同App的activity可以包在一個task內
彷彿只是前後頁的關係而已
不過我們也可以在manifest檔案中,
特別定義Activity的開啟方式:

standard : 每次都重新創造activity

singleTop : 如果activity已經存在,且位於stack的頂端
會把該Intent傳給onNewIntent( )

singleTask : 如果已有activity位於task內,結束位於自己以上的所有activity,使自己處於task的頂端,如果沒有stack,自己新增。

singleInstance:每次都自己創造一個tack,只存自己(不建議使用)

生命週期應用
Activity的四個狀態,決定了被系統回收的先後
而七個方法與三段週期,影響著內部流程的設計:


1. Entire lifetime: onCreate()開始 到 onDestroy()結束

2. Visible lifetime: onStart() 到 onStop()之間

3. Foreground lifetime: onResume() 到 onPause() 之間

長度分別是1 > 2 > 3,彼此一層包著一層

而Foreground可以說是Running狀態,直接呈現到使用者面前

Visible則是要做足準備,Entire 分別用來初始化與做結束

所以歸納如下>>

onCreate: 負責初始化、UI布局、資源讀取
onDestory: 相對用來釋放所有資源

onStart: 表示開始上工,綁定service、register在此處,確保activity跑起來時有得執行
onStop: 相對用來解除綁定,釋放部分資源

onResume
onPause 
是activity迅速回復時要做的,離使用者眼前最近,
所以不要處理業務,執行太久將大幅降低使用者體驗

onRestart 執行特別的修復工作

另外
旋轉screen時,通常會重新執行onCreate動作
但依流程來說還要跑onStart、onResume,
這太耗時了,因此我們可以透過override
 onConfigation( ),來避免重跑流程函數

saveInstance( ) 可以用來記住當前view狀態
所以必須要在onStop前執行,因為一旦Stop後,
view即消失了,也會沒東西可以存...

按下返回鍵會執行onDestory

按下home鍵、電源鍵,僅會執行onStop

 
小結
Activity因為事情很雜,所以有必要分配好流程
生命週期除了扮演流程上的設計階段
也決定著不同狀態,被系統處理掉的先後
因為最貼近使用者體驗
所以每個步驟跟模式都要特別注意






Android概念篇(ㄧ)Context淺解

在談到寫Android應用入門時
一定會聽見Activity, Service, Receiver, Provider這四大元件
但是在這四者之前,還有一個本體你一定要先知道
叫做Context

廢話不多說
 
進入正題
Context是Android系統中,維持每個Process正常運作的核心
主導整個系統環境的運行,裏頭描述了環境的信息
所以有權限可以去(或者說被用來)

1.獲取資源 2.啟動App 3.發出廣播 4.Intent去其他"場景"

根據上圖所示,Context本身是一個Abstract的Class
被兩個子類繼承

ContextWrapper->用來包裝Context的類別
ContextImpl->用來實現Context行為的類別

所以ContextWrapper會帶有ContextImpl的reference,
以便使用ContextImpl的API來操作Context,調用系統資源。
而ContextWrapper又把這一切包裝成Activity跟Service,
然後才呈現到開發者面前,提供設計應用。
所以一個App,會擁有App(1)+Activity+Service數量的Context,
而Provider跟Reciever則沒有,但是需要依靠別人給它。


Context實際上做甚麼?
我們每每在Activity中,使用this修飾字時
其實是在執行getContext( ),來取得當下的Context
而App的Context,可以透過getApplicationContext( )取得

我們每當使用showDialog(彈跳窗), startActivity(開啟頁面),

LayoutInflation(產生UI), startService(服務), BroadCast(廣播),

LoadResource(載入資源),都是需要Context的

這也是為什麼Activity不能直接被new起來執行
因為它一定要有Context,Android在應用上的頁面切換、啟動
大體來說還是嚴謹的,而且受到生命週期的管理,

所以如果你要啟動頁面、服務、廣播、調用功能...
請出示身分跟說明 : 從哪裡來,要往哪裡去,想拿什麼、幹什麼

所以Context類別也提供許多flag,來調用系統的服務
(可參考官方文件)

(提醒:context因為可以佔用資源,所以要小心生命週期的控管,


不要輕易指定給命比自己長,或static的reference)



小結
Context就是Activity, Service在Android環境中
最原始裸露的靈魂,Activity就像給了肉體(UI)的人,
Service就像不滅的精神、默默耕耘的無名氏。
但是Context翻譯過來的原意 : "上下文"概念太過於籠統
所以又分別依功能被包裝成Activity, Service。
但我們回過頭來看,它的確用於"承先啟後"
更可以循著它來對系統資源做存取。

所以Context實際上就是一個文"","上下文"
彼此有著不同權限與前後關係,我們的應用App
只是把它包裝成舉辦活動(Activity)提供服務(Service)



Context觀念上大致就是如此
實際的操作跟使用,還請參考官方的文件說明~