仿360一键清理实现(一)

来源:互联网 发布:安卓卫星电视软件 编辑:程序博客网 时间:2024/06/05 16:08

---------------------------------------------------------------------

编译环境:Android 4.0

测试环境:Android 4.2.2模拟器

屏幕分辨率:480*800

作者:疯狂小强

注意:

1.资源采集于网上,如有侵权请及时联系,以便处理。

2.代码仅用于学习交流,请勿商业化。

--------------------------------------------------------------------


先上部分效果图:

“一键清理”是一个桌面图标,点击图标后,显示一个视图,进行清理动画,之后显示清理了几个进程,释放了多少M内存,点击“设置过滤名单”启动另外一个Activity编辑过滤名单。

AndroidManifest.xml如下:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.tang.demo360"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="19" />    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@android:style/Theme.Black.NoTitleBar" >        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <activity android:name=".CleanActivity"            android:theme="@style/MTheme">            <intent-filter>                <action android:name="android.intent.action.MAIN" />            </intent-filter>        </activity>        <service android:name=".CleanService"> </service>        <activity android:name=".SetWhiteListActivity"></activity>    </application></manifest>


1.创建快捷方式

主界面就是一个按钮,单击后,会发出一个广播com.android.launcher.action.INSTALL_SHORTCUT创建一个快捷方式,这需要权限

<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>


                   图一                                              图二

