手機衛士-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);
            }
        }
    }
}

在裏面註冊實現一個內部廣播接收者並註冊(鎖屏用的) 然後在廣播裏進行清理進程的邏輯


資料下載

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