2015年3月5日 星期四

[轉] Android: startService vs bindService

http://blog.kenyang.net/2012/11/android-startservice-vs-bindservice.html

這篇要說明
  • startService
  • bindService
這兩者的差別!

其實在我之前的筆記有關Service的文章,都是使用startService!
並沒有使用bindService!
一方面是因為startService簡單很多!
再來也是最近才發現有bindService!
應該是說我原本以為startService的行為與bindService差不多!
但實際找答案以後才知道不是這樣的!


這裡就不多作說明如何使用startService或者bindService!
主要會說明這兩者差異性!
如果要看如何使用可以看之前的筆記:

  1. Android C2DM (四):實作之Android篇
  2. android detect screen on and screen off
  3. android 偵測 sdcard


先去看Google的定義,對於startService,Google定義如下
Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself.
簡言之用startService啟動,這個service是不會跟隨著啓動他的component消滅!且原則上是不能與UI互動的!(原則上)


而對於bindService,定義如下:
A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.
用bindService起來的話,則此service可以跟該component進行溝通!甚至可以做到 IPC!

小結:
bindService和startService最主要的差異在於其本身的lifecycle!
以及bindService可以用來做IPC!簡單的說就是可以讓你的service和UI溝通!
這裡就用一個project來說明吧!

先建立一個new project!
在這project中建立一個Activity,並指定它為launcher!
然後在建立一個Service!內如如下:
01public class Services extends Service {
02 
03 @Override
04 public IBinder onBind(Intent intent) {
05  // TODO Auto-generated method stub
06  Log.d("TestService","onBind");
07  return null;
08 }
09  
10 @Override
11 public int onStartCommand(Intent intent, int flags, int startId) {
12  Log.d("TestService","onStartCommand");
13  return super.onStartCommand(intent, flags, startId);
14 }
15 @Override
16 public ComponentName startService(Intent service) {
17  Log.d("TestService","startService");
18  return super.startService(service);
19 }
20  
21 @Override
22 public void onCreate() {
23  super.onCreate();
24  Log.d("TestService","onCreate");
25 }
26  
27 @Override
28 public void onDestroy() {
29  super.onDestroy();
30  Log.d("TestService","onDestroy");
31 }
32 
33 @Override
34 public boolean onUnbind(Intent intent) {
35  Log.d("TestService","onUnbind");
36  return super.onUnbind(intent);
37 }
38  
39}

可以看到上面的Service中並沒有做任何處理!
只有簡單的Log!
目的只是要讓我們瞭解startService和bindService的lifecycle差異性!

首先先來講startService,
我們先在onCreate中start!如下:
1@Override
2 protected void onCreate(Bundle savedInstanceState) {
3  super.onCreate(savedInstanceState);
4   
5  startService(new Intent(this,Services.class));
6 }

然後按下run,在開啟DDMS來觀看log!
會發現印出了”onCreate"->"onStartCommand"!

這時候按下back鍵來destroy這個activity,你會發現什麼都沒有印出來!
也就是說這個service仍在background執行!(你得手動stop)

這時候我們換成bindService來看看,如下:
1@Override
2 protected void onCreate(Bundle savedInstanceState) {
3  super.onCreate(savedInstanceState);
4   
5  bindService(new Intent(this,Services.class),null,Context.BIND_AUTO_CREATE);
6 }

一樣按下run!再看一下LOG!
會發現印出“onCreate"->"onBind"!

再按下back鍵,會發現...印出了!
“onUnbind"->"onDestroy"



總結上面兩項的差異,
會發現使用bindService時,
這個service的lifecycle是跟隨著bind它的object!
所以當該object被destroy,自然而然這個service也被destroy!

而使用startService,
就不是這樣了!這個service就是獨立的!不屬於任何一個object!
得靠stopService來停止!(任何一個Activity中都可以stop)


註:bindService的使用方式比較特別!
多帶了幾個參數!
在這個例子我們第二個參數設為null!
其實這參數是要傳入一個ServiceConnection的物件!
而ServiceConnection是一個Interface,這個Interface中
會有兩個callback function!
主要是用來monitor 這個service的state!

2015年2月28日 星期六

[轉] Linux中Workqueue機制分析

