您的位置:首页 > 移动开发

获取未安装的App信息

2015-12-25 14:01 344 查看
本篇文章属于进阶篇,纯技术分享,用来获取一个未安装的app的相关信息也就是一个apk文件。我们可以获取到应用名称,包名,应用图标,版本号,版本名称,含有四大组件中的哪些等等一切信息,听起来是不是有点小激动?没错,就是这么简单粗暴,下面就来动手实现吧!

本节知识你需要了解的类及其常用方法:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: