最近做一个功能时有这样一个需求,就是要扫描本地所有已安装的App,来判断是否安装了某个App,如果没有安装,点击按钮就下载,如果已经安装,点击按钮就打开该App。这个里面主要的功能就是获取当前安装的所有APP的信息。所以我就写了一个demo,展示所有已安装App的图标和包名。

进入正题,先看效果图:

上面是扫描到的App的图标和包名。这个demo主要分两块,一块是扫描的到App信息列表,一块是用ListView展示出来。

在展示的时候,我们只用到了两个应用信息,一个是应用的图标,一个是应用的包名。所以我们先定义一个类来代表我们扫描到的App。

public class MyAppInfo {
    private Drawable image;
    private String appName;

    public MyAppInfo(Drawable image, String appName) {
        this.image = image;
        this.appName = appName;
    }
    public MyAppInfo() {

    }

    public Drawable getImage() {
        return image;
    }

    public void setImage(Drawable image) {
        this.image = image;
    }

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }
}

只有两个属性,两个构造方法,和系统生成的get ,set方法,没什么好说的,image放置图标,appName放置包名。定义好了放置应用信息的类,接下来再定义一个ApkTool的工具类,用来帮助我们获取当前所安装的App。

/**
 * Created by gray_dog3 on 16/3/3.
 * 扫描本地安装的应用,工具类
 */
public class ApkTool {
    static  String TAG = "ApkTool";
    public static List<MyAppInfo> mLocalInstallApps = null;

    public static List<MyAppInfo> scanLocalInstallAppList(PackageManager packageManager) {
        List<MyAppInfo> myAppInfos = new ArrayList<MyAppInfo>();
        try {
            List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
            for (int i = 0; i < packageInfos.size(); i++) {
                PackageInfo packageInfo = packageInfos.get(i);
                //过滤掉系统app
//            if ((ApplicationInfo.FLAG_SYSTEM & packageInfo.applicationInfo.flags) != 0) {
//                continue;
//            }
                MyAppInfo myAppInfo = new MyAppInfo();
                myAppInfo.setAppName(packageInfo.packageName);
                if (packageInfo.applicationInfo.loadIcon(packageManager) == null) {
                    continue;
                }
                myAppInfo.setImage(packageInfo.applicationInfo.loadIcon(packageManager));
                myAppInfos.add(myAppInfo);
            }
        }catch (Exception e){
            Log.e(TAG,"===============获取应用包信息失败");
        }
        return myAppInfos;
    }

}

里面就只有一个方法,根据所传的packageManager获得所有已安装的应用的包信息。然后遍历,去除没有图标的包信息,把所有图标的应用放到myAppInfos列表里。非常简单,这里注意注释掉部分,是过滤掉所有系统应用的,因为有时候我们只想知道第三方应用列表,便于管理。

最上面的mLocalInstallApps是开始留给做缓存用的,由于在扫描的时候要另外开一个线程,加缓存还牵涉到加锁,同步的问题,讲起来比较麻烦,所以暂且先不讲缓存。只讲这一个知识点。其实加上缓存代码也很简单。

好了,ApkTool写好了,我们怎么去调用呢。不上代码都是耍流氓,直接看代码:

private void initAppList(){
    new Thread(){
        @Override
        public void run() {
            super.run();
            //扫描得到APP列表
            final List<MyAppInfo> appInfos = ApkTool.scanLocalInstallAppList(MainActivity.this.getPackageManager());
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mAppAdapter.setData(appInfos);
                }
            });
        }
    }.start();
}

就这几行,我们在MainActivity里面写了这个方法,在onCreat()里去调用,初始化列表。开一个线程,然后在run()方法里面做我们想做的事,调用我们在ApkTool里写好的方法。返回封装好的,只有图标和包名的应用信息列表。这里解释下,因为扫描比较耗时,可能会引起ANR,所以开了一个子线程,去调用ApkTool来扫描应用信息。扫描到结果以后,我们要通知Adapter来更新UI,Android不允许在非UI线程里面更新UI的,所以这里用了Handler去post一个Runnable对象。如果对Handler不是很懂,可以去看下官方怎么讲。我后面也会写一篇相关的博客来专门讲Handler。还是先简单说下吧:(懂的跳过)

