獲取未安裝的App資訊
本篇文章屬於進階篇,純技術分享,用來獲取一個未安裝的app的相關資訊也就是一個apk檔案。我們可以獲取到應用名稱,包名,應用圖示,版本號,版本名稱,含有四大元件中的哪些等等一切資訊,聽起來是不是有點小激動?沒錯,就是這麼簡單粗暴,下面就來動手實現吧!
本節知識你需要了解的類及其常用方法:
PackageItemInfo:
一般作為父類,很少直接使用,都是用其子類。
繼承關係:
java.lang.Object
android.content.pm.PackageItemInfo
直接已知子類:ApplicationInfo, ComponentInfo, InstrumentationInfo, PermissionGroupInfo, PermissionInfo間接子類有:方法:
PackageManager:
可以通過Context的getPackageManager得到,可以用來安裝、解除安裝應用,查詢許可權資訊,查詢已經安裝的應用,4大元件資訊,增加、刪除許可權,清楚使用者資料、緩衝、程式碼段等。
常用方法:
PackageInfo:
繼承關係:
java.lang.Object
android.content.pm.PackageInfo
屬性:
方法:
ApplicationInfo:
ApplicationInfo是從一個特定的應用得到的資訊。這些資訊是從相對應的Androdimanifest.xml的< application>標籤中收集到的。繼承關係:
java.lang.Object
android.content.pm.PackageItemInfo
android.content.pm.ApplicationInfo
屬性:
方法:
ActivityInfo:
繼承關係:
java.lang.Object
android.content.pm.PackageItemInfo
android.content.pm.ComponentInfo
android.content.pm.ActivityInfo
屬性:
方法:
ServiceInfo:
常用屬性及方法:
ResolveInfo:
ResolveInfo這個類是通過解析一個與IntentFilter相對應的intent得到的資訊。它部分地對應於從AndroidManifest.xml的< intent>標籤收集到的資訊。繼承關係:
java.lang.Object
android.content.pm.ResolveInfo
屬性:
方法:
上面的類基本都是從manifest檔案中獲取的,來一張關係圖:
ok,東西有點多,以後我會寫幾篇文章關於使用這些類的,大家只需要大致的看一遍就好了,以後如果不清楚的可以來這裡查。今天我們就來演示一個從網上下載一個apk檔案,然後在不安裝的情況下獲取該apk檔案的應用名稱、包名、版本號、版本名稱、icon圖示。這裡我們使用前面學過的觀察者模式來監聽下載,使用handler來發送訊息,還包括檔案的操作,我們以後的文章都會慢慢的把前面的知識融合起來,如果的android知識不是很紮實或者你對那塊不懂,可以檢視我們的前面的部落格,基本都有涉及到相關知識,以後的文章都會是融合性的不再是單一的知識點。先來看下效果圖:
下面我們以效果圖來切入,一步步開發出完整的APK,首先是Activity的佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/startAnalyze"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="開始分析" />
<Button
android:id="@+id/delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="刪除下載檔案" />
</LinearLayout>
<TextView
android:id="@+id/infoText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/ll"
android:layout_marginTop="20dp"
android:lineSpacingExtra="5dp"
android:textSize="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/infoText"
android:layout_marginTop="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="20dp"
android:gravity="center"
android:text="App圖示:"
android:textColor="#976"
android:textSize="20sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/iconImgv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
</LinearLayout>
</RelativeLayout>
還記得觀察者模式嗎?android中的觀察者用到了兩個類Observer和Obserable,前一個是觀察者,後一個是被觀察者,前一個是介面,後一個是類。ok,我們定義一個工具類用來下載資料,我們不使用非同步任務,直接使用一個執行緒下載,在下載完成的時候通過觀察者通知Activity進行下一步的操作。下面是工具類的完整程式碼:
public class DownAPK extends Observable {
public static final int EXISTS = 1, ERRORRESPONDED = -1, SUCCESS = 0, MAX = 2, PROGRESS = -2;
public static File file;
public int progress, max;
public void startLoadAPK(final String apkUrl, final String savePath, String savaName) {
file = new File(savePath + File.separator + savaName);
Log.e("檔案儲存路徑:", file.getAbsolutePath());
if (!file.exists()) {
try {
file.createNewFile();
downThread(apkUrl, file);
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.length() > 0) {
Log.e("檔案存在:", savaName);
setChanged();
notifyObservers(EXISTS);
return;
} else {
downThread(apkUrl, file);
}
}
//下載執行緒
private void downThread(final String apkUrl, final File file) {
new Thread(new Runnable() {
@Override
public void run() {
FileOutputStream fos = null;
InputStream data = null;
try {
HttpURLConnection conn = (HttpURLConnection) new URL(apkUrl).openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5 * 1000);
conn.setConnectTimeout(5 * 1000);
//傳送最大值
{
setChanged();
notifyObservers(conn.getContentLength());
}
//判斷是否能正常下載
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
setChanged();
notifyObservers(ERRORRESPONDED);
return;
}
max = conn.getContentLength();
setChanged();
notifyObservers(MAX);
data = conn.getInputStream();
fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = data.read(buf)) != -1) {
progress += len;
//傳送進度
setChanged();
notifyObservers(PROGRESS);
fos.write(buf, 0, len);
fos.flush();
}
//下載成功
setChanged();
notifyObservers(SUCCESS);
} catch (IOException e) {
e.printStackTrace();
} finally {
//關流操作
if (data != null)
try {
data.close();
} catch (IOException e) {
e.printStackTrace();
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
裡面就兩個方法,一個是公共的用來給外部呼叫下載,另一個是私有的方法,我們在公共方法中主要判斷檔案是否已經存在,如果存在我們就呼叫私有的方法去下載。程式碼不難,大家稍微的看一下就懂了。關鍵點是Activity裡面的程式碼,我們需要Activity實現obverser介面,從寫update方法,這個方法程式碼如下:
//觀察到資料變化就呼叫這個方法
@Override
public void update(Observable observable, Object data) {
Message message = handler.obtainMessage();
message.obj = data;
handler.sendMessage(message);
}
這裡我們很簡單的呼叫一個handler傳送一個訊息,下面來看看handler裡面的程式碼:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch ((int) msg.obj) {
case DownAPK.ERRORRESPONDED:
Toast.makeText(MainActivity.this, "下載失敗!", Toast.LENGTH_SHORT).show();
break;
case DownAPK.EXISTS:
Toast.makeText(MainActivity.this, "檔案存在!", Toast.LENGTH_SHORT).show();
analyzing();
break;
case DownAPK.SUCCESS:
Toast.makeText(MainActivity.this, "下載成功!", Toast.LENGTH_SHORT).show();
dialog.dismiss();
analyzing();
break;
case DownAPK.PROGRESS:
dialog.setProgress(observerable.progress);
break;
case DownAPK.MAX:
initDialog(observerable.max);
break;
}
}
};
根據不同的狀態實現不同的邏輯,如果觀察到檔案存在就直接分析apk檔案,如果是更新dialog的進度條就更新進度條,這裡程式碼也很簡單,關鍵是方法analyzing(),這個方法是用來分析apk檔案的,來看看裡面的程式碼:
private void analyzing() {
//得到包管理者
PackageManager packageManager = getPackageManager();
//apk的路徑
String savePath = getFilesDir().toString() + File.separator + fileName;
//得到包的訊息
PackageInfo archiveInfo = packageManager.getPackageArchiveInfo(savePath, PackageManager.GET_ACTIVITIES);
//獲取icon圖示
if (archiveInfo != null) {
ApplicationInfo applicationInfo = archiveInfo.applicationInfo;
applicationInfo.sourceDir = savePath;
applicationInfo.publicSourceDir = savePath;
Drawable drawable = applicationInfo.loadIcon(packageManager);
if (drawable != null) {
iconImgv.setImageDrawable(drawable);
}
buffer = new StringBuffer();
{
//應用名稱
CharSequence appName = applicationInfo.loadLabel(packageManager);
//包名
String packageName = applicationInfo.packageName;
//版本名稱
String versionName = archiveInfo.versionName;
//版本號
int versionCode = archiveInfo.versionCode;
buffer.append("應用名稱:" + appName + "\r\n").append("包名:" + packageName + "\r\n")
.append("版本號:" + versionName + "\r\n").append("版本:" + versionCode + "\r\n");
//顯示分析出的資訊
infoTxtv.setText(buffer.toString());
}
} else {
Toast.makeText(MainActivity.this, "獲取失敗!", Toast.LENGTH_SHORT).show();
}
}
註釋很詳細就不多說了。最後貼出Activity的完整程式碼:
public class MainActivity extends AppCompatActivity implements Observer {
private String apkPath = "http://www.apk3.com/uploads/soft/201504/jk.apk";
private String fileName;
private TextView infoTxtv;
private Button startBtn;
private ImageView iconImgv;
private ProgressDialog dialog;
private DownAPK observerable;
private StringBuffer buffer;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch ((int) msg.obj) {
case DownAPK.ERRORRESPONDED:
Toast.makeText(MainActivity.this, "下載失敗!", Toast.LENGTH_SHORT).show();
break;
case DownAPK.EXISTS:
Toast.makeText(MainActivity.this, "檔案存在!", Toast.LENGTH_SHORT).show();
analyzing();
break;
case DownAPK.SUCCESS:
Toast.makeText(MainActivity.this, "下載成功!", Toast.LENGTH_SHORT).show();
dialog.dismiss();
analyzing();
break;
case DownAPK.PROGRESS:
dialog.setProgress(observerable.progress);
break;
case DownAPK.MAX:
initDialog(observerable.max);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//新增觀察者
observerable = new DownAPK();
observerable.addObserver(this);
fileName = apkPath.substring(apkPath.lastIndexOf("/") + 1);
init();
}
private void init() {
//初始化控制元件
infoTxtv = (TextView) findViewById(R.id.infoText);
iconImgv = (ImageView) findViewById(R.id.iconImgv);
startBtn = (Button) findViewById(R.id.startAnalyze);
startBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//開始下載
observerable.startLoadAPK(apkPath, getFilesDir().toString(), fileName);
}
});
findViewById(R.id.delete).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
File file = new File(getFilesDir().getAbsolutePath());
File[] files = file.listFiles();
boolean isDeleted = false;
for (int i = 0; i < files.length; i++) {
if (files[i].getName().equals(fileName)) {
files[i].delete();
isDeleted = true;
infoTxtv.setText("");
Toast.makeText(MainActivity.this, fileName + ",刪除成功!", Toast.LENGTH_SHORT).show();
}
}
if (!isDeleted) {
Toast.makeText(MainActivity.this, "檔案不存在!", Toast.LENGTH_SHORT).show();
}
}
});
}
//觀察到資料變化就呼叫這個方法
@Override
public void update(Observable observable, Object data) {
Message message = handler.obtainMessage();
message.obj = data;
handler.sendMessage(message);
}
private void initDialog(int Max) {
dialog = new ProgressDialog(this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMax(Max);
dialog.setMessage("下載中請稍後...");
dialog.show();
}
private void analyzing() {
//得到包管理者
PackageManager packageManager = getPackageManager();
//apk的路徑
String savePath = getFilesDir().toString() + File.separator + fileName;
//得到包的訊息
PackageInfo archiveInfo = packageManager.getPackageArchiveInfo(savePath, PackageManager.GET_ACTIVITIES);
//獲取icon圖示
if (archiveInfo != null) {
ApplicationInfo applicationInfo = archiveInfo.applicationInfo;
applicationInfo.sourceDir = savePath;
applicationInfo.publicSourceDir = savePath;
Drawable drawable = applicationInfo.loadIcon(packageManager);
if (drawable != null) {
iconImgv.setImageDrawable(drawable);
}
buffer = new StringBuffer();
{
//應用名稱
CharSequence appName = applicationInfo.loadLabel(packageManager);
//包名
String packageName = applicationInfo.packageName;
//版本名稱
String versionName = archiveInfo.versionName;
//版本號
int versionCode = archiveInfo.versionCode;
buffer.append("應用名稱:" + appName + "\r\n").append("包名:" + packageName + "\r\n")
.append("版本號:" + versionName + "\r\n").append("版本:" + versionCode + "\r\n");
//顯示分析出的資訊
infoTxtv.setText(buffer.toString());
}
} else {
Toast.makeText(MainActivity.this, "獲取失敗!", Toast.LENGTH_SHORT).show();
}
}
}
掃描關注我的微信公眾號: