編輯:關於Android編程
Android源碼之“應用程序界面“分析二(PackageManager和PackageManagerService):
一。PackageManager:
上一篇中,我們用到了PackageManager的getPackageSizeInfo函數,來得到應用程序的詳細信息,這一篇中,我們將深入PackageManager的內部,來一探究竟。
1. PackageManager類:
它是一個抽象類,我們僅僅列出幾個要討論的函數:
定義: public abstract class PackageManager {
// @hide
public abstract void clearApplicationUserData(StringpackageName,
IPackageDataObserver observer);
// @hide
public abstract void getPackageSizeInfo(String packageName,
IPackageStatsObserver observer);
public void installPackage(Uri packageURI) {
installPackage(packageURI,null, 0);
}
說明:
1) Hide,意味著我們不能像上一篇文章中提到的settings中的實現方式來直接調用getPackageSizeInfo這樣的函數,也就是說,在我們的應用中,下面的做法是非法的:
PackageManager mPm = getPackageManager();
mSizeObserver = newPkgSizeObserver();
mPm.getPackageSizeInfo(packageName, mSizeObserver);
那麼,那麼,為了實現我們的功能,我們該如何做呢?
這就需要用到java反射機制,來調用android framework層的hide的函數。
在http://blog.csdn.net/liranke/article/details/13766349一篇文章中,詳細描述了這種實現方式。
2) 我們發現有installPackage這樣的函數,不難猜出,它是用來安裝應用程序的。
3) 再次提醒一下,這是一個定義為abstract的類,那麼,它的實現類在哪裡呢?
我們也沒有發現哪個類是繼承自它的啊。
事實上,實現它的類是在PackageManagerService中,而它的定義如下:
class PackageManagerService extends IPackageManager.Stub {
final Installer mInstaller; // 非常重要
。。。
getPackageSizeInfo(。。。)
。。。},
這裡,我們僅僅以getPackageSizeInfo來做為例子進行分析。
2. getPackageSizeInfo函數:
public void getPackageSizeInfo(final String packageName,
final IPackageStatsObserverobserver) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GET_PACKAGE_SIZE, null);
// Queue up an async operation since the package deletion may take alittle while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageStats lStats = new PackageStats(packageName);
final boolean succeded;
synchronized (mInstallLock) {
succeded = getPackageSizeInfoLI(packageName,lStats);
}
if(observer != null) {
try {
observer.onGetStatsCompleted(lStats, succeded);
} catch (RemoteException e){
Log.i(TAG,"Observer no longer exists.");
}
} //end if observer
} //end run
});
}
1)。需要注意,permission.GET_PACKAGE_SIZE權限。
2)。PackageStats lStats將會存放著得到的數據,
3)。getPackageSizeInfoLI是主要的函數。
4) 。onGetStatsCompleted, 我們需要實現的回調函數,這在上一篇文章中已經有體現。
那麼,在getPackageSizeInfoLI中,有:
int res = mInstaller.getSizeInfo(packageName,p.mPath,
publicSrcDir, pStats);
而mInstaller是Installer類型的一個實例,事實上,它是與android中的守護進程installd進行通信的,通過socket.
3. Installer的定義如下:
class Installer {
private static final String TAG ="Installer";
InputStream mIn;
OutputStream mOut;
LocalSocket mSocket;
byte buf[] = new byte[1024];
int buflen = 0;
private boolean connect() {
if (mSocket != null) {
return true;
}
Log.i(TAG, "connecting...");
try {
mSocket = new LocalSocket();
LocalSocketAddress address = newLocalSocketAddress(
"installd", LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (IOException ex) {
disconnect();
return false;
}
return true;
}
private void disconnect(){
Log.i(TAG,"disconnecting...");
try {
if (mSocket != null) mSocket.close();
} catch (IOException ex) { }
try {
if (mIn != null) mIn.close();
} catch (IOException ex) { }
try {
if (mOut != null) mOut.close();
} catch (IOException ex) { }
mSocket = null;
mIn = null;
mOut = null;
}
private boolean readBytes(byte buffer[], int len)
private boolean writeCommand(String _cmd)
private synchronizedString transaction(String cmd)
private int execute(String cmd) {
String res = transaction(cmd);
try {
return Integer.parseInt(res);
} catch (NumberFormatException ex) {
return -1;
}
}
public int install(String name, int uid, int gid) {
StringBuilder builder = newStringBuilder("install");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
return execute(builder.toString());
}
public int freeCache(long freeStorageSize) {
StringBuilder builder = newStringBuilder("freecache");
builder.append(' ');
builder.append(String.valueOf(freeStorageSize));
return execute(builder.toString());
}
public int getSizeInfo(String pkgName, StringapkPath,
String fwdLockApkPath, PackageStatspStats) {
StringBuilder builder = newStringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath != null ?fwdLockApkPath : "!");
String s =transaction(builder.toString());
String res[] = s.split(" ");
if((res == null) || (res.length != 4)){
return -1;
}
try {
pStats.codeSize =Long.parseLong(res[1]);
pStats.dataSize =Long.parseLong(res[2]);
pStats.cacheSize =Long.parseLong(res[3]);
return Integer.parseInt(res[0]);
} catch (NumberFormatException e) {
return -1;
}
}
分析:
1) 在connect函數中,與它進行socket連接的是installd。Installd是android中的守護進程,用ps可以看到它,它的父進程是init,關於Android啟動流程的更加詳細的說明,請看下面這篇文章:
http://blog.csdn.net/liranke/article/details/4694989。
2) 有一些read,write函數,當然是用於對socket讀取寫數據;
3) install, freeCache, getSizeInfo只是將固定字符串的命令傳入到execute函數中;
4) execute的實現,只是將命令字符串通過transaction傳下去,而在中會調用writeCommand,去真正地將命令寫入到與之連接好的守護進程Installd。
這下,好象理清一些了:
PackageManager------aidl------- > PackageManagerService的Installer ----socket--------> Installd(c程序)。
4. Installd:這是一個用c寫成的帶有main函數的守護進程,在開機之後由init.rc調用,代碼位於:framworks/base/cmds/installd目錄下。其中,最後一個字母d,代碼的就是“dameon”,事實上,在android中,定義了好多這樣的守護進程,正是由於它們的存在,android系統才能夠正常運轉起來,也正是由於整個系統中,像這種實現方式是android中到處可以看見的。關於Installd,給出一些可以想像得到的有趣的代碼片段,有興趣的讀者,可以自己去看源碼。
Installd.c:
struct cmdinfo cmds[] ={
{ "ping", 0, do_ping },
{ "install", 3, do_install },
{ "dexopt", 3, do_dexopt },
{ "movedex", 2, do_move_dex },
{ "rmdex", 1, do_rm_dex },
{ "remove", 1, do_remove },
{ "freecache", 1, do_free_cache },
{ "rmcache", 1, do_rm_cache },
{ "protect", 2, do_protect },
{ "getsize", 3, do_get_size },
{ "rmuserdata", 1, do_rm_user_data },
};
哦,這不就是從PackageManagerService的Installer 傳遞過來的命令字符串麼,那麼,還能想到什麼呢?
祝閱讀此文的朋友在android上描繪出精彩的一筆。
欣賞一下我們清爽的界面吧~如果是只用activity來制作這樣的東西簡直是太小兒科了,此處我們當然用的是service首先我們先上service的代碼:1、如果我們要訪問
假設我們現在有這麼一個需求,就是自定義一個組件,該組件由一個小圖標和圖標的文字說明組成,並且帶有背景色,背景色可設置,寬度高度可設置。如下圖所示正是兩個這樣的組件所組成。
Android剛興起的時候,著實讓一些小眾軟件火了一把,切水果,Tom貓,吹裙子就是其中的代表,當然還有實用性很強的關機重啟軟件,我們去百度上搜索一下。截圖:一.了解CM
序言現在說是換膚框架還有點誇大其詞,因為目前只實現了顏色的替換,目前網上已有的換膚框架我都研究過,主要感覺給每個View設置樣式,還要保存每個需要換膚的View,實在是太