什麼是 workqueue ?
Linux 中的 Workqueue 機制就是為了簡化核心執行緒的建立。 通過呼叫 workqueue 的介面就能建立核心執行緒。 並且可以根據當前系統 CPU 的個數建立執行緒的數量,使得執行緒處理的事務能夠並行化。

workqueue 是核心中實現簡單而有效的機制,他顯然簡化了核心 daemon 的建立,方便了用戶的編程,



Workqueue 機制的實現
Workqueue 機制中定義了兩個重要的數據結構,分析如下:

1、  cpu_workqueue_struct 結構。 該結構將CPU 和核心執行緒進行了綁定。 在建立 workqueue 的過程中, Linux 根據當前系統 CPU 的個數建立 cpu_workqueue_struct 。 在該結構主要維護了一個任務隊列,以及核心執行緒需要睡眠的等待隊列,另外還維護了一個任務上下文,即 task_struct 。

2、  work_struct 結構是對任務的抽象。 在該結構中需要維護具體的任務方法,需要處理的數據,以及任務處理的時間。 該結構定義如下:

struct work_struct {
 unsigned long pending;
 struct list_head entry;/* 將任務掛載到 queue 的掛載點 */
 void (*func)(void *);  /* 任務方法 */
 void *data;            /* 任務處理的數據 */
 void *wq_data;         /* work 的屬主 */
 strut timer_list timer;/* 任務延時處理定時器 */
};

      

       當用戶呼叫 workqueue 的初始化介面 create_workqueue 或者 create_singlethread_workqueue 對 workqueue 隊列進行初始化時,核心就開始為用戶分配一個 workqueue 對象,並且將其鏈到一個全局的 workqueue 隊列中。 然後 Linux 根據當前 CPU 的情況,為 workqueue 對象分配與 CPU 個數相同的 cpu_workqueue_struct 對象,每個 cpu_workqueue_struct 對像都會存在一條任務隊列。 緊接著, Linux 為每個 cpu_workqueue_struct 對象分配一個核心 thread ,即核心 daemon 去處理每個隊列中的任務。 至此,用戶呼叫初始化介面將 workqueue 初始化完畢,返回 workqueue 的指標。



       在初始化 workqueue 過程中,核心需要初始化核心執行緒,註冊的核心執行緒工作比較簡單,就是不斷的掃描對應 cpu_workqueue_struct 中的任務隊列,從中獲取一個有效任務,然後執行該任務。 所以如果任務隊列為空,那麼核心 daemon 就在 cpu_workqueue_struct 中的等待隊列上睡眠,直到有人喚醒 daemon 去處理任務隊列。



       Workqueue 初始化完畢之後,將任務運行的上下文環境構建起來了,但是具體還沒有可執行的任務,所以,需要定義具體的 work_struct 對象。 然後將 work_struct 加入到任務隊列中, Linux 會喚醒 daemon 去處理任務。



       上述描述的 workqueue 核心實現原理可以描述如下:

点击看大图
    在 Workqueue 機制中,提供了一個系統默認的 workqueue 隊列—— keventd_wq ,這個隊列是 Linux 系統在初始化的時候就建立的。 用戶可以直接初始化一個 work_struct 對象,然後在該隊列中進行調度,使用更加方便。



Workqueue 編程介面

1
 create_workqueue
 用於建立一個 workqueue 隊列,為系統中的每個 CPU 都建立一個核心執行緒。

 輸入參數:
@name : workqueue 的名稱

2
 create_singlethread_workqueue 用於建立 workqueue ,只建立一個核心執行緒。

輸入參數:
@name : workqueue 名稱

3
 destroy_workqueue
 釋放 workqueue 隊列。

輸入參數:
@ workqueue_struct :需要釋放的 workqueue 隊列指標

4
 schedule_work
 調度執行一個具體的任務,執行的任務將會被掛入 Linux 系統提供的 workqueue —— keventd_wq

輸入參數:
@ work_struct :具體任務對象指標

5
 schedule_delayed_work
 延遲一定時間去執行一個具體的任務,功能與 schedule_work 類似,多了一個延遲時間,

輸入參數:
@work_struct :具體任務對象指標
@delay :延遲時間

6
 queue_work
 調度執行一個指定 workqueue 中的任務。

輸入參數:
@ workqueue_struct :指定的 workqueue 指標
@work_struct :具體任務對象指標

7
 queue_delayed_work
 延遲調度執行一個指定 workqueue 中的任務,功能與 queue_work 類似,輸入參數多了一個 delay 。

基本順序

1.創建工作隊列
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
2.創建工作
DECLARE_WORK(name, void (*function)(void *), void *data);
//一個函數搞定定義和初始化
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
//要先定義work_struct,再調用這兩個函數
//INIT_WORK做更全的初始化,若需要改變工作隊列,則用PREPARE_WORK


3.提交工作給一個工作隊列
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
//返回0,添加成功,非0表明已存在此工作
int cancel_delayed_work(struct work_struct *work);
//返回非0,取消成功,返回0,取消不成功,工作仍在運行
void flush_workqueue(struct workqueue_struct *queue);
//確保cancel_delayed_work調用返回0後,停止運行工作

4.刪除隊列
void destroy_workqueue(struct workqueue_struct *queue);
//用完後刪掉工作隊列

2015年2月23日 星期一

android native library logging by logcat

http://www.kunli.info/2011/08/21/android-native-code-study-note/

http://blog.csdn.net/powq2009/article/details/39667105
http://blog.xuite.net/free6d1823/blog/162208532-How+to+use+ALOG

http://stackoverflow.com/questions/19887158/how-take-log-output-from-a-native-library-function-in-android
http://blog.csdn.net/learnerz/article/details/6698669


How to build kernel, uboot, android separately

1.单独编译boot.img

配置文件(board_name要与目标平台相匹配):
./device/amlogic/board_name/Kernel.mk
配置情况一:配置defconfig(要与目标平台相匹配)
KERNEL_DEFCONFIG := meson6_g24_jb_defconfig
配置情况二:配置源码位置
KERNET_ROOTDIR :=?common
配置情况三:配置ko(按需增减以及修改ko)
define cp-kernel-modules
……
endef
编译命令:
. build/envsetup.sh
lunch g24ref-user
make bootimage

2.单独编译recovery.img

编译命令:
. build/envsetup.sh
lunch g24ref-user
make recoveryimage

3.手工编译kernel

目的:
如果小范围修改kernel代码,需要重新编译boot.img。直接用make bootimage是可以的。不过在这样会搜索android的所有Makefile,速度会比较慢。所以可以选择手工编译,方法和以前相类似,但是要指定kernel输出文件的相对于源码的位置(out/target/product/xxx/obj/KERNEL_OBJ)。
3.1编译boot.img
在Android根目录下的命令:
device/amlogic/g24ref/quilk_build_kernel.sh bootimage
<注>
生成的boot.img在out/target/product/xxx/obj/KERNEL_OBJ/arch/arm/boot中。
从卡启动kernel命令:
   mmcinfo;fatload mmc 0:1 0x82000000 boot.img;bootm

3.2编译recovery.img
在Android根目录下的命令:
device/amlogic/g24ref/quilk_build_kernel.sh recoveryimage
<注>
生成的recovery.img在out/target/product/xxx/obj/KERNEL_OBJ/arch/arm/boot中。
从卡启动kernel命令:
   mmcinfo;fatload mmc 0:1 0x82000000 recovery.img;bootm

3.3查遍所有配置
命令:
make help
3.4菜单配置kernel
命令:
device/amlogic/g24ref/quilk_build_kernel.sh menuconfig
<注> 在菜单中按需配置kernel。
3.5保存config
命令:
device/amlogic/g24ref/quilk_build_kernel.sh saveconfig

2015年2月15日 星期日

[轉] stack vs heap:執行時期儲存兩大要角

現代電腦系統大多依照Von Neumann Architecture設計而成,其中一特色stored programming乃指『程式執行一定要將欲執行的指令跟資料放入記憶體方可執行』,由此可知執行過程中記憶體所佔的地位厥偉之處。但許多工程師卻搞不清楚記憶體中的stack跟heap space到底有何居別,下面簡單針對兩者加以論述,希望對讀者有所幫助~

三分天下。程式執行過程中其實主要分成三大區塊:globalstackheap三塊。其中global區塊最最易理解,主要存放全域變數或宣告為static的靜態變數在此就不多做贅述;另外兩個區塊分別為stackheap這兩者往往混淆不清,尤其在java中有時候會出現stack overflowheap overflow到底兩者差異在哪,若工程師連這都不清楚那以後怎麼去調整JVM中的heap memory spacestack memory space的大小呢?

貼心的系統全自動化管理區塊:Stack Memory Space在記憶體中不外乎就是要存放變數、函式相關資訊等資料,使運作過程可以順利取得所需的變數或函式所在地。要讓系統可以全自動化管理,代表需可被預期此變數或函數資訊的生命週期,一旦完全可預測代表可以安心的交由系統管理,這些資訊也將在執行過程中被存放在stack空間。Stack中常見的存放資訊如下:區域變數(local variable)、函式參數(function/method parameter)、函數的返回位址(function/method return address)等資訊。為何上述資訊會放於stack之中,簡單來說:
void method1() {
  int x = 100;
}
上述的int x = 100,系統會在stack中找一個區塊給x,另外裡面的內容為100。然而,x會被放入stack主要是因為在編譯時期系統已經可以預知x從何時開始配置跟何時結束回收(當然就是看所屬block結束就跟著回收),由於配置跟回收的規則明確,當然就往stack擺囉。
在舉一例子:
void method2() {
  method1();
}
上述當呼叫method1()時,系統會先把method2的返回位址存到stack當中,為何是存放在stack呢,因為函式的呼叫有後進先出的概念,當method1()被呼叫而開始執行,待結束時必定會查找該返回何處,故最後一定會讀取函式的返回位址,既然如此明確而有條理,當然也是往stack放!

可預測性外加後進先出的生存模式,令stack無疑是最佳的存放策略。由於程式語言中變數跟函式的生命週期皆為後進先出的概念,也就是越晚產生的會越先被回收或銷毀。正因如此只要是可預測性的相關資訊都是往stack存放。此外,由於stack中的資料之存活週期規律故由系統自行產生與回收其空間即可,就不勞工程師們費心啦!

天啊!程式中竟然有不可預測其存活時間的資料存在。在程式中,有部分的需求總是在執行中依據實際情況才會動態增減,這些資訊是難以被預測哪時候開始有?量有多少?何時該回收?這些不可預測的因素造成上述的stack區塊不適合運用於此。當資訊為動態配置產生,系統會存放在另外一塊空間,稱之為『Heap(注意這裡的Heap跟資料結構中的Heap不相關,可別會錯意!)Heap的區塊專收執行期間動態產生的資料,由於為動態產生故結束點無法由系統來掌握,故需使用者自行回收空間。在C++Java中利用new語法產生的就是動態配置的物件,需存放於heap中。

奇怪跑越久記憶體用越多的怪現象。許多時候執行的程式都沒有改變,但卻常出現隨時間執行越久程式所耗用的空間將越多,最後造成out of memory。工程師也不知為何如此,就是定期在out of memory之前restart程式即可。這中現象層出不窮,一般大多是因為工程師沒有正確將記憶體回收所導致。Heap中的資料如果沒有正常的回收,將會逐步成長到將記憶體消耗殆盡,下次發生上述問題的實後,切記自己檢查一下heap空間的資料有無正常回收。論述到此有些讀者可能會覺得納悶:為何在寫Java都不需要注意回收空間的問題?~答案是因為Java中會採用Garbage Collection(垃圾回收)的機制自動檢查Heap中哪些資料已經沒有被使用,當確認資料已經沒有使用會自動將空間回收,如此工程師就專注撰寫程式即可,不用擔心記憶體回收不當等問題。

The conclusion is…當產生stack overflow一般是因為過多的函式呼叫(例如:遞迴太深)、或區域變數使用太多,此時請試著將stack size調大一點,另外檢查看看函式的呼叫跟變數的使用量。反之,當發生heap overflow請檢查是否都有正確將heap space的資料回收,另外採行的動態配置是否合理,不要過渡濫用而new出無謂的空間,若真的是程式過於複雜造成,請將heap size調大一些。