zoukankan      html  css  js  c++  java
  • 抄360一个关键的洁净实现(一)

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

    编译环境: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.创建快捷方式

    主界面就是一个button。单击后,会发出一个广播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然后升至当前所占内存百分比的高度

    前三个比較easy不做介绍,对于水面动画,在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() {
    				@Override
    				public void run() {
    					// TODO Auto-generated method stub
    					if(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 block
    				e.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 exception
    						e.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
    {
    .....
    	@Override
    	public void run() 
    	{
    		// TODO Auto-generated method stub
    		handler.sendEmptyMessage(Value.BEGIN_CLEAN);
    		Object [] pram = cleanUtil.killRunnintAppInfo(this);
    		Message message = new Message();
    		message.obj = pram;
    		message.what = Value.UPDATE;
    		handler.sendMessage(message);
    	}
    
    	@Override
    	public void onCreate() {
    		// TODO Auto-generated method stub
    		if(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"/>

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

    由于技术水平有限,写作水平有限,如果您有任何错误,欢迎丹尼尔修正。

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    接口自动化1-基础知识
    pytest-fixture之conftest.py
    测试人员一定要懂的ADB操作,赶紧来看一看~
    必看!利用装饰器,帮你自动处理异常并优雅实现重跑case
    最全Airtest接口功能介绍和示例总结,新手同学千万不能错过呀!(二)
    总结一波 Redis 面试题,收藏起来!
    IntelliJ IDEA 2020.2.4款 神级超级牛逼插件推荐
    华为 Java 开发编程军规,谁违反谁走
    CTO:再写if-else,逮着罚款1000!
    VSCode 上竟然也能约会,谈对象了???
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4812666.html
Copyright © 2011-2022 走看看