本篇文章属于进阶篇,纯技术分享,用来获取一个未安装的app的相关信息也就是一个apk文件。我们可以获取到应用名称,包名,应用图标,版本号,版本名称,含有四大组件中的哪些等等一切信息,听起来是不是有点小激动?没错,就是这么简单粗暴,下面就来动手实现吧!
本节知识你需要了解的类及其常用方法:
PackageItemInfo:
一般作为父类,很少直接使用,都是用其子类。
继承关系:
java.lang.Object
android.content.pm.PackageItemInfo
直接已知子类:
方法:
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();
}
}
}
扫描关注我的微信公众号: