其實主要是看你的工作中會用到什么,感覺還是先把Android的基礎(chǔ)學(xué)好,畢竟框架也是在Android的基礎(chǔ)上面進行開發(fā)的,你說的這幾個框架我沒怎么用過,大概看過一點,比如xutils和ThinkAndroid,他們都有一些自己的模塊如view,http,bitmap相關(guān)的模塊,這些模塊把一些常用的操作都進行了封裝,直接拿過來使用就可以了,這樣確實減少了開發(fā)的工作,也會減少工作中可能會犯的一些錯誤,比如如果bitmap處理不好就很容易造成內(nèi)存的問題。網(wǎng)上對這些框架有比較詳細(xì)的介紹和每個框架里面實現(xiàn)的模塊以及各個模塊支持的功能。對比一下哪個是你工作中最需要的,然后學(xué)習(xí)一下即可,學(xué)會了一種那么其他的框架在看的時候就可以做到舉一反三了。不過在學(xué)習(xí)框架的同時還是要把android的基礎(chǔ)多學(xué)習(xí)一下的。
專業(yè)領(lǐng)域包括成都網(wǎng)站建設(shè)、網(wǎng)站制作、商城網(wǎng)站定制開發(fā)、微信營銷、系統(tǒng)平臺開發(fā), 與其他網(wǎng)站設(shè)計及系統(tǒng)開發(fā)公司不同,創(chuàng)新互聯(lián)的整合解決方案結(jié)合了幫做網(wǎng)絡(luò)品牌建設(shè)經(jīng)驗和互聯(lián)網(wǎng)整合營銷的理念,并將策略和執(zhí)行緊密結(jié)合,為客戶提供全網(wǎng)互聯(lián)網(wǎng)整合方案。
硬件抽象層模塊編寫規(guī)范
硬件抽象層最終都會生成.so文件,放到系統(tǒng)對應(yīng)的目錄中。在系統(tǒng)使用的時候,系統(tǒng)會去對應(yīng)目錄下加載so文件,實現(xiàn)硬件抽象層的功能。因此硬件抽象層的加載過程就是我們使用so的一個接口。先了解加載過程從源頭了解抽象層模塊兒的編寫規(guī)范。
1、硬件抽象層加載過程
系統(tǒng)在加載so的過程中,會去兩個目錄下查找對應(yīng)id的so文件。這兩個目錄分別是/system/lib/hw和/vendor/lib/hw。
so文件的名字分為兩個部分例如id.prop.so,第一部分是模塊id。第二部分是系統(tǒng)prop的值,獲取順序為“ro.hardware”、“ro.producat.board”、“ro.board.platform”、“ro.arch”,如果prop都找不到的話,就用default。(不是找不到prop的值,是找不到prop值對應(yīng)的so文件)。
負(fù)責(zé)加載硬件抽象層模塊的函數(shù)是hw_get_module,所在的文件是/hardware/libhardware/hardware.c如下:
/** Base path of the hal modules */
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#endif
/**
* There are a set of variant filename for modules. The form of the filename
* is ".variant.so" so for the led module the Dream variants?
* of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
*
* led.trout.so
* led.msm7k.so
* led.ARMV6.so
* led.default.so
*/
static const char *variant_keys[] = {
"ro.hardware", ?/* This goes first so that it can pick up a different
file on the emulator. */
"ro.product.board",
"ro.board.platform",
"ro.arch"
};
static const int HAL_VARIANT_KEYS_COUNT =
(sizeof(variant_keys)/sizeof(variant_keys[0]));
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi-id) != 0) {
ALOGE("load: id=%s != hmi-id=%s", id, hmi-id);
status = -EINVAL;
goto done;
}
hmi-dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
/*
* Check if a HAL with given name and subname exists, if so return 0, otherwise
* otherwise return negative. ?On success path will contain the path to the HAL.
*/
static int hw_module_exists(char *path, size_t path_len, const char *name,
const char *subname)
{
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (access(path, R_OK) == 0)
return 0;
return -ENOENT;
}
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i;
char prop[PATH_MAX];
char path[PATH_MAX];
char name[PATH_MAX];
char prop_name[PATH_MAX];
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new copy of the library).
* We also assume that dlopen() is thread-safe.
*/
/* First try a property specific to the class and possibly instance */
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) 0) {
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
/* Loop through the configuration variants looking for a module */
for (i=0 ; iHAL_VARIANT_KEYS_COUNT; i++) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
/* Nothing found, try the default */
if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
goto found;
}
return -ENOENT;
found:
/* load the module, if this fails, we're doomed, and we should not try
* to load a different variant. */
return load(class_id, path, module);
}
int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id, NULL, module);
}
找到so文件之后,調(diào)用方法load方法去加載對應(yīng)的so文件,并返回hw_module_t結(jié)構(gòu)體。load方法源碼在上面程序中。
load方法首先調(diào)用dlopen加載對應(yīng)的so文件到內(nèi)存中。然后用dlsym方法找到變量HAL_MODULE_INFO_SYM_AS_STR符號對應(yīng)的地址,這個地址也就是一個hw_module_t結(jié)構(gòu)體,然后從這個結(jié)構(gòu)體中拿出id比對load方法出入的id是否一致,如果是的話表示打開成功。加載過程完成。
HAL_MODULE_INFO_SYM_AS_STR這個符號值為HMI,也就是必須要保證這個符號之后是一個hw_module_t。接下來的規(guī)范中有這個要求。
到此,模塊加載完成
2、硬件抽象層模塊編寫規(guī)范
硬件抽象層有兩個結(jié)構(gòu)體,一個是hw_module_t和hw_device_t,定義在hardware.h中。
首先說一下hw_module_t的編寫規(guī)范。
1、必須要有一個“自定義硬件抽象層結(jié)構(gòu)體”,且結(jié)構(gòu)體第一個變量類型要為hw_module_t。
2、必須存在一個HARDWARE_MODULE_INFO_TAG的符號,且指向“自定義硬件抽象層結(jié)構(gòu)體”。在加載的時候根據(jù)這個符號找到地址,并把地址的轉(zhuǎn)變?yōu)閔w_module_t,這也是為什么第一條中hw_module_t必須要在第一個的原因。
3、hw_module_t的tag必須為HARDWARE_MODULE_TAG
4、結(jié)構(gòu)體中要有一個方法列表,其中要有一個open方法。用open方法獲得hw_device_t
接下來說一下hw_device_t的編寫規(guī)范
1、必須要有一個“自定義硬件設(shè)備結(jié)構(gòu)體”,且結(jié)構(gòu)體第一個變量類型要為hw_device_t。
2、hw_device_t的tag必須為HARDWARE_DEVICE_TAG
3、要有一個close函數(shù)指針,來關(guān)閉設(shè)備
按照上面規(guī)范編寫的硬件抽象層就可以由系統(tǒng)加載并正確獲取到device。具體的應(yīng)用層邏輯在device中實現(xiàn)。
相信你看過微信關(guān)于模塊化的分享 《微信Android模塊化架構(gòu)重構(gòu)實踐》 ,也注意到里面提到的pins工程結(jié)構(gòu)。
作者是這樣描述的 ------“pins工程能在module之內(nèi)再次構(gòu)建完整的多子工程結(jié)構(gòu),通過project.properties來指定編譯依賴關(guān)系。通過依賴關(guān)系在編譯時找到所有的資源和源碼路徑?!?/p>
仔細(xì)推敲這句話的意思,應(yīng)該能知道它實現(xiàn)的基本原理------通過設(shè)置sourceSets指定多個java、res等路徑.
有關(guān)sourceSets的介紹:
但是,有一個問題需要要知道的是,一個module只能指定一個AndroidManifest文件,pins工程中包含了多個AndroidManifest,它是怎么做到的?
研究過 com.android.tools.build:gradle ,會留意到它使用到一個子庫 com.android.tools.build:manifest-merger ,官方通過這個庫來合并多個AndroidManifest文件,或許pins工程也是用了這方式。
接下來,再它的基礎(chǔ)上,我做的一些改動,取了另一個名字叫 MicroModule ,先來看一下工程結(jié)構(gòu):
與pins工程的結(jié)構(gòu)大致不變,增加了 androidTest 和 test ,以及將 project.properties 替換為 build.gradle 。
基本原理是不變的,與微信pins工程一樣配置 sourceSets 。AndroidManifest合并用了 com.android.tools.build:manifest-merger 。
在根項目的build.gradle中添加插件依賴:
在模塊的build.gradle中引用插件并配置 MicroModule:
MicroModule中的build.gradle:
為了使用上的更加方便,專門寫了Android Studio的插件,能快速的創(chuàng)建一個MicroMoudle.
插件安裝步驟 :
插件詳解 :
插件項目地址 :
MicroModule已經(jīng)上傳至Github,歡迎star交流。
Android作為一個移動設(shè)備的平臺,其軟件層次結(jié)構(gòu)包括了一個操作系統(tǒng)(OS),中間件(MiddleWare)和應(yīng)用程序(Application)。
根據(jù)Android的軟件框圖,其Android核心模塊結(jié)構(gòu)自下而上分為以下幾個層次:
第一、操作系統(tǒng)層(OS)
第二、各種庫(Libraries)和Android 運行環(huán)境(RunTime)
第三、應(yīng)用程序框架(Application Framework)
第四、應(yīng)用程序(Application)
android應(yīng)用模塊開發(fā):
在android的項目開發(fā)中,都會遇到后期功能拓展增強與主程序代碼變更的現(xiàn)實矛盾,也就是程序的靈活度。
由于linux平臺的安全機制,再加上dalvik的特殊機制,各種權(quán)限壁壘,使得開發(fā)一個靈活多變的程序,變得比較困難,不像pc平臺下那么容易。
瞅瞅elipse的插件,瞅瞅360的插件,在android下,我們一開始很難寫好一個主程序,然后通過插件機制來應(yīng)對以后的功能拓展,于是程序變得不那么靈活多變了。
比如一款android下的安全軟件,新版本增加了一個功能,如短信攔截,往往會因為一個模塊的增加,而重新編譯一個apk包,這樣周而復(fù)始,哪怕只增加50kb的功能代碼,用戶也需要升級一個完整的apk,往往是5~6M的體積。