[Java] 纯文本查看 复制代码
/**
* 获取进程管理需要所有的进程
*
* @param context
* @return
*/
public static List<ProcessBean> getProcesses(Context context) {
List<ProcessBean> datas = new ArrayList<ProcessBean>();
// 用来记录描述一个应用对应多个进程
Map<String, List<ProcessBean>> map = new HashMap<String, List<ProcessBean>>();
// 获得运行的进程
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
// 遍历当前所有进程
for (RunningAppProcessInfo info : processes) {
// 进程名称
String processName = info.processName;
// 进程占用的内存大小
int pid = info.pid;
MemoryInfo memoryInfo = am.getProcessMemoryInfo(new int[] { pid })[0];
long memory = memoryInfo.getTotalPss() * 1024;
// 进程对应的应用,一个进程可以对应多个应用
String[] pkgList = info.pkgList;
for (String pkgName : pkgList) {
ProcessBean bean = new ProcessBean();
bean.processName = processName;
bean.memory = memory;
// bean.pkg = null;
bean.pid = pid;
List<ProcessBean> list = map.get(pkgName);
if (list == null) {
// 没有进程信息
list = new ArrayList<ProcessBean>();
map.put(pkgName, list);
}
list.add(bean);
}
}
PackageManager pm = context.getPackageManager();
// 循环数据
for (Map.Entry<String, List<ProcessBean>> me : map.entrySet()) {
String packageName = me.getKey();
List<ProcessBean> list = me.getValue();
try {
PackageInfo info = pm.getPackageInfo(packageName, 0);
PkgBean pkg = new PkgBean();
pkg.icon = PackageUtils.getAppIcon(context, info);
pkg.name = PackageUtils.getAppName(context, info);
pkg.packageName = packageName;
for (ProcessBean bean : list) {
bean.pkg = pkg;
}
datas.addAll(list);
} catch (Exception e) {
e.printStackTrace();
}
}
return datas;
}
[Java] 纯文本查看 复制代码
private class ProcessAdapter extends BaseAdapter implements
StickyListHeadersAdapter {
@Override
public int getCount() {
if (mDatas != null) {
return mDatas.size();
}
return 0;
}
@Override
public Object getItem(int position) {
if (mDatas != null) {
return mDatas.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemViewHolder holder = null;
if (convertView == null) {
// 没有复用
convertView = View.inflate(ProcessManagerActivity.this,
R.layout.item_process, null);
holder = new ItemViewHolder();
convertView.setTag(holder);
holder.tvProcessName = (TextView) convertView
.findViewById(R.id.item_process_tv_name);
holder.tvProcessSize = (TextView) convertView
.findViewById(R.id.item_process_tv_size);
} else {
holder = (ItemViewHolder) convertView.getTag();
}
// 数据设置
ProcessBean bean = mDatas.get(position);
holder.tvProcessName.setText(bean.processName);
holder.tvProcessSize.setText("占用:"
+ Formatter.formatFileSize(ProcessManagerActivity.this,
bean.memory));
return convertView;
}
@Override
public View getHeaderView(int position, View convertView,
ViewGroup parent) {
HeaderViewHolder holder = null;
if (convertView == null) {
// 没有复用
convertView = View.inflate(ProcessManagerActivity.this,
R.layout.item_process_header, null);
holder = new HeaderViewHolder();
convertView.setTag(holder);
holder.ivIcon = (ImageView) convertView
.findViewById(R.id.item_ph_iv_icon);
holder.tvName = (TextView) convertView
.findViewById(R.id.item_ph_tv_name);
holder.cbChecked = (CheckBox) convertView
.findViewById(R.id.item_ph_cb_checked);
} else {
holder = (HeaderViewHolder) convertView.getTag();
}
// 数据设置
ProcessBean processBean = mDatas.get(position);
PkgBean pkg = processBean.pkg;
if (pkg.icon == null) {
holder.ivIcon.setImageResource(R.drawable.ic_default);
} else {
holder.ivIcon.setImageDrawable(pkg.icon);
}
holder.tvName.setText(pkg.name);
// 设置选择框选中
holder.cbChecked.setChecked(pkg.isChecked);
// 是否显示checkbox,判断是否是当前的应用
if (pkg.packageName.equals(getPackageName())) {
// 说明是当前的应用
holder.cbChecked.setVisibility(View.GONE);
} else {
holder.cbChecked.setVisibility(View.VISIBLE);
}
return convertView;
}
@Override
public long getHeaderId(int position) {
ProcessBean bean = mDatas.get(position);
PkgBean pkg = bean.pkg;// 表示唯一的应用程序
// pkg.packageName --> hashcode
return pkg.hashCode();
}
}
private static class HeaderViewHolder {
ImageView ivIcon;
TextView tvName;
CheckBox cbChecked;
}
private static class ItemViewHolder {
TextView tvProcessName;
TextView tvProcessSize;
}
[Java] 纯文本查看 复制代码
public class LetterToast {
private WindowManager mWM;
private View mView;
private TextView mTvLetter;
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
private String mLastLetter = "";
private Handler mHandler = new Handler();
private Runnable mHiddenTask = new Runnable() {
@Override
public void run() {
if (mView.getParent() != null) {
mWM.removeView(mView);
}
}
};
public LetterToast(Context context) {
// 初始化WindowManager
mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// 加载自定义Toast的样式
mView = View.inflate(context, R.layout.toast_letter, null);
mTvLetter = (TextView) mView.findViewById(R.id.toast_tv_letter);
// param初始化
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
// params.windowAnimations =
// com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;// 不可以被触摸的
// params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;//
// 可以被触摸的,可以显示在打电话界面上方的
params.setTitle("Toast");
}
public void show(String letter) {
if (mLastLetter.equals(letter)) {
return;
}
// 显示 正在显示时,有可能出现移除显示,需要取消掉上一次自动关闭操作
mTvLetter.setText(letter);
if (mView.getParent() == null) {
mWM.addView(mView, mParams);
}
// 取消操作
mHandler.removeCallbacks(mHiddenTask);
// 显示后得自动延时关闭
mHandler.postDelayed(mHiddenTask, 1500);
}
}
条目全选反选的实现前面我们已经手机中的所有进程显示出来,下面我们要开始进行进程的清理操作;界面下方有两个按钮:全选和反选,右上角有一个杀死进程的按钮;以及listview每一个item条目的点击这些事件需要全部进行处理:
[Java] 纯文本查看 复制代码
private void initEvent() {
mListView.setOnScrollListener(this);
// 设置 item条目的点击
mListView.setOnHeaderClickListener(this);
// button
mBtnAll.setOnClickListener(this);
mBtnReverse.setOnClickListener(this);
// 清理
mIvClean.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v == mBtnAll) {
clickAll();
} else if (v == mBtnReverse) {
clickReverse();
} else if (v == mIvClean) {
clickClean();
}
}
首先为了实现应用程序的勾选操作我们需要用一个集合将当前所有的应用程序装载起来,在loadListview()中数据排序后可以实现:
接下来实现布局中全选按钮的点击事件,同时需要注意,避免将当前的黑马手机卫士进程选中,我们需要进行判断,具体的代码如下所示:
[Java] 纯文本查看 复制代码
/**全选*/
private void clickAll() {
if (mDatas == null || mDatas.size() == 0) {
return;
}
// 将所有的checkbox选中
// 找到所有的pkgbean,设置数据ischecked--》true
for (PkgBean bean : mPkgs) {
// 如果是当前的应用,就不让选中
if (bean.packageName.equals(getPackageName())) {
continue;
}
bean.isChecked = true;
}
// UI更新
mAdapter.notifyDataSetChanged();
}
接着,实现反选按钮的点击事件,具体的代码如下所示:
[Java] 纯文本查看 复制代码
/**反选*/
private void clickReverse() {
if (mDatas == null || mDatas.size() == 0) {
return;
}
// 如果是已经选中,就不选中,否则相反
for (PkgBean bean : mPkgs) {
if (bean.packageName.equals(getPackageName())) {
continue;
}
bean.isChecked = !bean.isChecked;
}
// UI更新
mAdapter.notifyDataSetChanged();
}
全选和反选操作完毕后都调用了mAdapter.notifyDataSetChanged()来刷新界面;所以在adapter的getViewHeader中需要判断当前pkgbean的checked状态来是否勾选以及是否是当前应用程序来进行显示和隐藏:
清理按钮的实现
接下来,我们需要实现清理按钮的点击事件,想要杀死一个应用程序肯定需要使用ActivityManager系统服务来操作;这里先在ProcessProvider中添加好一个静态的方法:
[Java] 纯文本查看 复制代码
/**
* 杀死应用的进程
*
* @param context
* @param packageName
*/
public static void killProcess(Context context, String packageName) {
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
am.killBackgroundProcesses(packageName);
}
那么清理按钮的点击事件中的操作如下:
[AppleScript] 纯文本查看 复制代码
/**杀死选中的应用进程*/
private void clickClean() {
if (mDatas == null || mDatas.size() == 0) {
return;
}
int killCount = 0;
long releaseMemory = 0;
Map<Integer, Long> map = new HashMap<Integer, Long>();
Iterator<PkgBean> iterator = mPkgs.iterator();
while (iterator.hasNext()) {
PkgBean pkg = iterator.next();
// 找到选中的应用
if (pkg.isChecked) {
// 杀死应用
ProcessProvider.killProcess(this, pkg.packageName);
// 内存移除
iterator.remove();
ListIterator<ProcessBean> listIterator = mDatas.listIterator();
while (listIterator.hasNext()) {
ProcessBean bean = listIterator.next();
if (bean.pkg.packageName.equals(pkg.packageName)) {
// 移除
listIterator.remove();
// 获得进程的内存
long memory = bean.memory;
map.put(bean.pid, memory);
}
}
}
}
// 计算杀死的个数和释放的内存
for (Map.Entry<Integer, Long> me : map.entrySet()) {
releaseMemory += me.getValue();
}
killCount = map.size();
// UI更新
mAdapter.notifyDataSetChanged();
if (killCount == 0) {
Toast.makeText(this, "没有可杀死的进程", Toast.LENGTH_SHORT).show();
} else {
String text = "杀死了" + killCount + "个进程,释放了"
+ Formatter.formatFileSize(this, releaseMemory) + "内存";
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
// 顶部ui改变
mRunningProcessCount -= killCount;
loadProcess();
mUsedMemory -= releaseMemory;
loadMemory();
}
}
在上面代码清理应用进程的过程中也需要统计当前杀死了多少个进程以及多少内存,界面上方的数据需要更新,以及通过吐司提示用户。
另外,需要注意的是,清理手机进程需要添加一个权限,如下所示。
[XML] 纯文本查看 复制代码
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
锁屏自动清理
当设置界面中的锁屏清理进程按钮开启时,就会打开进程清理的服务,在该服务中注册了监听屏幕锁屏的广播接收者,当屏幕锁屏时该广播接收到屏幕锁屏的消息后会自动清理进程。
首先在设置界面添加一个锁屏自动清理的条目:
布局以及控件的初始化操作都跟归属地显示设置一样,这里就不再贴代码了;主要是开启按钮的时候需要开启的服务AutoCleanService,在服务中动态注册广播监听锁屏的操作:
[Java] 纯文本查看 复制代码
public class AutoCleanService extends Service {
private static final String TAG = "AutoCleanService";
private ScreenOffReceiver mReceiver;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "自动清理服务开启");
// 锁屏时自动清理服务
// 监听锁屏
// 注册广播
mReceiver = new ScreenOffReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);// 锁屏的行为
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(mReceiver, filter);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "自动清理服务关闭");
// 注销
unregisterReceiver(mReceiver);
}
private class ScreenOffReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 接受到锁屏
// 杀死进程
List<ProcessBean> processes = ProcessProvider.getProcesses(context);
for (ProcessBean bean : processes) {
if (bean.pkg.packageName.equals(context.getPackageName())) {
// 不杀死自己
continue;
}
ProcessProvider.killProcess(context, bean.pkg.packageName);
}
}
}
}
不被杀死的前台服务
为了让我们的应用做的更加完善更加健壮,以至于不被其他一些杀毒软件在清理进程的时候给杀死;可以让我们的应用做成一个前台进程;即开启一个前台的服务在后台一直运行。
创建一个服务ProtectingService
[Java] 纯文本查看 复制代码
public class ProtectingService extends Service {
private int id = 100;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 不被杀死的服务
// 1.新建Notification
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this).setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("My notification")
.setContentText("Hello World!");
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, SplashActivity.class);
// The stack builder object will contain an artificial back stack for
// the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(SplashActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
Notification notification = mBuilder.build();
mNotificationManager.notify(id, notification);
// 2.注册成前台进程
startForeground(id, notification);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
在splashActivity的oncreate中开启:
[Java] 纯文本查看 复制代码
if(!ServiceStateUtils.isServiceRunning(this,ProtectingService.class)){
startService(new Intent(this,ProtectingService.class));
}
运行程序后发现手机上方的title栏有一个我们自己应用程序的notification通知在显示即表示我们的进程已被做成一个前台进程了