手机卫士-09

手机卫士-09

课1

回顾高级工具的短信备份 ToolsActivity.java(前台界面) smsBackupparser.java(后台逻辑) 备份短信是一个很耗时的操作,可能需要很长的时间 修改activity_tool.xml的进度条progressBar

<!--   <ProgressBar
    android:id="@+id/pb"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />  -->

在ToolsActivity.java里使用ProgressDialog

在smsbackup里使用。 由于在ToolsActivity的子线程里打印土司结合looper的代码是个坑,所以换一种写法,使用runOnUiThread的线程开启方式来运行代码。

ToolsActivity.class

public class ToolsActivity extends Activity {
    ProgressDialog pd;

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

    /**
     * 归属地查询
     * 
     * @param view
     */
    public void queryLocation(View view) {
        Intent intent = new Intent(this, QueryLocationActivity.class);
        startActivity(intent);
    }

    private class TaskRunnable implements Runnable {

        @Override
        public void run() {
            final boolean result = SmsBackUpParser.getBackUp(getApplicationContext(), new CallBack() {
                @Override
                public void onSmsBackup(int progress) {
                    pd.setProgress(progress);
                }
                @Override
                public void beforeSmsBackup(int max) {
                    pd.setMax(max);
                }
            });
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (result) {
                        Toast.makeText(getApplicationContext(), "备份成功", 1)
                                .show();
                    } else {
                        Toast.makeText(getApplicationContext(), "备份失败", 1)
                                .show();
                    }
                }
            });
            System.out.println("---------------关闭对话框。。。。。。。。啊啊啊啊啊啊");
            pd.dismiss();
        }
    }

    /**
     * 备份短信
     * 
     * @param view
     */
    public void smsbackup(View view) {
        pd = new ProgressDialog(this);
        pd.setTitle("提醒");
        pd.setMessage("正在备份短信。。。");
        pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        pd.show();
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(1);
        TaskRunnable task = new TaskRunnable();
        newFixedThreadPool.execute(task);
    }

}
  • (接口和回调)
  • 但是现在发现前台界面或后台逻辑耦合性太高,前改导致后台也要大改。!!!总结该部分
  • 那么怎么解耦。
  • 使用接口:
  • SmsBackUpParser.java里定义一个接口

SmsBackUpParser.java

public class SmsBackUpParser {

    public interface CallBack{
        /**
         * 短信备份之前调用的方法, max短信的总的个数
         * @param max
         */
        public void beforeSmsBackup(int max);
        /**
         * 短信备份过程中调用的方法,progress当前备份的进度
         * @param progress
         */
        public void onSmsBackup(int progress);
    }


    private static Cursor cursor;
    static String CRYPT_SEED = "hoge";

    /**
     * 备份短信是一个很耗时的操作 可能需要很长的时间。
     * @param context
     * @param callback 备份的回调
     * @return
     */
    public static boolean getBackUp(Context context, CallBack callback) {}
}

然后在getBackUp里调用接口,而不再调用之前前台改来改去的进入条对话框的对象或者是其他什么前台想新增的前台效果的控件对象。

然后在ToolsActivity(前台)里实现接口。这样就可以一劳永逸了。就只修改前台就ok

private class TaskRunnable implements Runnable {

    @Override
    public void run() {
        final boolean result = SmsBackUpParser.getBackUp(getApplicationContext(), new CallBack() {
            @Override
            public void onSmsBackup(int progress) {
                pd.setProgress(progress);
            }
            @Override
            public void beforeSmsBackup(int max) {
                pd.setMax(max);
            }
        });
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (result) {
                    Toast.makeText(getApplicationContext(), "备份成功", 1)
                            .show();
                } else {
                    Toast.makeText(getApplicationContext(), "备份失败", 1)
                            .show();
                }
            }
        });
        System.out.println("---------------关闭对话框。。。。。。。。啊啊啊啊啊啊");
        pd.dismiss();
    }
}

这种接口也叫回调函数。

(后台已经把界面要修改的部分抽象成接口出来暴露出去了。) 以后在公司同事会经常使用这种callback接口的思想,要习惯,就连在谷歌的源代码里都经常使用这样思想,就是实现控件里的监听器的里的逻辑实现,就是使用这种方式。接口的好处、

解耦:

我暴露给你一个回调函数 你提供给我一个接口 回调:CallBack 作业。实现短信还原

介绍多次点击的事件。

首先解决ShowLocationService里的显示归属地的小bug 那是服务停止了,但是监听器的对象没注销掉。那么就把一些变量改成成员变量、然后再在服务里的onDestroy去取消这些监听对象

private TelephonyManager tm;
private MyPhoneStateListener listener;

@Override
public void onDestroy() {
    tm.listen(listener, PhoneStateListener.LISTEN_NONE);
    super.onDestroy();
}

需求:双击自定义归属地框框时,该框框会自动居中。 新建双击事件工程-->演示双击事件 发现在点击事件里是没有双击事件的api 那么要实现双击事件就要自定义了 双击:快速的点击两次:就是双击 快速:时间 一般手机游戏计算时间用SystemClock.uptimeMillis()方法 在工程里模拟双击的逻辑 然后这种双击事件也可以使用在双击两次返回键退出app

课2 (该视频教我们如何观察源码!工作中自学的重要技巧,重视!!!)

继续实现三击事件 新建三击事件工程(这是模板代码:留好) 谷歌给我们实现了很厉害的代码:在关于手机的界面:多次点击Android版本,会解除开发者模式。 查看上层所有应用的源码--->settings 把文件导入eclipse里观察源码 怎么找到 (观察源码的技巧)界面-->布局--->逻辑--->去观察我们想要的在android手机里所看见的app或界面的实现 ctrl+h--->search(然后按照上面的逻辑去顺藤摸瓜找到我们要找的源码功能) 把从源码中找到的三击事件的源码复制到工程中。 然后不断修改long[] mHits = mew long[x]; x代表数字,不断换数字就代表多少次的点击事件。为什么谷歌的这个源代码这么吊? 继续观察源码、 System.arraycopy方法里就是把一个数组里的数组往左移一位。

继续在工程里继续完成需求:双击把自定义归属地框进行居中操作:

ShowLocationService.java

实现双击居中--->也就是修改框框的位置座标

long[] mHits = new long[2];
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
private WindowManager wm;
WindowManager.LayoutParams params;


view.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        System.out.println("------------gagag被点击了。。。。");
        System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
        mHits[mHits.length - 1] = SystemClock.uptimeMillis();
        if (mHits[0] >= (SystemClock.uptimeMillis() - 500)) {
            params.x = wm.getDefaultDisplay().getWidth()/2 - view.getWidth()/2;
            wm.updateViewLayout(view, params);
        }
    }
});


然后在双击事件里双击发现功能没有实现。原因是:因为双击的其中操作被触摸的事件给消费掉了。所以双击没有实现。 也就是双击事件的过程中被触摸事件给消费调用,所以双击事件里的逻辑没有执行。 面试题: 问,点击事件和触摸事件的区别是什么? 点击--->是一组动作的组合,包括 按下--->停留--->擡起

触摸--->就是一个单一的动作

那么解决问题就关键就是把触摸事件里的return true改成false,通过双击去把触摸里的逻辑给解决。

//设置吐司的触摸监听
view.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()) {
        // 当手指按住屏幕的时候,调用的方法
        case MotionEvent.ACTION_DOWN:


            break;
        // 当手指按住屏幕移动的时候,调用的方法
        case MotionEvent.ACTION_MOVE:

            break;
        // 当手指擡起的时候调用的方法
        case MotionEvent.ACTION_UP:

            break;
        }
        //True if the listener 消费掉了事件, false 没有处理完毕事件.
        return false;
    }
});

进程管理模块的实现

手机的进程列出来 回顾安卓基础知识块: 进程级别: process lifecycle

1、前台进程:用户可以看到这个应用程序,并且可以玩

2、可视进程:应用程序的界面用户仍然可以看到,但是摸不着

3、服务进程:应用程序后台有一个服务正在运行

4、后台进程:应用程序没有服务,界面已经最小化了,onStop()被调用,但是onDestroy方法没有被调用

5、空进程:没有服务没有activity,什么都没有。

最近使用:LRU 短信 后台进程 10:18分 浏览器 后台进程 10:17分 email 后台进程 10:16分 先干掉email

开始设计进程管理模块

TaskManagerActivity.java--->配置清单文件 UI类似应用管家里 activitytaskmanager.xml

activitytaskmanager.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        style="@style/textview_title_style"
        android:gravity="center"
        android:text="进程管理器" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_process_count"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="运作进程进程数量:"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_avail_ram"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="剩余内存:"
            android:textSize="12sp" />
    </LinearLayout>

    <ListView
        android:id="@+id/lv_task_manager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </ListView>

</LinearLayout>

1、注入布局

2、找到我们所关心的控件变量(初始化完毕)

使用PackageManager 包管理器 管理的是静态的信息 还有一个ActivityManager 活动管理器 管理的是动态的信息 在之前的工具包的时候使用过ActivityManager 新建SystemInfoUtils工具类:获取系统信息的工具类

SystemInfoUtils.java

/**
 * 获取系统信息的工具类
 * @author Administrator
 *
 */
public class SystemInfoUtils {
    /**
     * 获取正在运行的进程数量
     * @param context上下文
     * @return
     */
    public static int getRunningProcessCount(Context context){
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        return am.getRunningAppProcesses().size();
    }


    /**
     * 获取系统的可用内存
     * @param context上下文
     * @return long类型可用内存大小 单位byte
     */
    public static long getAvailRamSize (Context context){
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        MemoryInfo outInfo = new MemoryInfo();
        am.getMemoryInfo(outInfo);
        return outInfo.availMem;
    }

    /**
     * 获取系统的总内存
     * @param context上下文
     * @return long类型可用内存大小 单位byte
     */
    public static long getTotalRamSize (Context context){
        try {
            File file = new File("/proc/meminfo");
            //MemTotal:         513000 kB
            FileInputStream fis = new FileInputStream(file);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String line = br.readLine();//把第一行读到
            StringBuilder sb = new StringBuilder();
            for(char c: line.toCharArray()){
                if(c>='0'&&c<='9'){
                    sb.append(c);
                }
            }
            return Long.parseLong(sb.toString())*1024;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

在工具类里获取到内存大小和进程数量后 在TaskManagerActivity.java去给控件赋值。Text:运行进程数量和可用内存

课3(重点)

显示总内存是不可以在低版本使用的 原因:去搜索源码setting-->/proc/meminfo--->它其实就是读取系统linux里的系统文件meminfo中记录的内存总量信息。 观察了源码后发现了如何获取总内存信息的方法去兼容低版本方法。 在SystemInfoUtils.java里的方法getTotalRamSize(Context context):模仿系统源码获取内存总大小!!

/**
 * 获取系统的总内存
 * @param context上下文
 * @return long类型可用内存大小 单位byte
 */
public static long getTotalRamSize (Context context){
    try {
        File file = new File("/proc/meminfo");
        //MemTotal:         513000 kB
        FileInputStream fis = new FileInputStream(file);
        BufferedReader br = new BufferedReader(new InputStreamReader(fis));
        String line = br.readLine();//把第一行读到
        StringBuilder sb = new StringBuilder();
        for(char c: line.toCharArray()){
            if(c>='0'&&c<='9'){
                sb.append(c);
            }
        }
        return Long.parseLong(sb.toString())*1024;
    } catch (Exception e) {
        e.printStackTrace();
        return 0;
    }
}

继续设计布局文件activitytaskmanager.xml 继续在TaskManagerActivity.java初始化控件 创建TaskInfoParser.java进程信息的解析器:获取系统里面的进程信息工具类:返回值是list,给TaskManagerActivity.java中的listview使用 创建TaskInfo类Bean:

TaskInfo.java

public class TaskInfo {
    private Drawable icon;
    private String appname;
    private long memsize;
    private String packname;
    public Drawable getIcon() {
        return icon;
    }
    public void setIcon(Drawable icon) {
        this.icon = icon;
    }
    public String getAppname() {
        return appname;
    }
    public void setAppname(String appname) {
        this.appname = appname;
    }
    public long getMemsize() {
        return memsize;
    }
    public void setMemsize(long memsize) {
        this.memsize = memsize;
    }
    public String getPackname() {
        return packname;
    }
    public void setPackname(String packname) {
        this.packname = packname;
    }

}

实现TaskInfoParser.java TaskInfoParser.java

/**
 * 获取系统里面的进程信息工具类
 *
 */
public class TaskInfoParser {
    /**
     * 获取手机系统里面正在运行当前全部进程信息
     * @param context 上下文
     * @return
     */
    public static List<TaskInfo> getTaskInfos(Context context){
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        PackageManager pm = context.getPackageManager();
        List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();
        List<TaskInfo> taskInfos = new ArrayList<TaskInfo>();
        for(RunningAppProcessInfo info:infos){
            TaskInfo taskInfo = new TaskInfo();
            int pid = info.pid;
            String packname = info.processName;
            taskInfo.setPackname(packname);
            long memsize = am.getProcessMemoryInfo(new int[]{pid})[0].getTotalPrivateDirty()*1024;
            taskInfo.setMemsize(memsize);
            try {
                Drawable icon = pm.getPackageInfo(packname, 0).applicationInfo.loadIcon(pm);
                taskInfo.setIcon(icon);
                String name = pm.getPackageInfo(packname, 0).applicationInfo.loadLabel(pm).toString();
                taskInfo.setAppname(name);
            } catch (NameNotFoundException e) {
                e.printStackTrace();
                taskInfo.setAppname(packname);
                taskInfo.setIcon(context.getResources().getDrawable(R.drawable.ic_launcher));
            }
            taskInfos.add(taskInfo);
        }
        return taskInfos;
    }
}

PID:进程标识符 parser的目的是返回list:TaskInfo里有图标,进程标识,进程名,包名也有了 继续实现TaskManagerActivity

TaskManagerActivity.java

public class TaskManagerActivity extends Activity {
    private TextView tv_avail_ram;
    private TextView tv_process_count;
    private ListView lv_task_manager;
    //所有的进程信息
    private  List<TaskInfo> taskInfos;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_task_manager);
        tv_process_count = (TextView) findViewById(R.id.tv_process_count);
        tv_avail_ram = (TextView) findViewById(R.id.tv_avail_ram);
        int count = SystemInfoUtils.getRunningProcessCount(this);
        tv_process_count.setText("运行进程:" + count);
        long availRam = SystemInfoUtils.getAvailRamSize(this);
        long totalRam = SystemInfoUtils.getTotalRamSize(this);
        tv_avail_ram.setText("可用/总内存:"
                + Formatter.formatFileSize(this, availRam) + "/"
                + Formatter.formatFileSize(this, totalRam));
        lv_task_manager = (ListView) findViewById(R.id.lv_task_manager);

        //可能是耗时的操作。
        taskInfos = TaskInfoParser.getTaskInfos(this);
        lv_task_manager.setAdapter(new TaskManagerAdapter());
    }

    private class TaskManagerAdapter extends BaseAdapter{
        @Override
        public int getCount() {
            return taskInfos.size();
        }
        @Override
        public Object getItem(int position) {
            return null;
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view;
            ViewHolder holder;
            if(convertView!=null && convertView instanceof RelativeLayout){
                view = convertView;
                holder = (ViewHolder) view.getTag();
            }else{
                holder = new ViewHolder();
                view = View.inflate(getApplicationContext(), R.layout.item_task_info, null);
                holder.icon = (ImageView) view.findViewById(R.id.iv_task_icon);
                holder.name = (TextView) view.findViewById(R.id.tv_task_name);
                holder.size = (TextView) view.findViewById(R.id.tv_task_size);
                view.setTag(holder);
            }
            TaskInfo info = taskInfos.get(position);
            holder.icon.setImageDrawable(info.getIcon());
            holder.name.setText(info.getAppname());
            holder.size.setText("内存占用:"+Formatter.formatFileSize(getApplicationContext(), info.getMemsize()));
            return view;
        }
    }
    static class ViewHolder{
        ImageView icon;
        TextView name;
        TextView size;
    }
}

继续创建getview里的每个条目的布局文件itemtaskinfo.xml

itemtaskinfo.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/iv_task_icon"
        android:layout_width="55dip"
        android:layout_height="55dip"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:layout_marginLeft="3dip"
        android:layout_marginTop="5dip"
        android:id="@+id/tv_task_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_task_icon"
        android:text="标题"
        android:textColor="#000000"
        android:textSize="20sp" />

    <TextView
        android:layout_marginLeft="3dip"
        android:layout_marginTop="2dip"
        android:id="@+id/tv_task_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_task_name"
        android:layout_toRightOf="@id/iv_task_icon"
        android:text="内存大小"
        android:textColor="#000000"
        android:textSize="15sp" />

    <CheckBox 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        />
</RelativeLayout>

在TaskManagerActivity.java的getview进行填充布局 测试,发现列出来的进程有些是没有名字和图标,为什么? 因为有些进程没有图片或名字就会报异常,所以在异常catch里加上一些默认的名字或者图标。

} catch (NameNotFoundException e) {
    e.printStackTrace();
    taskInfo.setAppname(packname);
    taskInfo.setIcon(context.getResources().getDrawable(R.drawable.ic_launcher));
}

课4

上午进程管理器的界面已经实现了,现在来修改界面里的细节 调节activitytaskmanager.xml布局文件,调整textview 进程的图标太大了,不好看,需要调节

activitytaskmanager.xml

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_process_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:text="运作进程进程数量:"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_avail_ram"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="剩余内存:"
            android:textSize="12sp" />
    </RelativeLayout>

把用户进程和系统进程区分开来、 TaskInfo.java里加上区分进程类型的属性和方法

TaskInfo.java

/**
 * 是否是用户进程 true用户 false系统
 */
private boolean userTask;

public boolean isUserTask() {
    return userTask;
}
public void setUserTask(boolean userTask) {
    this.userTask = userTask;
}

TaskInfoParser.java里加上对进程进行分类的逻辑flags

TaskInfoParser.java

int flags = pm.getPackageInfo(packname, 0).applicationInfo.flags;
//区分用户进程和系统进程
if((flags & ApplicationInfo.FLAG_SYSTEM)==0){
    taskInfo.setUserTask(true);
}else{
    taskInfo.setUserTask(false);
}

在TaskManagerActivity.class分别定义用户进程和系统进程的信息集合 fillData()实现该方法:填充数据的业务方法,可能是个耗时的操作,所以使用子线程:把不同类型的进程放进对应的集合里

TaskManagerActivity.class

/**
 * 填充数据的业务方法 耗时操作
 */
private void fillData() {
    userTaskInfos = new ArrayList<TaskInfo>();
    systemTaskInfos = new ArrayList<TaskInfo>();
    new Thread() {
        public void run() {
            // 可能是耗时的操作。
            taskInfos = TaskInfoParser
                    .getTaskInfos(TaskManagerActivity.this);
            for (TaskInfo info : taskInfos) {
                if (info.isUserTask()) {
                    userTaskInfos.add(info);
                } else {
                    systemTaskInfos.add(info);
                }
            }
            // 设置数据适配器。更新ui,消息机制。
            handler.sendEmptyMessage(0);
        };
    }.start();
}

在fillData里进行listview的条目填充。需要使用Handler和Message的配合。 给自定义的适配器设置逻辑:getView 区分并展示后测试一下:完成

(技巧)还有一个checkBox,发现样式很丑,我们想自定义一个样式,怎么做呢?

观察源码

sdk-->platforms--->android10-->data--->res--->values--->style.xml 观察不同的安卓版本style.xml大小差别很大 在style.xml里搜checkbox 回到drawable--->btn_check.xml---->选择器。找到选择器里使用的资源图片,发现系统的很丑!!!! 所以我们可以按照原理去自定义checkBox的样式

测试下进程管理器的框架,点击条目的checkBox时,勾上时,状态会被其他条目继续复用。

所以以下需求是在TaskInfo里加上新的属性:checked当前条目是否被选中

TaskInfo.java

/**
 * 代表当前条目是否别选中
 * true被选中,false没有被选中
 */
private boolean checked;


public boolean isChecked() {
    return checked;
}
public void setChecked(boolean checked) {
    this.checked = checked;
}

在itemtaskinfo.xml的checkBox控件加上id

<CheckBox android:id="@+id/cb" android:clickable="false" android:focusable="false" android:layoutwidth="wrapcontent" android:layoutheight="wrapcontent" android:layoutalignParentRight="true" android:layoutcenterVertical="true" />

TaskManagerActivity.java注册checkBox的控件

给其设置点击事件 以后的checkBox的记录方式都可以借鉴这种方式,就是把状态存进bean里。 但是点击checkBox只是局部点击,我们想给整个条目注册点击事件,点哪里都可以反应点击。 所以TaskManagerActivity.java--->lvtastmanager--->加上点击事件

TaskManagerActivity.java

// 给listview的item注册点击事件
        lv_task_manager.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                Object obj = lv_task_manager.getItemAtPosition(position);
                if (obj != null) {
                    TaskInfo info = (TaskInfo) obj;
                    if(info.getPackname().equals(getPackageName())){
                        return;
                    }
                    if (info.isChecked()) {
                        info.setChecked(false);
                    } else {
                        info.setChecked(true);
                    }
                    // 通知数据适配器更新listview的界面。
                    adapter.notifyDataSetChanged();
                }
            }
        });

但是发现我们点击这个条目的单击事件时,发现测试的log没打印出来,这是因为该条目的点击事件的焦点被checkBox给占用了 解决就是在布局文件checkBox里改掉其中一个状态为false

 <CheckBox 
        android:id="@+id/cb"
        android:clickable="false"
        android:focusable="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        />

因此继续给条目的点击事件里进行逻辑组织:就是点击条目的时候checkbox怎么也同时切换样式,就是通过adapter的更新操作来切换样式。

课5

为点击条目实现功能



首先实现布局,activitytaskmanager.xml的最下方加上四个按钮(清理、设置、全选、反选)

activitytaskmanager.xml

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
     <Button 
        android:layout_width="0dip"
        android:onClick="selectAll"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:text="全选"
        />
       <Button 
        android:layout_width="0dip"
        android:onClick="selectOther"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:text="反选"
        />
    <Button 
        android:layout_width="0dip"
        android:onClick="cleanSelect"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:text="清理"
        />
    <Button 
        android:onClick="enterSetting"
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:text="设置"
        />
</LinearLayout>

然后给控件之间加上weight,以免被listView挤出去

<ListView
    android:id="@+id/lv_task_manager"
    android:layout_width="fill_parent"
    android:layout_weight="1"
    android:layout_height="0dip" >
</ListView>

TaskManagerActivity.java

实现全选selectAll(View view):使用循环去设置TaskInfo的勾选属性

TaskManagerActivity.java

/**
 * 全选所有的item
 * 
 * @param view
 */
public void selectAll(View view) {
    for (TaskInfo info : userTaskInfos) {
        if(info.getPackname().equals(getPackageName())){
            continue;
        }
        info.setChecked(true);
    }
    for (TaskInfo info : systemTaskInfos) {
        info.setChecked(true);
    }
    adapter.notifyDataSetChanged();
}

实现反选selectOther(View view)

TaskManagerActivity.java

/**
 * 反选所有的item
 * 
 * @param view
 */
public void selectOther(View view) {
    for (TaskInfo info : userTaskInfos) {
        if(info.getPackname().equals(getPackageName())){
            continue;
        }
        info.setChecked(!info.isChecked());
    }
    for (TaskInfo info : systemTaskInfos) {
        info.setChecked(!info.isChecked());
    }
    adapter.notifyDataSetChanged();
}


实现清理(杀死)选中的进程cleanSelect(View view):如何实现逻辑呢? 首先拿到进程管理器:AvtivityManager,使用方法是注意要声明权限

You must hold the permission android.Manifest.permission.KILL_BACKGROUND_PROCESSES to be able to call this method.

测试时发现系统进程清理不去

一般用户不会理解为什么一些进程清理不去,但是发现金山的进程管理可以清空,但这是欺骗的显示而已。

我们如何实现? TaskManagerActivity.java的cleanSelect里进行欺骗操作,利用一个第三方的集合来记录 在修改一些清理进程的一些细节 还有如何过滤掉自己app的checkbox不让选择,如何隐藏:判断 if(info.getPackname().equals(getPackageName())) 还有禁用点击事件 还有全选和反选都做处理

TaskManagerActivity.java

    /**
     * 清理(杀死)选中的进程
     * 
     * @param view
     */
    public void cleanSelect(View view) {
        ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        List<TaskInfo> killedTasks = new ArrayList<TaskInfo>();
        for (TaskInfo info : userTaskInfos) {
            if (info.isChecked()) {
                am.killBackgroundProcesses(info.getPackname());
                killedTasks.add(info);
            }
        }
        for (TaskInfo info : systemTaskInfos) {
            if (info.isChecked()) {
                am.killBackgroundProcesses(info.getPackname());
                killedTasks.add(info);
            }
        }
        long savedMem =0;
        int count = killedTasks.size();
        for(TaskInfo info: killedTasks){
            if(info.isUserTask()){
                userTaskInfos.remove(info);
            }else{
                systemTaskInfos.remove(info);
            }
            savedMem+=info.getMemsize();
        }
        adapter.notifyDataSetChanged();
        Toast.makeText(this, "杀死了"+count+"个进程,释放了"+
        Formatter.formatFileSize(this, savedMem)+"的内存", 1).show();
        runningProcessCount -=count;
        tv_process_count.setText("运行进程:" + runningProcessCount);
        availRam+=savedMem;
        tv_avail_ram.setText("可用/总内存:"
                + Formatter.formatFileSize(this, availRam) + "/"
                + Formatter.formatFileSize(this, totalRam));
    }

继续实现设置的功能 点击设置按钮会进入到一个新的界面

TaskManagerActivity.java

    /**
     * 进入进程管理器的设置界面
     * @param view
     */
    public void enterSetting(View view){
        Intent intent = new Intent(this,TaskManagerSettingActivity.class);
        startActivityForResult(intent, 0);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        adapter.notifyDataSetChanged();
        super.onActivityResult(requestCode, resultCode, data);
    }

新建新的Activity:TaskManagerSettingAvtivity.java+清单文件注册

TaskManagerAvtivity.java--->enterSetting(View view)--->意图跳转到新界面TaskManagerSettingAvtivity.java

设置布局:activitytaskmanagersetting.xml

activitytaskmanagersetting.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <CheckBox
        android:id="@+id/cb_showsystem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="显示系统进程" />

    <CheckBox
        android:id="@+id/cb_autokill"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="定时清理进程" />

</LinearLayout>

设置TaskManagerSettingActivity.java的逻辑

TaskManagerSettingActivity.java

    public class TaskManagerSettingActivity extends Activity {
        private CheckBox cb_showsystem;
        private CheckBox cb_autokill;
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                //定时执行一个任务
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_taskmanager_setting);
            cb_showsystem = (CheckBox) findViewById(R.id.cb_showsystem);
            boolean showsystem = SharedPreferencesUtil.getBoolean(this, "showsystem", true);
            cb_showsystem.setChecked(showsystem);
            cb_showsystem.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    SharedPreferencesUtil.saveBoolean(getApplicationContext(), "showsystem", isChecked);
                }
            });
            cb_autokill = (CheckBox) findViewById(R.id.cb_autokill);
            cb_autokill.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if(isChecked){
                        Intent intent = new Intent(TaskManagerSettingActivity.this,AutoKillService.class);
                        startService(intent);
                    }else{
                        Intent intent = new Intent(TaskManagerSettingActivity.this,AutoKillService.class);
                        stopService(intent);
                    }
                }
            });
        }

        @Override
        protected void onStart() {
            boolean running = ServiceIsRunning.isRunning(this, "com.itheima.mobile47.service.AutoKillService");
            cb_autokill.setChecked(running);
            super.onStart();
        }
    }

实现设置里的定时清理进程功能:把功能写进服务里 新建AutoKillService.java并配置清单文件

AutoKillService.java

public class AutoKillService extends Service {
    InnerScreenLockReceiver receiver;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        receiver = new InnerScreenLockReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(receiver, filter);
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(receiver);
        receiver = null;
        super.onDestroy();
    }


    private class InnerScreenLockReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            System.out.println("屏幕锁屏了。。。。。");
            ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
            List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();
            for(RunningAppProcessInfo info:infos){
                am.killBackgroundProcesses(info.processName);
            }
        }
    }
}

在里面注册实现一个内部广播接收者并注册(锁屏用的) 然后在广播里进行清理进程的逻辑


资料下载

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章