編輯:關於Android編程
APP運行時可執行的代碼,主要有三部分:
1) 虛擬機初始化時加載的系統jar包,主要包含framework.jar和libcore.jar,分別對應android framework代碼和jdk代碼
2) APP自身程序代碼,也就是打包入APK的dex文件
3) APP程序運行需要額外加載的library,對應manifest裡配置的uses-library字段
前兩個沒啥好講的,重點講下第三部分,app可以在manifest裡頭聲明自身運行需要額外的library,但是,這裡僅僅只是聲明,最終還是要看系統有沒有配置這個sharelibrary,配置的方式可查看Android%20PackageManagerService%E8%AF%A6%E8%A7%A3.doc" target="_blank">2.2 初始化SystemConfig,如果app的uses-library在系統中不存在,apk會安裝失敗。
PMS初始化share library代碼:
ArrayMap
for (int i=0; i
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}
通過library path來初始化SharedLibraryEntry並保存到mSharedLibraries中。
在apk數據解析時,會調用updateSharedLibrariesLPw來將apk配置的user library更新到對應的package對象:
private void updateSharedLibrariesLPw(PackageParser.Package pkg,
PackageParser.Package changingLib) throws PackageManagerException {
if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
final ArraySet
int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
for (int i=0; i
final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i));
if (file == null) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + pkg.packageName + " requires unavailable shared library "
+ pkg.usesLibraries.get(i) + "; failing!");
}
addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
}
N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
for (int i=0; i
final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
if (file == null) {
Slog.w(TAG, "Package " + pkg.packageName
+ " desires unavailable shared library "
+ pkg.usesOptionalLibraries.get(i) + "; ignoring!");
} else {
addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
}
}
N = usesLibraryFiles.size();
if (N > 0) {
pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]);
} else {
pkg.usesLibraryFiles = null;
}
}
}
遍歷usesLibraries和usesOptionalLibraries,調用addSharedLibraryLPw依次將share library添加到usesLibraryFiles,並最終保存到pkg.usesLibraryFiles。
private void addSharedLibraryLPw(ArraySet
PackageParser.Package changingLib) {
if (file.path != null) {
usesLibraryFiles.add(file.path);
return;
}
PackageParser.Package p = mPackages.get(file.apk);
if (changingLib != null && changingLib.packageName.equals(file.apk)) {
// If we are doing this while in the middle of updating a library apk,
// then we need to make sure to use that new apk for determining the
// dependencies here. (We haven't yet finished committing the new apk
// to the package manager state.)
if (p == null || p.packageName.equals(changingLib.packageName)) {
p = changingLib;
}
}
if (p != null) {
usesLibraryFiles.addAll(p.getAllCodePaths());
}
}
file.path不為空,直接添加到usesLibraryFiles即可
程序啟動的時候,AcivityThread會通過app對應的application info來創建LoadedApk,然後LoadedApk內部根據dex和usesLibraryFiles來創建app對應的classloader。
APK掃描的過程,其實就是數據收集的過程,掃描的一些代碼,其實在上頭的內容中就有提到, PMS中調用scanPackageLI掃描一個apk文件,為了讓整個思路更加清晰,接下去將基於首次掃描一個nonsystem apk來分析,至於其他的一些細節,這裡就不做更多的描述,大家可自行閱讀源碼。
先介紹幾個核心類:
從下到上,依次包含,詳細介紹如下
1:PackageParser.Package 對應一個apk包完整的原始數據
2:PackageSetting 包含PackageParser.Package對象實例,說明它也對應一個apk包的數據,不同的是,它還包含apk相關配置數據,比如,apk內部哪些component是被disable等。
3:Settings 包含PackageSetting對象列表,也就是說它包含了系統所有apk數據
還有就是PackageParser,顧名思義,負責APK數據解析。
掃描新apk的流程描述:
1) 調用scanPackageLI,傳入要解析的apk文件
2) 創建PackageParser對象,接著調用parsePackage來解析apk清單數據,返回
PackageParser.Package對象pkg,pkg包含了apk原始的清單數據
3) 接著調用collectCertificatesLI獲取apk的簽名數據並保存到pkg中,這個函數的詳細介紹可看1.2.4 PMS中簽名相關代碼介紹
4) 調用scanPackageLI並傳入pkg,接著調用scanPackageDirtyLI
5) 接著判斷pkg.mSharedUserId是否為空,如果不為空,說明apk設置了share user id,那就要調用getSharedUserLPw創建新的share user id
6) 接著調用mSettings.getPackageLPw生成pkg對應的PackageSetting對象pkgSetting
7) 針對非系統app(parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR)== 0),調用updateSharedLibrariesLPw更新pkg的usesLibraryFiles
8) 如果是新安裝app((scanFlags &SCAN_NEW_INSTALL) != 0), 則查看app內部聲明的provider是否已經被其他已安裝的app定義,如果是,拋出異常
9) 調用fixProcessName生成app對應的進程名
10)調用createDataDirsLI在/data/data/下創建app私有目錄
11)調用NativeLibraryHelper.copyNativeBinariesForSupportedAbi拷貝so文件,接著調用setNativeLibraryPaths更新pkg.applicationinfo中的nativeLibraryDir等信息
12)調用performDexOptLI對apk中的dex做優化
13)調用mSettings.insertPackageSettingLPw(pkgSetting, pkg);將app對應的PackageSetting添加到Settings中
14)調用mPackages.put(pkg.applicationInfo.packageName, pkg);將app對應的Package添加到mPackages中
15)將app中定義的providers添加到mProvidersByAuthority,services添加到mServices,
receivers添加到mReceivers,activities添加到mActivities
16)將app中定義的permissionGroups添加到mPermissionGroups,permissions添加到permissionMap,instrumentation添加到mInstrumentation
很多細節代碼在前面章節都有過描述,這裡就不再一一展開。
先來了解一個服務:
DefaultContainerService
PMS安裝過程中APK文件的拷貝,都要通過它來完成,所以在安裝時,需要連接到該服務
以獲取對應的BinderProxy來遠程調用其功能。
接著介紹幾個類:
1) PackageHandler
對應變量mHandler,在PMS構造時會創建並綁定到HandlerThread,APK的安裝相關代碼會通過Message執行在這個工作線程。該Handler的主要Message ID如下:
ID
描述
INIT_COPY
將要安裝APK信息放置於msg.obj並發送該Message到Handler,Handler執行後,會做兩件事情:
1:判斷當前是否已經連接到DefaultContainerService,如果未連接,調用connectToService進行連接
2:將要安裝的APK信息添加到mPendingInstalls
3:如果mPendingInstalls長度為0,觸發
MCS_BOUND消息
MCS_BOUND
依次從mPendingInstalls取出HandlerParams來進行安裝
POST_INSTALL
安裝成功後,會收到該消息來執行相關操作
2) HandlerParams
抽象類,如果要將安裝操作通過INIT_COPY消息添加到PackageHandler,必須實現
HandlerParams,並將對應的實例放置於msg.obj中,MCS_BOUND命令在處理時,會將msg.obj轉換成HandlerParams,接著調用其函數startCopy來觸發拷貝,拷貝過程中,會回調如下狀態:
abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
APK拷貝對應的類為:
class InstallParams extends HandlerParams {
…
}
3) InstallArgs
也是一個抽象類,對應的實現類有兩個, FileInstallArgs和AsecInstallArgs,分別對應APK安裝到系統存儲和SD卡,他兩在實際APK數據解析上,沒任何區別,唯一不同的就是拷貝目錄和文件目錄操作存在差異,為了簡單起見,後續我們只基於FileInstallArgs來分析。
HanderParams是為了給PackageHandler傳遞參數並觸發拷貝和安裝行為的,所以,它的接口更多的為了配合PackageHandler來設計的
FileInstallArgs則是拷貝的執行者,同時還處理安裝前和安裝後的一些邏輯處理等,它的設計,是跟PMS深度結合在一起的。
程序可以通過調用PMS. installPackage並傳入APK文件路徑,IPackageInstallObserver2對應的binder proxy, install Flags等來啟動安裝,IPackageInstallObserver2是用於給調用方反饋安裝狀態,install Flag指定安裝命令,比如是否是替換安裝等。
installPackage什麼都沒做,直接轉到installPackageAsUser:
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// Caller holds INSTALL_PACKAGES permission, so we're less strict
// about installerPackageName.
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, observer, installFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
函數開頭先檢查調用程序是否有INSTALL_PACKAGES權限,如果沒有,直接拋出異常。
接著通過調用程度的user id來判斷其是否被限制安裝APK功能,如果是,直接返回
通過UID來判斷是否是通過ADB來安裝,接著基於傳入的參數創建InstallParams實例並保存到INIT_COPY msg.obj中並發送到PackageHandler.
安裝從拷貝開始,拷貝由發送INIT_COPY到PackageHandler觸發,完整流程圖:
從上面的流程可以看出,APK數據的解析,是通過installPackageLI來完成的,同樣的,下面只基於新安裝包這一情形來分析
看installPackageLI的代碼後會發現,其實她實現的內容跟scanPackageL(File f…)基本上是一致的:
1) 創建PackageParser對象,接著調用parsePackage來解析apk清單數據,返回
PackageParser.Package對象pkg,pkg包含了apk原始的清單數據
2) 調用collectCertificates獲取安裝APK的簽名數據
3) 調用installNewPackageLI並傳入pkg
4) 調用scanPackageLI傳入pkg進行apk數據掃描
5) 調用updateSettingsLI將apk數據更新到Settings
6) updatePermissionsLPw同步系統權限數據
github:https://github.com/zarics/ZrcListView先貼一個自己畫的ZrcListView的UML類圖(學習ing。。。)滾動的實現想
大家好!首先申明一下,本人是一名初級Android程序員,剛接觸EventBus(3.0的用法)有什麼說的不對的地方還請大神指點一二。此文章是面向能力不強,對於大神級別就
本文實例講述了Android viewpager中動態添加view並實現偽無限循環的方法。分享給大家供大家參考,具體如下:viewpager的使用,大家都熟悉,它可以實現
這裡介紹在Android中實現相機調取、拍照片、獲取照片、存儲新路徑等已經打開相冊、選擇照片等功能首先看一下界面,很簡單配置讀取內存卡和調用照相頭的功能 <!--