[Android]实现容易的任务管理器(非root)

[Android]实现简单的任务管理器(非root)

由于不是系统级的应用, 也没有获得ROOT权限, 所以自己实现任务管理器其实意义并不是很大, 就像没有root的手机安装了LBE这类的手机助手, 虽然也带一键清理内存清理后台进程的功能, 但由于手机助手没有ROOT的最高权限, 因此面对开启了守护进程或者其他自启动的应用进程也是手无举措. 而随着谷歌的推动, 即将推送面世的新系统Android  M, 也对权限的管理越来越严格, 这也看出一个趋势, 对移动系统的安全和稳定性要求是越来越高.


虽然意义并不是很大, 但我们还是来练练手吧, 说不定哪天就用上了呢... 哈哈哈.

(效果图)

[Android]实现容易的任务管理器(非root)


API是直接提供获取包信息, 应用进程信息以及关闭后台进程等接口的. 思路大概是, 首先获取所有正在运行的进程, 包括系统应用进程和非系统应用进程. 为了防止误关系统进程而引起的错误, 我们过滤掉系统进程, 只在列表显示非系统进程. 然后根据进程的包名, 可以实现关闭正在运行的某个进程或所有进程. 


但这里通过这里的例子, 发现关闭了比如微信这样的软件, 但很快又在后台重新创建线程继续运行, 所以做的再好的第三方手机助手应用, 只要这个应用程序没有获取root权限或者系统权限, 依然是无法彻底关闭进程或者防止自启的. 就算加入了类似锁屏自动清后台, 一键清理内存等等这样的功能, 没有root权限的普通应用只能通过类似守护进程的方式, 维持本应用的生命力, 然后每隔一段时间就清一次后台进程. 也额外的耗一点电. 当然这好不好, 就看后台一个应用程序能不能"杀"掉众多第三方后台应用而比原来更节电, 见仁见智吧哈.


实现的过程, 并没有什么困难的地方, 所以就直接上代码了:

首先是ListView的item布局文件 process_list_item.xml :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:padding="10dp"
    android:descendantFocusability="blocksDescendants"
    >

    <ImageView
        android:id="@+id/imv_avatar"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:src="@mipmap/ic_launcher"
        />

    <TextView
        android:id="@+id/tv_app_name"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:gravity="center_vertical|left"
        android:layout_toRightOf="@+id/imv_avatar"
        android:layout_centerVertical="true"
        android:layout_marginLeft="15dp"
        android:textSize="18sp"
        android:text=""
        />

    <TextView
        android:id="@+id/tv_app_process_name"
        android:layout_width="160dp"
        android:layout_height="40dp"
        android:gravity="center"
        android:layout_centerVertical="true"
        android:textSize="18sp"
        android:visibility="gone"
        android:maxLines="2"
        />

    <Button
        android:id="@+id/btn_stop_app"
        android:layout_width="80dp"
        android:layout_height="50dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:text="关闭进程"
        />

</RelativeLayout>

然后是主类布局文件 activity_main.xml :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <TextView
        android:id="@+id/tv_main_title"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="任务管理器"
        android:gravity="center"
        android:textSize="18sp"
        android:background="#BDBDBD"
        android:layout_alignParentTop="true"
        />
    <Button
        android:id="@+id/btn_main_clear"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="一键清理"
        android:gravity="center"
        android:textSize="16sp"
        android:textColor="#23BB67"
        android:layout_below="@+id/tv_main_title"
        />

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/btn_main_clear"
        />


</RelativeLayout>

进程信息对象 ProcessInfo :

/**
 * Created by AlexTam on 2015/7/29.
 */
public class ProcessInfo {
    private String labelName;
    private Drawable labelIcon;
    private String processName;

    public ProcessInfo(){ }

    public ProcessInfo(String labelName, Drawable labelIcon, String processName) {
        this.labelName = labelName;
        this.labelIcon = labelIcon;
        this.processName = processName;
    }

    public String getLabelName() {
        return labelName;
    }

    public void setLabelName(String labelName) {
        this.labelName = labelName;
    }

    public Drawable getLabelIcon() {
        return labelIcon;
    }

    public void setLabelIcon(Drawable labelIcon) {
        this.labelIcon = labelIcon;
    }

    public String getProcessName() {
        return processName;
    }

    public void setProcessName(String processName) {
        this.processName = processName;
    }
}

然后是Adapter, 里面给每个item的按钮都添加点击事件 : 

/**
 * Created by AlexTam on 2015/7/29.
 */
