360項目-09

## 進程管理 ##
-  頂部佈局和軟件管理同樣是寫好的 組合控件java

- 寫一個類 ProcessInfoProvider提供進程信息數據android

- 獲取正在運行進程數安全

        public static int getRunningProcess(Context context) {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
            return runningAppProcesses.size();
        }app

- android:process 屬性ide

>每一個應用啓動起來至少有一個進程, 進程名稱默認和包名同樣, 四大組件默認都在這個進程中運行.
系統提供了一個屬性, android:process, 這個屬性能夠配置在application節點或者四大組件節點中,表示當前組件運行在指定進程中,
>例如在清單文件中作以下配置: 注意寫法 要加 : 這個冒號
    <activity android:name=".activity.SettingActivity"
        android:process=":setting"/>函數

當啓動這個Activity的時候, 這個Activity會運行在 com.itheima.mobilesafe:setting 這個進程中.
這種狀況比較少見, 可是仍是有必定的應用場景的, 好比能夠擴大內存使用量, 或者多進程守護等等.佈局

        
-  得到全部的進程數動畫

        public static int getAllProcess(Context context) {
            PackageManager pm = context.getPackageManager();
            // 這裏由於有process標籤的緣由 因此要添加flag 獲取全部組件的進程
            List<PackageInfo> installedPackages = pm.getInstalledPackages(PackageManager.GET_ACTIVITIES
                    | PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS
                    | PackageManager.GET_PROVIDERS);
            // 用HashSet取出重複的值
            HashSet<String> set = new HashSet<String>();
            for (PackageInfo packageInfo : installedPackages) {
                String appProcessName = packageInfo.applicationInfo.processName;
                set.add(appProcessName);
                ActivityInfo[] activities = packageInfo.activities;
                if (activities != null) {
                    for (ActivityInfo activityInfo : activities) {
                        String pn = activityInfo.processName;
                        set.add(pn);
                    }
                }
                ServiceInfo[] services = packageInfo.services;
                if (services != null) {
                    for (ServiceInfo serviceInfo : services) {
                        String pn = serviceInfo.processName;
                        set.add(pn);
                    }
                }
                ProviderInfo[] providers = packageInfo.providers;
                if (providers != null) {
                    for (ProviderInfo providerInfo : providers) {
                        String pn = providerInfo.processName;
                        set.add(pn);
                    }
                }
                ActivityInfo[] receivers = packageInfo.receivers;
                if (receivers != null) {
                    for (ActivityInfo receiverInfo : receivers) {
                        String pn = receiverInfo.processName;
                        set.add(pn);
                    }
                }
            }
            return set.size();
        }ui


## 內存信息獲取 ram ##this

- 獲取可用(剩餘)內存, 使用ActivityManager獲取便可


        /**
         * 獲取可用的內存, 單位 byte
         * @param context
         * @return
         */
        public static long getFreeMemory(Context context) {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            MemoryInfo outInfo = new MemoryInfo();
             // 賦值函數(輸出函數), 傳入一個對象, 給對象賦值
            am.getMemoryInfo(outInfo);
            long availMem = outInfo.availMem;
            return availMem;
        }


-  獲取總內存, 這個也但是使用ActivityManager獲取, 可是會有版本兼容問題
            
        /**
         *  得到全部的內存, 單位 byte
         * @param context
         * @return
         */
        @SuppressLint("NewApi")//去掉警告
        public static long getTotalFreeMemory(Context context) {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            MemoryInfo outInfo = new MemoryInfo();
            am.getMemoryInfo(outInfo);
            long totalMem = 0;
            判斷版本號  重點重點重點重點重點重點重點重點
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                totalMem = outInfo.totalMem;
            } else {
                totalMem = getLowTotalMem();
            }
            return totalMem;
        }


        /**
         * 低版本獲取全部內存  /proc/meminfo 文件讀取
         * @return
         */
        private static long getLowTotalMem() {
            // MemTotal: 513492 kB
            BufferedReader br = null;
            String line = "";
            try {
                File file = new File("/proc/meminfo");
                br = new BufferedReader(new FileReader(file));
                line = br.readLine();
                line = line.replaceAll("MemTotal:", "");
                line = line.replaceAll("kB:", "");
                line = line.trim();
                br.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return Long.parseLong(line) * 1024;
        }


-  listivew先簡單實現   convertView, 和viewholder寫法

        對應的 bean
        public class ProcessInfo {
            public String name;
            public long memory;
            public Drawable icon;
            public boolean isSystem;
            public boolean isChecked;
            public String packageName;
        }


- ###  獲取全部的內存信息 ###


        /**
         * 獲取所有正在運行的進程信息
         *
         * @return
         */
        public static ArrayList<ProcessInfo> getProcessInfos(Context context) {
            ArrayList<ProcessInfo> infos = new ArrayList<ProcessInfo>();
    
            // 獲取活動管理器
            ActivityManager activityManager = (ActivityManager) context
                    .getSystemService(Context.ACTIVITY_SERVICE);
            // 獲取正在運行的全部的進程
            List<RunningAppProcessInfo> runningAppProcesses = activityManager
                    .getRunningAppProcesses();
    
            // 獲取包管理器
            PackageManager packageManager = context.getPackageManager();
    
            // 遍歷 全部的進程
            for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
                // runningAppProcessInfo.pid;//進程的惟一標識
                String processName = runningAppProcessInfo.processName;// 進程名字 默認
                                                                        // 和包名同樣
                // 圖標
                Drawable icon = null;
                // 名字
                String name = null;
                // 是否是系統進程
                boolean isSys = false;
                try {
                    // 獲取應用信息對象
                    ApplicationInfo applicationInfo = packageManager
                            .getApplicationInfo(processName, 0);
                    // 圖標
                    icon = applicationInfo.loadIcon(packageManager);
                    // 名字
                    name = applicationInfo.loadLabel(packageManager).toString();
    
                    if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) {
                        // 是系統進程
                        isSys = true;
                    } else {
                        // 用戶進程
                        isSys = false;
                    }
    
                } catch (NameNotFoundException e) {
                    e.printStackTrace();
                    // 出異常的清空 1 本身添加了進程 :aa 2 系統進程
                    // 設置出異常的默認值
                    name = processName;
                    icon = context.getResources().getDrawable(
                            R.drawable.ic_launcher);
                    isSys = true;
                }
    
                int[] pids = new int[] { runningAppProcessInfo.pid };
                // 獲取指定pid的進程的 內存信息
                android.os.Debug.MemoryInfo[] processMemoryInfo = activityManager
                        .getProcessMemoryInfo(pids);
                // 獲取內存大小
                long totalPss = processMemoryInfo[0].getTotalPss() * 1024;
    
                ProcessInfo info = new ProcessInfo(name, icon, totalPss, isSys,
                        false, processName);
                infos.add(info);
            }
            return infos;
        }


## StickyListHeaders的使用 ##

- 導入開源項目 StickyListHeaders  設置爲依賴項目.       注意 使用的工程和依賴的工程要在一個盤符下
-
到Github上搜索 StickyListHeaders, 下載整個zip, 導入到Eclipse中, 最好拷貝到工做空間.
將佈局文件中的ListView換成

        <se.emilsjolander.stickylistheaders.StickyListHeadersListView
        android:id="@+id/lv_pm"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


-   讓Adapter實現StickyListHeadersAdapter接口  實現多出的兩個方法


        /**
         * 返回每一個條目對應的分隔條目view   根據每一個條目的信息進行判斷  這裏是有兩種
         */
        @Override
        public View getHeaderView(int position, View convertView, ViewGroup parent) {
            TextView tv;
            if (convertView == null) {
                convertView = new TextView(getApplicationContext());
                tv = (TextView) convertView;
            } else {
                tv = (TextView) convertView;
            }
            tv.setBackgroundColor(Color.parseColor("#aaaaaa"));
            tv.setTextSize(15);
            tv.setTextColor(Color.BLACK);
            tv.setPadding(4, 4, 4, 4);
            ProcessInfo info = (ProcessInfo) getItem(position);
            if (info.isSysPro) {
                tv.setText("系統進程" + sysProInfos.size() + "個");
            } else {
                tv.setText("用戶進程" + userProInfos.size() + "個");
            }
            return convertView;
        }

        /**
         * 獲取每個分隔條目的id, 有多少個分隔條目, 這個方法就應該返回多少種不一樣的值
         */
        @Override
        public long getHeaderId(int position) {
            ProcessInfo info = (ProcessInfo) getItem(position);
            if (info.isSysPro) {
                return 0;
            } else {
                return 1;
            }
        }


###勾選, 全選, 反選###

-  若是ListView條目中有 Button, ImageButton, CheckBox等默承認點擊的控件,
    這些控件會搶走條目的點擊事件. 能夠給條目最外層佈局設置一個屬性:
    android:descendantFocusability="blocksDescendants"
    這樣設置的話就是點在哪一個控件上, 哪一個控件響應點擊事件.

 

-  注意 注意 注意 注意 , 在咱們這個頁面中, CheckBox應該設置設置爲不可點擊, 而後給ListView條目設置點擊事件,
CheckBox的勾選狀態應由當前條目對應的JavaBean決定.


         <CheckBox
            android:id="@+id/cb_itempm"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:clickable="false"
            android:focusable="false"
            android:layout_marginRight="10dp"
            android:focusableInTouchMode="false" />

- 設置listview的單條點擊 進行CheckBox選中狀態的切換

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            System.out.println("item");
            // 獲取點擊條目的數據信息
            ProcessInfo info = mInfos.get(position);
            if (TextUtils.equals(info.packageName, getPackageName())) {
                return;
            // 更改數據選中狀態,而後刷新界面
            info.isChecked = !info.isChecked;
            mAdapter.notifyDataSetChanged();
        }

- 在adapter的getview方法裏

        // 根據數據設置是否選中
            viewHolder.cbSelect.setChecked(info.isCheck);

-  在頁面中添加全選, 反選按鈕

>這裏有一個小技巧:在 LinearLayout 中, 若是一個控件高度設置爲 0dp, weight爲1,
其餘控件都的不配置weight屬性, 則weight爲1的控件能夠佔滿 LinearLayout 除了其餘控件的剩餘空間.

- 全選反選的點擊事件

        case R.id.btn_pm_all:
            // 更改數據源裏的選中狀態 而後刷新頁面
            for (ProcessInfo info : mInfos) {
                info.isChecked = true;
            }
            mAdapter.notifyDataSetChanged();
            break;
        case R.id.btn_pm_reverse:
            // 更改數據源裏的選中狀態 而後刷新頁面
            for (ProcessInfo info : mInfos) {
                info.isChecked = !info.isChecked;
            }
            mAdapter.notifyDataSetChanged();
            break;
##進程清理功能##
-  ActivityManager提供了殺死進程的方法:
    /**
     * 殺死後臺進程, 須要權限: KILL_BACKGROUND_PROCESSES
     * @param context
     * @param packageName
     */
    public static void killBackgroundProcess(Context context, String packageName) {
        ActivityManager actManager = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        // 只能殺死後臺進程
        actManager.killBackgroundProcesses(packageName);


        // 只有系統才能調用下面這個方法, 對應的權限也只能系統申請
        // actManager.forceStopPackage("");
    }

- 點擊清理按鈕的時候, 遍歷數據集合,若是當前對象是被選中的, 殺死這個進程,
殺死進程以後應該從數據集合裏移除對象, 因此要使用 ListIterator, 最後刷新ListView
    

        case R.id.ib_process_manager_clean:
        // 殺死進程
        ListIterator<ProcessInfo> listIterator = mInfos.listIterator();
        while (listIterator.hasNext()) {
            ProcessInfo processInfo = (ProcessInfo) listIterator.next();
            if (processInfo.isCheckd) {
              ProcessInfoProvider.killPro(getApplicationContext(), info.packageName);
                // 從集合裏移除
              listIterator.remove();
          }
        }
        // 刷新ListView
        mAdapter.notifyDataSetChanged();
        break;
有一些系統進程是殺不死的, 這裏咱們只是欺騙一下用戶.體驗好一些

-  若是點擊是咱們本身的應用,不去執行選中效果 , 過濾掉 以下:

        在 onItemClick 裏:
        // 不勾選本身
        if(TextUtils.equals(processInfo.mPackageName, getPackageName())) {
            return;
        }

        在全選和反選的for循環裏:
        // 不勾選本身
        if(TextUtils.equals(processInfo.mPackageName, getPackageName())) {
            continue;
        }

- 清理進程後給用戶提示

        int killCount = 0;//記錄清理數量
        long killSize = 0;//記錄清理內存大小
        while (listIterator.hasNext()) {
            ProcessInfo info = listIterator.next();
            if (info.isChecked) {
                ProcessInfoProvider.killPro(getApplicationContext(), info.packageName);
                killCount++;
                killSize += info.memory;
                listIterator.remove();
            }
        }
        ToastUtils.Show(
                getApplicationContext(),
                "清理進程: " + killCount + "個, 釋放內存: "
                        + Formatter.formatFileSize(getApplicationContext(), killSize));


##SlidingDrawer的使用##
- 在佈局裏的 FrameLayout 中添加一個 SlidingDrawer, 用法能夠看這個類的註釋, 注意id的寫法 id爲content對應的內容佈局  id爲handle對應的爲拖動的手柄
    

        <SlidingDrawer
            android:id="@+id/sd_pm"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:content="@+id/content"
            android:handle="@+id/handle" >

            <RelativeLayout
                android:id="@id/handle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/drawer_bg" >

                <ImageView
                    android:id="@+id/iv_pm_arrow1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerHorizontal="true"
                    android:layout_marginTop="20dp"
                    android:src="@drawable/drawer_arrow_up" />

                <ImageView
                    android:id="@+id/iv_pm_arrow2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/iv_pm_arrow1"
                    android:layout_centerHorizontal="true"
                    android:src="@drawable/drawer_arrow_up" />
            </RelativeLayout>

            <LinearLayout
                android:id="@id/content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#fff"
                android:clickable="true"
                android:orientation="vertical" >

                <TextView
                    style="@style/NormalTextStyle"
                    android:layout_margin="10dp"
                    android:text="進程管理設置" />

                <cn.itcast.mobliesafe05.view.SettingView
                    android:id="@+id/sv_pm_showsys"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:background="@drawable/selector_setting_item_top"
                    itcast:istoggle="true"
                    itcast:title="顯示系統進程" >
                </cn.itcast.mobliesafe05.view.SettingView>

                <cn.itcast.mobliesafe05.view.SettingView
                    android:id="@+id/sv_pm_lockclean"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@drawable/selector_setting_item_bottom"
                    itcast:istoggle="true"
                    itcast:title="鎖屏自動清理" >
                </cn.itcast.mobliesafe05.view.SettingView>
            </LinearLayout>
        </SlidingDrawer>

- 三角箭頭的動畫

        /**
         * 開啓小抽屜上的 箭頭動畫
         */
        private void startArrowAnimation() {
    
            AlphaAnimation aa = new AlphaAnimation(0.2f, 1.0f);
            aa.setDuration(500);// 動畫時間
            aa.setRepeatCount(Animation.INFINITE);// 動畫次數爲 無限次
            aa.setRepeatMode(Animation.REVERSE);// 動畫模式
            imgArrow1.setImageResource(R.drawable.drawer_arrow_up);// 設置圖片是向上箭頭
            imgArrow1.startAnimation(aa);
            AlphaAnimation aa1 = new AlphaAnimation(1.0f, 0.2f);
            aa1.setDuration(500);// 動畫時間
            aa1.setRepeatCount(Animation.INFINITE);// 動畫次數爲 無限次
            aa1.setRepeatMode(Animation.REVERSE);// 動畫模式
            imgArrow2.setImageResource(R.drawable.drawer_arrow_up);// 設置圖片是向上箭頭
            imgArrow2.startAnimation(aa1);
        }

        /**
         * 中止箭頭動畫
         */
        protected void stopArrowAnimation() {
            // 中止箭頭動畫
            imgArrow1.clearAnimation();
            imgArrow2.clearAnimation();
            // 更換圖片爲向下箭頭
            imgArrow1.setImageResource(R.drawable.drawer_arrow_down);
            imgArrow2.setImageResource(R.drawable.drawer_arrow_down);
        }

 

- SlidingDrawer的打開和關閉


        sdPm.setOnDrawerOpenListener(new OnDrawerOpenListener() {
            @Override
            public void onDrawerOpened() {
                stopArrowAnimation();
            }
        });
        sdPm.setOnDrawerCloseListener(new OnDrawerCloseListener() {

            @Override
            public void onDrawerClosed() {
                // TODO Auto-generated method stub
                startArrowAnimation();
            }
        });


-  是否顯示系統進程


            case R.id.sv_pm_showsys:
            // 顯示系統進程
            svShowSys.toggle();

            // 保存是否顯示系統進程的狀態
            SharedPreferencesUtils.saveBoolean(getApplicationContext(),
                    Constants.IS_SHOW_SYS, svShowSys.isToggle());

            // 記錄當前開關的狀態 刷新頁面時用
            isShowSys = svShowSys.isToggle();
            // 刷新頁面
            mAdapter.notifyDataSetChanged();

            break;
            

- 回顯數據

        isSysShow = PreferencesUtil.getBoolean(getApplicationContext(), Constants.PRO_SHOW_SYS,
                true);
        sivShowSys.setToggleOn(isSysShow);

        
> adapter裏要作處理

        @Override
        public int getCount() {
            System.out.println("刷新");
            if (isSysShow) {
                return mInfos.size();
            } else {
                return mUserInfos.size();
            }
        }

- 系統進程是否顯示後的細節處理  
        1.更改多選和反選時循環遍歷的數據源

            // 建立一個集合 用來指向不用的數據
            ArrayList<ProcessInfo> selectProcess;
    
            if (isShowSys) {
                // 若是顯示系統進程 那麼就遍歷所有數據
                selectProcess = mInfos;
            } else {
                // 若是不顯示系統進程 那麼就遍歷用戶數據
                selectProcess = mUserInfos;
            }


        2. 清理進程時也要修改 遍歷的數據源 而且清理後刪除數據源時要注意:
            
            // 根據系統進程是否顯示 判斷移除另外一個數據源數據 保證顯示正確
                mUserInfos.remove(info);
                mSysInfos.remove(info);    

            
- 鎖屏殺死進程 ,鎖屏廣播必須動態註冊 ,並且功能在退出應用後也能夠執行,因此要開啓一個後臺服務

        sivAutoClean.toggle();
        Intent intent = new Intent(this, LockCleanService.class);
        if (ServiceStateUtils.isRunning(getApplicationContext(), LockCleanService.class)) {
            stopService(intent);
        } else {
            startService(intent);
        }    

- 在onstart裏回顯數據    因爲進程信息實時變化,因此初始化數據也放在了 裏面
    

        @Override
        protected void onStart() {
            super.onStart();
            initTopData();
            initListData();
            
            // 回顯 鎖屏清理服務的狀態
            boolean isAutoCleanRun = ServiceStateUtils.isRunning(
                    getApplicationContext(), LockAutoCleanService.class);
            svLockClean.setToggleOn(isAutoCleanRun);
        }


- 服務裏監聽鎖屏

        @Override
        public void onCreate() {
            super.onCreate();
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_SCREEN_OFF);// 鎖屏的action
            // 動態註冊 鎖屏的廣播接收者 鎖屏的廣播只能動態註冊起做用
            registerReceiver(receiver, filter);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            unregisterReceiver(receiver);
        }

        private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //清除全部進程
            ProcessInfoProvider.killAllPro(context);
        }
    };


## 前臺服務 ##

- 360, 金山, 騰訊的應用是殺不死的, 這時由於他們開啓了前臺服務, 系統認爲他們是前臺進程.
     而 actManager.killBackgroundProcesses只能殺死後臺進程.
    要讓一個應用成爲前臺進程, 能夠在使用前臺服務. 前臺服務也叫守護服務, 會在通知欄裏顯示一個通知,
    通知欄所在的應用叫 SystemUI, 這個應用是不會掛掉的, 若是一個應用有前臺服務, 在通知欄裏有通知,
    系統也會認爲這個進程是前臺進程, 不會殺死它.

-  寫一個服務, ProtectService, 在開機廣播裏, 和 SplashActivity 的onCreate 方法裏
    開啓這個服務. 接下來就要把它變成一個前臺服務了. 在 onCreate 方法裏調用以下代碼:

        @Override
        public void onCreate() {
            super.onCreate();
            // 開啓前臺服務
            System.out.println("開啓前臺服務");
            Notification notification = new Notification();
            // 指定狀態欄裏的小圖標
            notification.icon = R.drawable.ic_launcher;
            // 指定通知欄裏顯示的View
            notification.contentView = new RemoteViews(getPackageName(), R.layout.view_notification);
            // 必需要有一個id, 代表是本應用哪個通知. 隨便寫一個便可
            startForeground(mId, notification);
        }

- 對應的佈局文件:
-
        <?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="wrap_content"
            android:orientation="horizontal" >
            
            <ImageView android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@drawable/ic_launcher"/>
            
            <TextView android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="黑馬手機衛士保護您的安全"/>
        
        </LinearLayout>
應用成了前臺應用, 即便退出全部的Activity, 依然是前臺進程, 不會被殺死

 

---------------------------------10 13號-----------------------------------------------------------------------------------------------------

# widget  #

- 長按桌面, 或者在全部應用列表裏向右滑動, 能夠添加窗口小部件.
- 寫一個類, 繼承 AppWidgetProvider, 而後在清單文件裏配置, 按照文檔來就好了.
主要是 meta-data 標籤裏配置的 xml:

        <!-- <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
            android:minWidth="40dp" 最小寬高
            android:minHeight="40dp"
            android:updatePeriodMillis="1800000" 多久更新一下Widget, 單位是毫秒, 最短是半個小時 1800000
            android:previewImage="@drawable/preview" 預覽圖片
            android:initialLayout="@layout/example_appwidget" 佈局文件
            android:configure="com.example.android.ExampleAppWidgetConfigure" 配置頁面
            android:resizeMode="horizontal|vertical" 縮放模式
            android:widgetCategory="home_screen"> 類型, 顯示在桌面上, 仍是顯示在鎖屏界面上, API 17
        </appwidget-provider> -->

-  修改佈局, 反編譯金山的apk, 反編譯以後再清單文件裏搜索, 把須要的文件所有拷貝過來
該拷的拷, 該刪的刪, 該改的改. 改完了別忘了修改清單文件
-  Widget的生命週期


        public class WidgetReceiver extends AppWidgetProvider{
            /**
             * 接收到事件
             */
            @Override
            public void onReceive(Context context, Intent intent) {
                super.onReceive(context, intent);
                System.out.println("onReceive");
            }
            /**
             * 第一次添加
             */
            @Override
            public void onEnabled(Context context) {
                super.onEnabled(context);
                System.out.println("onEnabled");
            }
            /**
             * 被添加(第一次, 或者再次添加)/更新
             */
            @Override
            public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
                super.onUpdate(context, appWidgetManager, appWidgetIds);
            }
            /**
             * 被刪除
             */
            @Override
            public void onDeleted(Context context, int[] appWidgetIds) {
                super.onDeleted(context, appWidgetIds);
            }
            /**
             * 最後一個被刪除
             */
            @Override
            public void onDisabled(Context context) {
                super.onDisabled(context);
            }
        }


-  定時更新Widget
看一下日誌, 發現金山每隔一會就更新一下Widget, 它在配置文件裏配的 android:updatePeriodMillis="0",
不依賴系統的更新, 它實際上是啓動了一個服務, 在服務裏作定時操做.
咱們也寫一個服務, 定時更新Widget, 在 onEnabled 方法裏啓動, 在 onDisable 方法裏中止
另外在 onUpdate 方法裏也要起一下服務, 確保服務正在運行. 這是爲了不桌面上已經有Widget,
而後直接在項目上右鍵 run as 不走 onEnabled 方法.
   
         /**
         * 被添加(第一次, 或者再次添加)/更新
         */
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            super.onUpdate(context, appWidgetManager, appWidgetIds);
            // onUpdate 方法裏也要起一下服務, 確保服務正在運行. 避免桌面上已經有Widget,
            // 而後直接在項目上右鍵 run as 不走 onEnabled 方法.
            if (!ServiceStateUtil.isServiceRunning(context, UpdateWidgetService.class)) {
                context.startService(new Intent(context, UpdateWidgetService.class));
            }
        }
    
## 定時器 ##

    java提供的Timer類
     private void timer1() {
        Timer timer = new Timer();
        參1 定時任務 參2 第一次執行的延時時間 參3 間隔
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定時器: Timer");
            }
        }, 0, 3000);
        // 中止
        // timer.cancel();
    }

- 在相應的服務裏: 在oncreate方法中
        

           // 鬧鐘管理器 能夠執行定時任務 而且應用退出也起做用 可是timer就不行
                am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
                Intent intent = new Intent();
                intent.setAction("com.itheima.mobilesafe.action.upwidget");
                // PendingIntent 延時的intent 對應的動做不會當即執行 須要有一個觸發的事件
                // 這裏是用定時器來觸發發送一個廣播
                operation = PendingIntent.getBroadcast(getApplicationContext(), 100, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                // 參1 計時方式,參2 第一次執行的延時時間,參3執行間隔 參4 延時的intent
                am.setRepeating(AlarmManager.RTC, 2000, 5000, operation);

            //註冊接受更新的廣播
            IntentFilter filter = new IntentFilter();
            filter.addAction("com.itheima.mobilesafe.action.upwidget");
            registerReceiver(receiver, filter);


- 關於setRepeating的參數1計時方式  瞭解一下

        public   static   final   int  ELAPSED_REALTIME    
        // 當系統進入睡眠狀態時,這種類型的鬧鈴不會喚醒系統。直到系統下次被喚醒才傳遞它,該鬧鈴所用的時間是相對時間,是從系統啓動後開始計時的,包括睡眠時 間,能夠經過調用SystemClock.elapsedRealtime()得到。系統值是3    (0x00000003)。     
            
        public   static   final   int  ELAPSED_REALTIME_WAKEUP    
        //能喚醒系統,用法同ELAPSED_REALTIME,系統值是2 (0x00000002) 。     
            
        public   static   final   int  RTC    
        //當系統進入睡眠狀態時,這種類型的鬧鈴不會喚醒系統。直到系統下次被喚醒才傳遞它,該鬧鈴所用的時間是絕對時間,所用時間是UTC時間,能夠經過調用 System.currentTimeMillis()得到。系統值是1 (0x00000001) 。     
            
        public   static   final   int  RTC_WAKEUP    
        //能喚醒系統,用法同RTC類型,系統值爲 0 (0x00000000) 。     
            
        Public static   final   int  POWER_OFF_WAKEUP    
        //能喚醒系統,它是一種關機鬧鈴,就是說設備在關機狀態下也能夠喚醒系統,因此咱們把它稱之爲關機鬧鈴。受SDK版本影響,某些版本並不支持,使用方法同RTC類型,系統值爲4(0x00000004)。


- 在註冊的receiver裏


            private BroadcastReceiver receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    String action = intent.getAction();
                    if (TextUtils.equals(action, "com.itheima.mobilesafe.action.upwidget")) {
                        updateWidget();
                    }
                }
            };


            //更新Widget內容, 要使用 AppWidgetManager 這個類
            private void updateWidget() {
                // 獲取 AppWidgetManager
                mWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
                // 初始化widget組件
                ComponentName provider = new ComponentName(this, WidgetReceiver.class);
                // 初始化遠程view對象, 這個View不在咱們的應用進程裏
                RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.process_widget);
                remoteView.setTextViewText(R.id.tv_running_num,
                        "正在運行的軟件:" + ProcessInfoProvider.getRunningProcessCount(this));
                remoteView.setTextViewText(R.id.tv_avail_mem,
                        "可用內存:" + Formatter.formatFileSize(this, ProcessInfoProvider.getAvailMemory(this)));
                // 更新遠程view
                mWidgetManager.updateAppWidget(provider, remoteView);
            }


- AppWidgetManager 和 ComponentName 初始化能夠放在 onCreate 方法裏.

- 在ondestroy裏

        @Override
        public void onDestroy() {
            super.onDestroy();
            //服務結束取消定時器  和取消註冊
            am.cancel(operation);
            unregisterReceiver(receiver);
        }


-  設置Widget的點擊事件 仍是以前的 RemoteViews那裏 增長點擊事件就好


           Intent intent = new Intent();
        intent.setAction("com.itheima.mobilesafe.action.widgetclean");
        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 101,intent, PendingIntent.FLAG_UPDATE_CURRENT);
        //點擊發送一個廣播 去殺死進程
        views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
        widgetManager.updateAppWidget(component, views);

> 以前 註冊的廣播增長一個action

        IntentFilter filter = new IntentFilter();
        filter.addAction("com.itheima.mobilesafe.action.upwidget");
        filter.addAction("com.itheima.mobilesafe.action.widgetclean");
        registerReceiver(receiver, filter);

> 廣播接收者裏也增長判斷

        private BroadcastReceiver receiver = new BroadcastReceiver() {             @Override             public void onReceive(Context context, Intent intent) {                 String action = intent.getAction();                 if (TextUtils.equals(action, "com.itheima.mobilesafe.action.upwidget")) {                     updateWidget();                 } else if (TextUtils.equals(action, "com.itheima.mobilesafe.action.widgetclean")) {                     ProcessInfoProvider.killAllPro(getApplicationContext());                 }             }         };

相關文章
相關標籤/搜索