由于Android是移动设备,用户交互几乎全部通过屏幕,用户的全部注意力几乎都集中在屏幕上,屏幕交互卡顿,或者UI信息有丝毫的延迟,用户的感觉是很不爽的。所以Android把UI显示放到主线程里,并且强烈建议耗时操作放到子线程里。但是别的线程把事情做好了,总要通知UI,展示界面变化吧,比如新闻客户端里,连接网络加载信息的线程,把新闻从服务端拉过来了,如果它直接去操作界面,结束loading,显示内容,这样也不是不可以,但是,如果没有约束,UI展示的时序行就难保证了,各种事情做好了就来操作UI,那UI显示就乱掉了。所以android是拒绝的。但是UI线程也要知道事情做完了。所以UI线程就给子线程提供了一个交互的信使Handler。就是UI线程说,你想跟哥汇报什么事,先跟我的Handler说,我每隔一段时间再把我的Handler叫过来,取一件处理。我们在UI线程里new Handler的时候也就相当于雇了一个信使,然后子线程就可以用这个信使来给UI线程传信了。在Android里我们通过Handler可以传递一些Message,也可以通过传Runnable对象,这样Runnable的run()方法里面的内容就是我们想要在UI线程里要执行的代码。

扯完了Handler细节很多,但作用就是这个,还是建议认真看下官方文档。

关于ListView部分就不多讲了,直接贴出代码:

public class MainActivity extends AppCompatActivity {
    private ListView lv_app_list;
    private AppAdapter mAppAdapter;
    public Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv_app_list = (ListView) findViewById(R.id.lv_app_list);
        mAppAdapter = new AppAdapter();
        lv_app_list.setAdapter(mAppAdapter);
        initAppList();
    }

    private void initAppList(){
        new Thread(){
            @Override
            public void run() {
                super.run();
                //扫描得到APP列表
                final List<MyAppInfo> appInfos = ApkTool.scanLocalInstallAppList(MainActivity.this.getPackageManager());
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mAppAdapter.setData(appInfos);
                    }
                });
            }
        }.start();
    }



    class AppAdapter extends BaseAdapter {

        List<MyAppInfo> myAppInfos = new ArrayList<MyAppInfo>();

        public void setData(List<MyAppInfo> myAppInfos) {
            this.myAppInfos = myAppInfos;
            notifyDataSetChanged();
        }

        public List<MyAppInfo> getData() {
            return myAppInfos;
        }

        @Override
        public int getCount() {
            if (myAppInfos != null && myAppInfos.size() > 0) {
                return myAppInfos.size();
            }
            return 0;
        }

        @Override
        public Object getItem(int position) {
            if (myAppInfos != null && myAppInfos.size() > 0) {
                return myAppInfos.get(position);
            }
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder mViewHolder;
            MyAppInfo myAppInfo = myAppInfos.get(position);
            if (convertView == null) {
                mViewHolder = new ViewHolder();
                convertView = LayoutInflater.from(getBaseContext()).inflate(R.layout.item_app_info, null);
                mViewHolder.iv_app_icon = (ImageView) convertView.findViewById(R.id.iv_app_icon);
                mViewHolder.tx_app_name = (TextView) convertView.findViewById(R.id.tv_app_name);
                convertView.setTag(mViewHolder);
            } else {
                mViewHolder = (ViewHolder) convertView.getTag();
            }
            mViewHolder.iv_app_icon.setImageDrawable(myAppInfo.getImage());
            mViewHolder.tx_app_name.setText(myAppInfo.getAppName());
            return convertView;
        }

        class ViewHolder {

            ImageView iv_app_icon;
            TextView tx_app_name;
        }
    }

}

最后说下,如果想实时监听有app安装或者卸载,可以在资源清单里面注册监听安装和卸载app的广播,也可以在Activity里面监听,只要收到广播后再调用上面的initAppList()方法即可。如果你要实现跟我一样的功能,检测一个应用是否安装,可以在遍历的时候去拿包名比较,就可以的到结果。如果你要列出所有的系统应用或所有的非系统应用,上面注释出也给出了过滤方法。

好了,传送门:

https://github/angularzues/APPList.git



















更多推荐

获取Android手机里所有已安装的APP