仿QQ獲取設備中APK并分享
我和郭霖
13年,郭霖開始寫技術博客。我在CSDN的博客頻道逛著玩,剛好看到他的文章,于是點擊進去閱讀。看完之后最大的感受就是:代碼整齊,技術深厚;文章寫得圖文并茂,有條有理。從那時起,我就成了郭霖的一個粉絲,每周四早晨都等著看他的更新。誠實地說:沒有腦殘地追過星,但這么實在地喜歡看一個人寫的技術博客,這還是頭一回。其實,從他每篇文章的點擊量和評論數也可以看出:大家對郭霖非常的認可。他也常出現在CSDN的首頁,有時還無恥地連續出現幾次。去年,部門經理交給我十幾個大學畢業生,老大問我:他們適合從哪里學起?我基本沒有過腦子地說:每個人買一本《第一行代碼》,快去吧,京西十一點前下單下午就能送到。從那以后,部門來的新手,公司都會讓他們照著這本書夯實一下基礎。
嗯哼,期待郭大嬸帶給我們更多的分享。
書歸正傳。
前幾天看到同事里有一個界面絢麗的應用,覺得有點意思,就讓他把APK發給我,我想反編譯看看里面的代碼。結果,這哥們在手機里找了好一陣子,最后給我說:手機沒有root,找不到APK文件在哪里。我再讓他試試其他機子,結果都差不多:要不然找起來很麻煩,要不然根本都找不到。這時,測試的妹子說:手機QQ有這個功能。我打開手機QQ一看,果然有,平時都沒有注意到啊。
這個功能點稍作總結:
- 每個item包括:應用的icon,名字,安裝文件的大小,最后更新時間
- 點擊item分享其對應的APK文件
看到這里,心里怪癢癢的,我們也能做這么個類似的東西么?
能的!必須能!否則在測試的妹子面前怎么能抬起頭!?
我們先獲取手機中已經安裝的應用:
List<PackageInfo> packageInfoList = mPackageManager.getInstalledPackages(0);
這些應用已經都躺在這里了,我們現在就一步一步地來找出每個應用的相關信息。
(1) 獲取應用的名稱
/** * 獲取應用的名稱 */
public String getApplicationName(String packageName,PackageManager packageManager) {
String applicationName=null;
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
applicationName = (String) packageManager.getApplicationLabel(applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
}
return applicationName;
}
嗯哼,這個不難,剛上路的小司機也可以輕松的搞定。
(2) 獲取應用的icon
應用的名字我們容易獲得,那它的icon又在哪里呢?
既然獲得應用名字用的是:
packageManager.getApplicationLabel(applicationInfo);
那么是不是有類似的getApplicationIcon方法呢?趕緊試著敲一下代碼,AS提示果然有我們想要的東西:
packageManager.getApplicationIcon(ApplicationInfo info)
嗯哼,真愉快,我們猜對啦。把代碼運行起來瞅瞅,我的華為手機沒有問題。換個三星試試,也對呢;再用HTC跑跑,獲取到的居然是個小綠人——系統默認的圖標!再從測試妹子那里拿個小米過來,一樣啊,沒有正確獲取到應用對應的圖標。
不開心了,喝口水,默默地打開度娘找答案。看到這樣的方式:
//.........
Class pkgParserCls = Class.forName(PATH_PackageParser);
Class[] typeArgs = new Class[1];
typeArgs[0] = String.class;
Constructor pkgParserCt = pkgParserCls.getConstructor(typeArgs);
Object[] valueArgs = new Object[1];
valueArgs[0] = apkPath;
Object pkgParser = pkgParserCt.newInstance(valueArgs);
//.........
居然用的是反射,復雜就不說了而且還影響效率。所以,在不得已的情況下還是不要這么做了。看來想通過PackageManager獲取應用的Icon是不行了,那就換個角度從PackageInfo入手試試,看到一個字段:
public ApplicationInfo applicationInfo;
官方文檔的解釋是: Information collected from the application。也就是說這個字段包含了App的眾多信息。所以,接著看ApplicationInfo里面有啥東西,掃了一眼,看到一個東西:
//Retrieve the current graphical icon associated with this item.
public Drawable loadIcon(PackageManager pm) {
return pm.loadItemIcon(this, getApplicationInfo());
}
利用該方法才可以避免在某些機型上無法獲取應用的icon的bug。手邊的機子試了一遍,都沒問題。這個小問題解決了,就接著往下走。
(3) 獲取應用的最后更新時間
這個也挺容易的,PackageInfo中有相應的字段:
public long lastUpdateTime;
當然這個值是個毫秒值,需要利用SimpleDateFormat將其轉換成項目需要的日期格式。
有些情況下還需要獲取應用的第一次安裝時間,PackageInfo中也有相應的字段:
public long firstInstallTime;
同理,也需要對其進行格式化。
(4) 獲取Apk文件大小
要獲取Apk文件大小,首先得找到Apk文件。就像我想周末和妹子去逛街,前提是我得有個妹子啊(打住,不說了,眼淚滴到鍵盤上了)
但是它到底在哪里呢?幻想著利用PackageManager是不行的,它根本沒有類似于getApplicationApk( )的方法。
那怎么辦呢?喔,還記得前面提到的PackageInfo中的ApplicationInfo字段么?我們繼續去里面找,看看有沒有啥收獲,在源碼501行發現一個字段:
public String sourceDir;
官方文檔是這么描述的:Full path to the base APK for this application。嗯哼,bingo!找到了就是它,它代表了APK文件的完整路徑。文件路徑已經拿到了,啥都好辦了(就像知道了妹子住哪里,就可以……..)
File apkFile = new File(packageInfo.applicationInfo.sourceDir);
我們將該路徑封裝成一個文件,再獲取它的大小即可.
apkFile.length() / 1024 / 1024
在此處將文件大小轉換成了MB單位,比如豌豆莢的APK文件為6.46MB
好了,想要的東西我們都找到了,我們用一個ListView把每個APK的相關信息作為item展示出來就行了。有個小問題請注意:獲取手機中APK信息,這是一個耗時的過程,所以我們要在子線程中來做這個事情。
好了,看看做出來的效果
我們接著實現點擊item分享Apk文件:
private class ItemClickListenerImpl implements AdapterView.OnItemClickListener{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
File apkFile = mAppInfoList.get(position).getApkFile();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(apkFile));
startActivity(intent);
}
}
在此處Apk文件放到intent中再調用系統自帶的分享功能即可。
界面做出來了,功能也實現了,再回過頭來看,其實不是很難。難點在于我們不知道——不知道它的icon和Apk文件到底放在哪里了。這讓我想起了拉姆斯菲爾德的一段話:
We also know there are known unknowns; that is to say we know there
are some things we do not know. But there are also unknown unknowns –
the ones we don’t know we don’t know.
嘿嘿,我們不是不會,只是不知道罷了。
來自: http://blog.csdn.net/lfdfhl/article/details/51286284