private void createShortcut(){ Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");              // 快捷方式的名字 String name = getResources().getString(R.string.app_name);     shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME,name);     //不允许重复创建 shortcut.putExtra("duplicate", false);   Intent shortcutIntent = new Intent(); ComponentName componentName = new ComponentName(getPackageName(), "com.tang.demo360.CleanActivity"); shortcutIntent.setComponent(componentName); shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); ShortcutIconResource iconRes=null;             //快捷方式的图标                 iconRes = Intent.ShortcutIconResource.fromContext(this, R.drawable.shortcut_process_clear_shortcut_icon); shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes);  sendBroadcast(shortcut);  Log.i("AAA", "sendBroadcast : INSTALL_SHORTCUT");}

创建出来的这个快捷方式不是打开图一界面,而是打开图二的。

从createShortcut()方法代码上看

ComponentName componentName = new ComponentName(getPackageName(), "com.tang.demo360.CleanActivity");shortcutIntent.setComponent(componentName);
打开了com.tang.demo360.CleanActivity这个Activity。但事实上只做了这些还不够,我们必须为CleanActivity在manifest中配置Action:

<action android:name="android.intent.action.MAIN" />这两者配合使用就可以直接启动CleanActivity了。



2.确定打开CleanActivity的位置

由效果图我们知道:快捷方式所在的位置不同CleanActivity的位置也是不同的,点击桌面快捷方式启动Activity的时候,我们打“ActivityManager”的logcat会发现如下信息:


其中:bnds=[510,282][679,399]好象是个坐标,到Launcher的源码上一看发现:

public void onClick(View v){........Object tag = v.getTag();        if (tag instanceof ShortcutInfo) {            // Open shortcut            final Intent intent = ((ShortcutInfo) tag).intent;            int[] pos = new int[2];            v.getLocationOnScreen(pos);            intent.setSourceBounds(new Rect(pos[0], pos[1], pos[0]                    + v.getWidth(), pos[1] + v.getHeight()));            boolean success = startActivitySafely(intent, tag);......}
点击快捷方式的时候的确是吧快捷方式的位置放在一个Rect中传出去了。


位置得到了接下来就只要把Activity的一些style改一下,然后把这个位置数据用上,就可以得到效果图上面的效果了

    <style name="MTheme" parent="@android:style/Theme">         <item name="android:windowNoTitle">true</item>         <item name="android:windowBackground">@android:color/transparent</item>       <item name="android:windowIsFloating">true</item>         <item name="android:backgroundDimAmount">0</item>    </style>
android:backgroundDimAmount   调节背景灰度

android:windowIsFloating  悬浮窗口,表示浮在屏幕上的,如果在这里使用了,整个layout就会在 屏幕中心,相当于浮在屏幕上。

Rect rect = getIntent().getSourceBounds();rootView = new CleanView(this,rect);setContentView(rootView);WindowManager.LayoutParams lp = getWindow().getAttributes();WindowManager windowManager = getWindowManager();int w = windowManager.getDefaultDisplay().getWidth();int h = windowManager.getDefaultDisplay().getHeight();lp.x = lp.x+(rect.left-w/2)+Value.WIDTH/2;lp.y = lp.y+(rect.top-h/2)+Value.HEIGHT/2;getWindow().setAttributes(lp);

由于使用悬浮窗后坐标原点也在屏幕中心,和我们所得到的Rect中的参数的左边原点不同,所以需要做位置关系转化

lp.x = lp.x+(rect.left-w/2)+Value.WIDTH/2;

lp.y = lp.y+(rect.top-h/2)+Value.HEIGHT/2;


3.CleanView布局的实现

为了实现效果图上面的效果,将CleanView分成两部分

part1.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:background="@drawable/shortcut_process_clear_cover">   <ImageView        android:id="@+id/image"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/shortcut_process_clear_fg" /> <com.tang.demo360.view.LevelView     android:id="@+id/text0"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:gravity="center"     android:layout_centerHorizontal="true"     android:layout_centerVertical="true"     android:textColor="#ffffff"     android:text="10%"      android:background="@drawable/task_killer"/></RelativeLayout>

part2.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:orientation="vertical"     android:gravity="center">    <TextView            android:id="@+id/text1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="清理进程0个" />                <TextView            android:id="@+id/text2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="释放内存0M" />        <Button            android:id="@+id/button"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="设置过滤名单"               android:background="@drawable/shortcut_process_clear_wlist"/></LinearLayout>
再根据快捷方式所处的位置判断先把哪部分加入到父容器中

view =  new LinearLayout(context);view.setOrientation(LinearLayout.HORIZONTAL);view.setBackgroundResource(R.drawable.shortcut_process_clear_bg);WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);int screenWidth = windowManager.getDefaultDisplay().getWidth();if(Value.WIDTH+rect.left+50>screenWidth){view.addView(view2);view.addView(view1);}else{view.addView(view1);view.addView(view2);}addView(view);

4.CleanView动画实现

出现方式:由无到有

退出方式:由有到无

清理过程动画:part1.xml ImageView不断旋转,速度由慢变快再变慢最后消失。

水面动画:先由未清理所占内存的百分比高度降低到0然后升至当前所占内存百分比的高度

前三个比较容易不做介绍,对于水面动画,在CleanView中有

public void updateView(Object [] parm){text1.setText("清理进程"+(Integer)parm[0]+"个");DecimalFormat decimalFormat=new DecimalFormat("0.0");String temp=decimalFormat.format(parm[1]);text2.setText("释放内存"+temp+"M");setLevelAnimation((Integer)parm[2]);}public void setLevelAnimation(int level){ClipDrawable clip = (ClipDrawable) text0.getBackground();text0.setText(level+"%");clip.setLevel(level*100);}

有2个方法负责快速更新以便达到动画效果。

水面的背景

<?xml version="1.0" encoding="utf-8"?>  <clip xmlns:android="http://schemas.android.com/apk/res/android"       android:drawable="@drawable/shortcut_process_clear_level"      android:clipOrientation="vertical"    android:gravity="bottom">  </clip> 

使用ClipDrawable代表从其它位图上截取一个图片片段setLevel方法设置截取百分比,我在主线程中使用Timer来快速更新这个数值

timer = new Timer();

timer.schedule(task, 500, 1);

task = new TimerTask() {@Overridepublic void run() {// TODO Auto-generated method stubif(isDown){level = (level-Value.V)>=0?(level-Value.V):0;if(level ==0){isDown = false;}}else{level = (level+Value.V)<=newlevel?(level+Value.V):newlevel;}parm[2] =level/100;handler.sendEmptyMessage(Value.UPDATE_VIEW);}};

这样只要在主线程中接受Value.UPDATE_VIEW这个Message然后调用updateView()方法就可以目的动画效果。


5.我们来杀进程吧

对于这个我在网上查了一些资料写了一个工具类

CleanUtil.java

package com.tang.util;import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.lang.reflect.Method;import java.util.List;import android.app.Activity;import android.app.ActivityManager;import android.content.Context;import android.util.Log;public class CleanUtil {//关闭当前运行的进程public Object [] killRunnintAppInfo(Context context) {MSaveList mSaveList = new MSaveList(context.getSharedPreferences("demo360", Activity.MODE_PRIVATE));List<String> list = mSaveList.load();ActivityManager mActivityManager = (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);List<ActivityManager.RunningAppProcessInfo> mRunningProcess = mActivityManager.getRunningAppProcesses();int appSize = getRunningTasksSize(context);long memory = getUesdMemory(context);for (ActivityManager.RunningAppProcessInfo amProcess : mRunningProcess){if(amProcess.processName.equals("com.tang.demo360")||amProcess.processName.startsWith("system")){Log.d("AAA", "跳过不杀的进程:" + amProcess.processName);continue;}else {if(isInWhiteList(amProcess.processName,list)){Log.d("AAA", "跳过不杀的进程:" + amProcess.processName);}else{mActivityManager.killBackgroundProcesses(amProcess.processName);Log.d("AAA", "杀掉的进程:"+amProcess.processName);}}}appSize = Math.abs(appSize -getRunningTasksSize(context));memory = Math.abs(memory -getUesdMemory(context));return getRecycleMemoryInfo(context,appSize,memory);}//强制关闭进程private  void forceKillApp(ActivityManager am, String packageName) {Method forceStopPackage = null;try {forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage", String.class);forceStopPackage.setAccessible(true);  forceStopPackage.invoke(am, packageName);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}  }/** * 将要传出去的数据 * 杀了多少进程 * 释放多少M内存 * 当前内存百分比 * @param context * @param appSize * @param memory * @return */private Object [] getRecycleMemoryInfo(Context context,int appSize,long memory) {         Object[] pram=new Object[]{0,0,0};;        if(memory>=0)        {          pram[0] = appSize;        pram[1] = (memory/1024.0);        pram[2] = getUesdMemoryRate(context);        }        return pram;    } private int getRunningTasksSize(Context context) { ActivityManager am = (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);     return am.getRunningAppProcesses().size(); } /** * 得到设备的所有RAM * @return 返回所有内存大小,单位:kb */private int getAllMemory() {String filePath = "/proc/meminfo";int ram = 0;FileReader fr = null;BufferedReader localBufferedReader = null;try {fr = new FileReader(filePath);localBufferedReader = new BufferedReader(fr, 8192);String line = localBufferedReader.readLine();int a = line.length() - 3;int b = line.indexOf(' ');String str = line.substring(b, a);while (str.substring(0, 1).equals(" ")) {str = str.substring(1, str.length());}ram = Integer.parseInt(str);} catch (IOException e) {e.printStackTrace();} finally {try {fr.close();localBufferedReader.close();} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}return ram;}/** * 得到设备的可用RAM * @return 返回所有内存大小,单位:kb */private long getAvailMemory(Context context) {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();am.getMemoryInfo(mi);return mi.availMem / 1024;}/** * 得到设备的已用RAM * @return 返回所有内存大小,单位:kb */private long getUesdMemory(Context context) {return getAllMemory() - getAvailMemory(context);}public int getUesdMemoryRate(Context context){return (int) (getUesdMemory(context)*100/getAvailMemory(context));}/** * 判断是否在白名单之内 * @param pkg * @param list * @return */private boolean isInWhiteList(String pkg,List<String> list){boolean inOrNot = false;if(list!=null){for(int i=0;i<list.size();i++){if(pkg.equals(list.get(i))){inOrNot = true;break;}}}return inOrNot;}}
在启动CleanActivity的时候,在onCreate中启动了CleanService,热这个实现了Runnable接口

public class CleanService extends Service implements Runnable{.....@Overridepublic void run() {// TODO Auto-generated method stubhandler.sendEmptyMessage(Value.BEGIN_CLEAN);Object [] pram = cleanUtil.killRunnintAppInfo(this);Message message = new Message();message.obj = pram;message.what = Value.UPDATE;handler.sendMessage(message);}@Overridepublic void onCreate() {// TODO Auto-generated method stubif(CleanActivity.local!=null){handler = CleanActivity.local.getHandler();new Thread(this).start();cleanUtil = new CleanUtil();}super.onCreate();}....}
将杀进程的工作放在后台进行。

当然杀进程也是需要权限的:

<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>

第一部分就这样了,第二部分分析白名单处理

由于技术水平有限,写文章的水平更是有限,如有什么错误,欢迎大牛指正。

7 0