获取未安装的App信息
2015-12-25 14:01
344 查看
本篇文章属于进阶篇,纯技术分享,用来获取一个未安装的app的相关信息也就是一个apk文件。我们可以获取到应用名称,包名,应用图标,版本号,版本名称,含有四大组件中的哪些等等一切信息,听起来是不是有点小激动?没错,就是这么简单粗暴,下面就来动手实现吧!
本节知识你需要了解的类及其常用方法:
PackageItemInfo:
一般作为父类,很少直接使用,都是用其子类。
继承关系:
方法:
PackageManager:
可以通过Context的getPackageManager得到,可以用来安装、卸载应用,查询权限信息,查询已经安装的应用,4大组件信息,增加、删除权限,清楚用户数据、缓冲、代码段等。
常用方法:
PackageInfo:
继承关系:
java.lang.Object
属性:
方法:
ApplicationInfo:
ApplicationInfo是从一个特定的应用得到的信息。这些信息是从相对应的Androdimanifest.xml的< application>标签中收集到的。
继承关系:
属性:
方法:
ActivityInfo:
继承关系:
属性:
方法:
ServiceInfo:
常用属性及方法:
ResolveInfo:
ResolveInfo这个类是通过解析一个与IntentFilter相对应的intent得到的信息。它部分地对应于从AndroidManifest.xml的< intent>标签收集到的信息。
继承关系:
属性:
方法:
上面的类基本都是从manifest文件中获取的,来一张关系图:
ok,东西有点多,以后我会写几篇文章关于使用这些类的,大家只需要大致的看一遍就好了,以后如果不清楚的可以来这里查。今天我们就来演示一个从网上下载一个apk文件,然后在不安装的情况下获取该apk文件的应用名称、包名、版本号、版本名称、icon图标。这里我们使用前面学过的观察者模式来监听下载,使用handler来发送消息,还包括文件的操作,我们以后的文章都会慢慢的把前面的知识融合起来,如果的android知识不是很扎实或者你对那块不懂,可以查看我们的前面的博客,基本都有涉及到相关知识,以后的文章都会是融合性的不再是单一的知识点。先来看下效果图:
下面我们以效果图来切入,一步步开发出完整的APK,首先是Activity的布局文件:
还记得观察者模式吗?android中的观察者用到了两个类Observer和Obserable,前一个是观察者,后一个是被观察者,前一个是接口,后一个是类。ok,我们定义一个工具类用来下载数据,我们不使用异步任务,直接使用一个线程下载,在下载完成的时候通过观察者通知Activity进行下一步的操作。下面是工具类的完整代码:
里面就两个方法,一个是公共的用来给外部调用下载,另一个是私有的方法,我们在公共方法中主要判断文件是否已经存在,如果存在我们就调用私有的方法去下载。代码不难,大家稍微的看一下就懂了。关键点是Activity里面的代码,我们需要Activity实现obverser接口,从写update方法,这个方法代码如下:
这里我们很简单的调用一个handler发送一个消息,下面来看看handler里面的代码:
根据不同的状态实现不同的逻辑,如果观察到文件存在就直接分析apk文件,如果是更新dialog的进度条就更新进度条,这里代码也很简单,关键是方法analyzing(),这个方法是用来分析apk文件的,来看看里面的代码:
注释很详细就不多说了。最后贴出Activity的完整代码:
到这里都完成了,我们开发都是用别人封装好的API就可以了,能否开发出来一个好的app文件关键是我们队API的熟练程度和我们的知识面,没开发一个app都会遇到各种的问题,我们在解决问题的时候慢慢的成长起来,为了做一个优秀的软件工程师而不是一只程序员,我们的路还有很长,不骄不躁,少玩游戏多看书,相信我们定能得到我们想要的那份荣耀!最后送上demo福利:demo
本节知识你需要了解的类及其常用方法:
PackageItemInfo:
一般作为父类,很少直接使用,都是用其子类。
继承关系:
java.lang.Object android.content.pm.PackageItemInfo
直接已知子类:ApplicationInfo, ComponentInfo, InstrumentationInfo, PermissionGroupInfo, PermissionInfo间接子类有:ActivityInfo、ProviderInfo、ServiceInfo。 属性:
方法:
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(); } }
}
到这里都完成了,我们开发都是用别人封装好的API就可以了,能否开发出来一个好的app文件关键是我们队API的熟练程度和我们的知识面,没开发一个app都会遇到各种的问题,我们在解决问题的时候慢慢的成长起来,为了做一个优秀的软件工程师而不是一只程序员,我们的路还有很长,不骄不躁,少玩游戏多看书,相信我们定能得到我们想要的那份荣耀!最后送上demo福利:demo
相关文章推荐
- 小谈APP产品设计
- Swift ?? 符号的使用
- 一些Xcode升级后的小技能
- Android 系统编译 1
- ios触摸事件四:触摸
- 很不错的GCD线程 使用和介绍
- 转 Android 系统编译
- android 旋转动画
- sharepreference不能更改默认路径,随着APP卸载删除,不适合调试保存的数据
- RxJava与RxAndroid 接收消息通知
- android studio 的奇葩环境问题
- Android 中的自定义开关按钮
- Mybatis3源码分析(19)-Mapper生成过程-示例
- iOS Animation开发简单动画
- 【Android】Android Studio 快速打开Github上的Demo
- Android Service在bindService不能触发onServiceConnected方法
- Debug的方式
- [IOS 开发] GCD学习 dispatch_barrier_async
- Android进程间通信之Messenger浅析
- 从IC从业者痛苦的转APP开发者的痛苦历程---起步篇