android 其实就是linux 上面包装了一个java的框架. linux 系统下 所有的硬件,设备(网卡,显卡等) 都是以文件的方式来表示. 文件里面包含的有很多设备的状态信息. 所有的流量相关的信息 都是记录在文件上的. 注意:模拟器 是不支持流量查询的. adb devices列出所以设置 然后通过 :adb -s 3835197E43F100EC shell 可以进入指定的设备. proc 系统的状态信息 adb -s 3835197E43F100EC shell 所以,那些流量都是读取的这些文件的内容. 在uid_stat 的目录下有一堆文件夹 名字是以应用程序的uid作为名字的. 内容就是这个应用程序 上传和下载产生的流量信息 tcp_rcv 采用tcp协议 接收到的数据的大小 tcp receive tcp_snd 采用tcp协议 发送的数据的byte大小 snd send tcp_rcv_pkt 采用tcp协议 接收到的包的数目 本来我们应该去遍历系统的这些文件,但是谷歌工程师用C已经 封装好了. 流量信息 : 上一次开机到现在这个程序产生的流量 . 世面安全软件: 定义一个数据库 packname uid appname lasttrafficdata 手机在关机的时候 是会有一个关机的广播事件ACTION_SHUTDOWN ,然后在这个广播里面会去把这次开机期间用的流量记录下来。 下一次开机的时候. 再去展示这个流量统计的界面。 后台每隔五分钟去更新一下数据库,很显然它也是非常费电的。 流量矫正. 其实大体的流量数据. 参考 禁止某一款应用上网. root权限 原理 :使用了一个linux系统下的防火墙 iptables iptables_armv5 allow uid 10035 disallow uid 10074 iptables_armv5 需要有linux的一些核心库才运行 模拟器不支持iptables miui cymod 网上有一个开源的项目 droidwall (防火墙,) google code. 屏幕适配: 尽量使用相对布局和线性布局. 不推荐使用绝对布局. 图片,控件的大小,最好都是用dip的单位作为控件的大小. dip==dp 文件的大小使用sp hvga 48*48px qvga 32*32px wvga 60*60px 代码里面有hard code的大小 根据不同的分辨率做不同的适配. /********************************************************/ 可扩展的ListView. 把这个数据库放在资产目录上,在资产目录下的资源不会生成相应的引用. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //Get the total number of bytes received through the mobile interface. /*long mobilerx = TrafficStats.getMobileRxBytes(); //Get the total number of bytes transmitted(发送) through the mobile interface. long mobiletx = TrafficStats.getMobileTxBytes(); long mobiletotal = mobilerx + mobiletx; StringBuilder sb = new StringBuilder(); sb.append("2g/3g总流量"+TextFormater.getDataSize(mobiletotal)); sb.append("\n"); //Get the total number of bytes received through all network interfaces.(总流量 ,包含WIFI 和手机上网) long totalrx = TrafficStats.getTotalRxBytes(); long totaltx = TrafficStats.getTotalTxBytes(); long total = totalrx + totaltx; //WIFI可以用总流量减去用手机上网的流量 long wifitotal = total - mobiletotal; sb.append("wifi总流量"+TextFormater.getDataSize(wifitotal)); sb.append("\n"); TextView tView = new TextView(this); tView.setText(sb.toString()); setContentView(tView);*/ //我们应该得到的是每一个应用程序所用的流量 // 在手机里面得到所有的产生图标的应用程序 PackageManager pm = getPackageManager(); Intent intent = new Intent(); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.LAUNCHER"); List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resolveInfos) { String appname = resolveInfo.loadLabel(pm).toString(); System.out.println("appname:"+appname); Drawable icon = resolveInfo.loadIcon(pm); System.out.println("icon:"+icon.getCurrent()); String packageName = resolveInfo.activityInfo.packageName; try { PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); int uid = packageInfo.applicationInfo.uid; System.out.println("下载流量"+ TextFormater.getDataSize( TrafficStats.getUidRxBytes(uid))); System.out.println("上传流量"+ TextFormater.getDataSize( TrafficStats.getUidTxBytes(uid))); } catch (NameNotFoundException e) { e.printStackTrace(); } System.out.println("------"); } } 抽屉最重要的两个属性: android:handle="@+id/handle" android:content="@+id/content" 一个是把手,另外一个是拖动把手显示的内容. <SlidingDrawer android:layout_width="match_parent" android:layout_height="match_parent" android:handle="@+id/handle" android:content="@+id/content" android:orientation="horizontal" > <ImageView android:id="@id/handle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/notification" /> <ListView android:id="@id/content" android:layout_width="fill_parent" android:layout_height="fill_parent" > </ListView> </SlidingDrawer> ListView有一个非常好的方法,为自己增加标题. lv_content.addHeaderView(View.inflate(this, R.layout.traffic_title, null)); gravity:是指它里面的控件显示的位置. layout_gravity是指自己处在父窗体的什么位置. 为了让这些列表在用户看的时间是实时可变的与桌面那个看内存一样, 所以我们用一个计时器,什么时间开始呢,应该在用户看到它的时间 即onStart()方法里面,什么时间结束呢?onstop()里面吧。 很多应用程序 都会在sd卡上生成一些临时文件 1.清理sd卡的临时文件*(它是存储了每个软件对应的缓存的目录) 第一步 获取手机里面安装的apk信息 第二步 查询apk信息是否在softdetail表中有对应关系 删除sd卡上这个缓存目录 2.获取每个应用程序的缓存 提示用户去清除这个缓存 操,太神奇了,用安卓自带的list,直接用getListView()就可 以了。绝对是一个惊天技巧。 eclispss》??? <ListView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@android:id/list" /> 这是用了安卓自带的id. 你在java文件加获取这个对象的时间非常简单。 ListView lv = getListView();不过这个activity要继承ListActivity 这个表示文字的显示不完要横排。 android:ellipsize="marquee" 跳转到程序的清理缓存界面: Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); intent.addCategory("android.intent.category.DEFAULT"); intent.addCategory("android.intent.category.VOICE_LAUNCH"); CacheInfo info = (CacheInfo) lv.getItemAtPosition(position); intent.putExtra("pkg", info.getPackname()); startActivity(intent); 删除所有文件,使用递归: private void deleteDir(File file){ if(file.isDirectory()){ File[] files = file.listFiles(); for(int i=0;i<files.length;i++){ deleteDir(file); } }else{ file.delete(); } } Message msg = Message.obtain();用这个效率更高。这个例子基本把所有的进度框的难点都涉及的, 只要理解它,你基本不会存在任何问题。 特别好的一个东西: 我们在看到清理内存时间那种显示的进度,并且在清理什么什么的。 其实它就是在不断刷新界面。 package cn.itcast.clear; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import android.app.Activity; import android.content.pm.PackageInfo; import android.content.res.AssetManager; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; public class DemoActivity extends Activity { private TextView tv; private ProgressBar pb; private SQLiteDatabase db; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); String text = (String) msg.obj; tv.setText(text); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView) this.findViewById(R.id.tv); pb = (ProgressBar) this.findViewById(R.id.progressBar1); // 判断手机内存里面是否有数据库存在 File file = new File("/data/data/cn.itcast.clear/files/clearpath.db"); if (!file.exists()) { copyfile(); } // 文件写到哪里了? // data/data/cn.itcast.clear/files/name } public void start(View view) { db = SQLiteDatabase.openDatabase("/data/data/cn.itcast.clear/files/clearpath.db", null, SQLiteDatabase.OPEN_READONLY); new Thread(){ @Override public void run() { List<PackageInfo> packinfos = getPackageManager().getInstalledPackages(0); pb.setMax(packinfos.size());// 设置进度条的最大条目个数 int total=0; for(PackageInfo info : packinfos){ String packname = info.packageName; Cursor curosr = db.rawQuery("select filepath from softdetail where apkname=?", new String[]{packname}); if(curosr.moveToFirst()){ String path = curosr.getString(0); System.out.println("清除"+packname+"sd卡缓存"+path); File file = new File(Environment.getExternalStorageDirectory(),path); deleteDir(file); try { sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } total++; pb.setProgress(total); curosr.close(); Message msg = Message.obtain(); msg.obj = "清除"+packname; handler.sendMessage(msg); } Message msg = Message.obtain(); msg.obj = "清除完毕"; handler.sendMessage(msg); db.close(); } }.start(); } private void copyfile() { try { InputStream is = getClass().getClassLoader().getResourceAsStream( "clearpath.db"); OutputStream fos = this.openFileOutput("clearpath.db", MODE_PRIVATE); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } private void deleteDir(File file){ if(file.isDirectory()){ File[] files = file.listFiles(); for(int i=0;i<files.length;i++){ deleteDir(file); } }else{ file.delete(); } } }