public class ProcessListAdapter extends BaseAdapter{
    private Context context;
    private List<ProcessInfo> processList;
    private viewHolder holder;
    private processListButtonClick listener;


    public ProcessListAdapter(Context context, List<ProcessInfo> processList,
            processListButtonClick listener)
    {
        this.context = context;
        this.processList = processList;
        this.listener = listener;
    }

    @Override
    public int getCount() {
        return processList.size();
    }

    @Override
    public Object getItem(int position) {
        return processList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ProcessInfo processInfo = (ProcessInfo)getItem(position);

        if(convertView == null)
        {
            holder = new viewHolder();
            convertView = View.inflate(context, R.layout.process_list_item, null);
            holder.imv_avatar = (ImageView) convertView.findViewById(R.id.imv_avatar);
            holder.tv_name = (TextView) convertView.findViewById(R.id.tv_app_name);
            holder.tv_processName = (TextView) convertView.findViewById(R.id.tv_app_process_name);
            holder.btn = (Button) convertView.findViewById(R.id.btn_stop_app);
        }
        else
        {
            holder = (viewHolder)convertView.getTag();
        }

        holder.imv_avatar.setImageDrawable(processInfo.getLabelIcon());
        holder.tv_name.setText(processInfo.getLabelName());
        holder.tv_processName.setText(processInfo.getProcessName());
        holder.btn.setOnClickListener(new POnClickListener(processInfo.getProcessName()));

        convertView.setTag(holder);

        return convertView;
    }

    private class POnClickListener implements View.OnClickListener
    {
        private String processName;

        public POnClickListener(String processName)
        {
            this.processName = processName;
        }

        @Override
        public void onClick(View v)
        {
            if(listener != null)
                listener.onButtonClick(processName);
        }
    }

    private class viewHolder
    {
        ImageView imv_avatar;
        TextView tv_name;
        TextView tv_processName;
        Button btn;
    }

    public interface processListButtonClick
    {
        void onButtonClick(String processName);
    }

}

OK, 最后是主类 MainActivity :

/**
 * Created by AlexTam on 2015/7/29.
 */
public class MainActivity extends Activity {
    private ListView ll_main;

    private List<ProcessInfo> processList
            = new ArrayList<ProcessInfo>();
    private List<ApplicationInfo> applicationInfoList ;

    private ProcessListAdapter adapter = null;

    private Button btn_clear;

    private List<String> processNamelist = new ArrayList<String>();

    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ll_main = (ListView) findViewById(R.id.lv_main);
        btn_clear = (Button) findViewById(R.id.btn_main_clear);

        getProcessList();

