欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android获取应用程序大小的方法

程序员文章站 2022-06-29 09:27:47
今天碰到个问题,想获取某个已安装的包的大小,没找到合适的方法。搜索了一下,发现packagemanager里面有个getpackagesizeinfo方法,可惜是hide的...

今天碰到个问题,想获取某个已安装的包的大小,没找到合适的方法。搜索了一下,发现packagemanager里面有个getpackagesizeinfo方法,可惜是hide的,而且它执行之后,会将结果回调给ipackagestatsobserver的ongetstatscompleted方法。后来想直接计算/data/app和/system/app里面的apk大小,可是有时候会碰到权限问题,需要root才可以获取大小。        再后来,我想起系统的设置里面有一个应用程序管理,它里面列出了所有程序的占用空间大小、数据大小和缓存大小。恩,这个就是突破口。
以前写过一篇获取其他包的context ,这个东西是真有用,这个结合反射,可以做很多神奇的事情,比如今天的这个。

上代码:

java代码

复制代码 代码如下:

package chroya.demo; 

import java.lang.reflect.constructor; 
import java.lang.reflect.field; 
import java.lang.reflect.invocationtargetexception; 
import java.util.concurrent.countdownlatch; 

import android.app.activity; 
import android.content.context; 
import android.content.pm.packagestats; 
import android.content.pm.packagemanager.namenotfoundexception; 
import android.os.bundle; 
import android.os.handler; 
import android.os.message; 
import android.util.log; 

public class main extends activity { 
    private packagestats ps; 

    public void getpackagestats(string packagename) { 
        try { 
            //获取setting包的的context 
            context mmsctx = createpackagecontext("com.android.settings", 
                    context.context_include_code | context.context_ignore_security); 
            //使用setting的classloader加载com.android.settings.manageapplications类 
            class<?> maclass = class.forname("com.android.settings.manageapplications", true, mmsctx.getclassloader()); 
            //创建它的一个对象 
            object maobject = maclass.newinstance(); 

            /*
             * 将私有域mpm赋值。因为mpm在sizeobserver的invokegetsize中用到了,
             * 却因为没有执行oncreate而没有初始化,所以要在此处初始化。
             */ 
            field f_mpm = maclass.getdeclaredfield("mpm"); 
            f_mpm.setaccessible(true);             
            f_mpm.set(maobject, mmsctx.getpackagemanager()); 

            /*
             * 给mhandler赋值为重新定义的handler,以便接收sizeobserver的
             * ongetstatscompleted回调方法中dispatch的消息,从中取packagestats对象。
             * */ 
            field f_mhandler = maclass.getdeclaredfield("mhandler"); 
            f_mhandler.setaccessible(true); 
            f_mhandler.set(maobject, new handler() { 
                  public void handlemessage(message msg) { 
                      if(msg.what == 1) { 
                          //此处获取到packagestats对象 
                          ps = (packagestats) msg.getdata().getparcelable("applicationpackagestats");                          
                          log.d("", ""+ps.codesize);                           
                      } 
                  } 
            }); 

            //加载内部类sizeobserver 
            class<?> sizeobserverclass = class.forname("com.android.settings.manageapplications$sizeobserver", true, mmsctx.getclassloader()); 
            constructor sizeobserverconstructor = sizeobserverclass.getdeclaredconstructors()[0]; 
            sizeobserverconstructor.setaccessible(true); 
            /*
             * 创建sizeobserver对象,两个参数,第一个是外部类的对象,
             * 也就是manageapplications对象,第二个是msgid,也就是
             * 分发消息的id,跟handler接收的msgid一样。
             * */ 
            object soobject = sizeobserverconstructor.newinstance(maobject, 1); 
            //执行invokegetsize方法 
            sizeobserverclass.getmethod("invokegetsize", string.class, 
                    countdownlatch.class).invoke(soobject, packagename, new countdownlatch(1));          
        } catch (namenotfoundexception e) { 
            e.printstacktrace(); 
        } catch (classnotfoundexception e) { 
            e.printstacktrace(); 
        } catch (illegalaccessexception e) { 
            e.printstacktrace(); 
        } catch (illegalargumentexception e) { 
            e.printstacktrace(); 
        } catch (securityexception e) { 
            e.printstacktrace(); 
        } catch (invocationtargetexception e) { 
            e.printstacktrace(); 
        } catch (nosuchmethodexception e) { 
            e.printstacktrace(); 
        } catch (instantiationexception e) { 
            e.printstacktrace(); 
        } catch (nosuchfieldexception e) { 
            e.printstacktrace(); 
        } 
    } 

    @override 
    public void oncreate(bundle savedinstancestate) { 
        super.oncreate(savedinstancestate);   
        getpackagestats("chroya.demo");        
    } 

注释都在代码里面了,稍微理解一下应该都能懂的。
获取到packagestats对象,就可以从中获取到应用程序的占用空间大小、数据大小和缓存大小。

另,这毕竟只是hack code,不可能通用。这段代码的局限性是,只有1.5能用,而且如果别人把setting包去掉了,也没法使用。要写出各版本sdk通用的代码,就必须查看每个版本的setting包,看代码有何变化,然后根据上面给出的思路为每个版本写一个方法,就ok了。

想要获得成功,首先要自己相信自己,再者要赢得周围朋友的信任!