        btn_clear.setOnClickListener(new MyOnclick());
    }


    @Override
    public void onDestroy()
    {
        super.onDestroy();
    }


    private class MyOnclick implements View.OnClickListener
    {
        @Override
        public void onClick(View v)
        {
            if(v == btn_clear)
            {
                clearAllBackgroundProcess();
            }
        }
    }


    /**
     * 获取进程信息列表
     */
    private void getProcessList()
    {
        ActivityManager activityManager
                = (ActivityManager)getSystemService(ACTIVITY_SERVICE);

        //获取所有将运行中的进程
        List<ActivityManager.RunningAppProcessInfo> runningAppList
                = activityManager.getRunningAppProcesses();

        PackageManager packageManager
                = this.getPackageManager();

        //获取所有包信息
        applicationInfoList
                = packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);


        if(processList != null && processList.size() > 0)
            processList.clear();

        if(processNamelist != null && processNamelist.size() > 0)
            processNamelist.clear();

        for(ActivityManager.RunningAppProcessInfo process : runningAppList)
        {
            if(process.processName.indexOf(this.getPackageName()) < 0)
            {   //过滤本应用包名
                ProcessInfo p = new ProcessInfo();

                ApplicationInfo appInfo = getApplicationInfoByProcessName(process.processName);
                if(appInfo == null)
                {
                    //有些应用的守护进程并没有目标应用对应,此时返回null
                }
                else
                {
                    p.setLabelIcon(appInfo.loadIcon(packageManager));
                    p.setLabelName(appInfo.loadLabel(packageManager).toString());
                    p.setProcessName(appInfo.processName);

                    processNamelist.add(appInfo.processName);
                    processList.add(p);
                }
            }
        }

        if(adapter == null)
        {
            adapter = new ProcessListAdapter(MainActivity.this, processList,new ItemButtonClick());
            ll_main.setAdapter(adapter);
            ll_main.setOnItemClickListener(new MyOnItemClickListener());
        }
        else
        {
            adapter.notifyDataSetChanged();
        }

    }

    /**
     * 根据进程名获取应用信息
     * @param processNames
     * @return
     */
    private ApplicationInfo getApplicationInfoByProcessName(String processNames)
    {
        if(applicationInfoList == null || applicationInfoList.size() < 1)
            return null;

        for(ApplicationInfo applicationInfo : applicationInfoList)
        {
            if(applicationInfo.processName.equals(processNames)
                    && (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0)
                //只显示第三方的应用进程,不显示系统应用
                //要显示所有应用进程,删去(applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0 即可
                return applicationInfo;
        }
        return null;
    }

    private class MyOnItemClickListener implements AdapterView.OnItemClickListener
    {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id)
        {
            TextView tv_appName = (TextView) view.findViewById(R.id.tv_app_name);
            TextView tv_processName = (TextView) view.findViewById(R.id.tv_app_process_name);

            String appName = tv_appName.getText().toString();
            String processName = tv_processName.getText().toString();

            Toast.makeText(MainActivity.this, "应用: " + appName + "\n进程: " + processName,
                    Toast.LENGTH_SHORT).show();
        }
    }

    private class ItemButtonClick implements ProcessListAdapter.processListButtonClick
    {
        String pName = null;

        @Override
        public void onButtonClick(String processName) {
            pName = processName;

            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setTitle("关闭进程")
                    .setMessage("确定要关闭 " + processName+" 进程吗?")
                    .setPositiveButton("确定", new DialogInterface.OnClickListener(){
                        public void onClick(DialogInterface dialog, int which)
                        {
                            if(pName != null)
                            {
                                ActivityManager activityManager
                                        = (ActivityManager)MainActivity.this.
                                        getSystemService(ACTIVITY_SERVICE);

                                activityManager.killBackgroundProcesses(pName);
                                getProcessList();
                            }
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener(){
                        public void onClick(DialogInterface dialog, int which)
                        {
                            dialog.dismiss();
                        }
                    });

            builder.show();
        }
    }


    private Handler mHandler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            if(msg.what == 0x1)
            {
                startClearAnim();
            }
            else if(msg.what == 0x2)
            {
                stopClearAnim();
                getProcessList();
            }
            super.handleMessage(msg);
        }
    };


    /**
     * 一键清理
     */
    private void clearAllBackgroundProcess()
    {
        mHandler.sendEmptyMessage(0x1);

        ActivityManager activityManager
                = (ActivityManager)MainActivity.this.getSystemService(ACTIVITY_SERVICE);

        if(processNamelist != null && processNamelist.size() > 0)
        {
            for(String processName : processNamelist)
            {
                activityManager.killBackgroundProcesses(processName);
            }
        }
        mHandler.sendEmptyMessageDelayed(0x2, 2000);
    }

    private void startClearAnim()
    {
        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setMessage("努力清理中...");
        progressDialog.show();
    }

    private void stopClearAnim()
    {
        progressDialog.dismiss();
    }


}

因为是探讨功能, 一键清理并没有加入复杂的动画. 如果要加入, 可以考虑在视图中间隐藏一个ImageView, 然后用AnimatorSet存放两个属性动画去实现, 一个是控制ImageView从透明到完全显示, 同时大小也是从0.0f 到1.0f . 第二个是控制无限的旋转. 当然, 用Animation也能实现, 一样的哈. 最后当清理完成, 就再次隐藏ImageView, 设为gone. 至于那些将快捷方式放到手机桌面, 然后点击快捷方式组件就有个动画出来, 同时清理后台的, 可以考虑做一个Activity, theme为一个dialog, 然后播放动画同时清理后台. 也能达到类似的效果.


相信很多人都明白, 其实Android系统有自己的运行机制, 每个应用都有自己的进程, 每个进程对应有一个虚拟机, 这样设计为的是当自己这个进程挂掉也不会影响系统的运行和其他进程. 而当运行一些比较占内存的应用时, 系统也会自动将优先级低, 视为"无用,过时"的后台进程关闭, 从而挤出内存. 当然这不是万能的, 因为系统也并不总是能这么及时的处理, 以至于我们在使用比如游戏这样的应用时, 低内存的手机还是会由于内存不足的原因感觉有些卡顿. 所以显得手动请后台有一定的合理性.



版权声明:本文为博主原创文章,未经博主允许不得转载。