360项目-09

## 进程管理 ##
-  顶部布局和软件管理一样是写好的 组合控件

- 写一个类 ProcessInfoProvider提供进程信息数据

- 获取正在运行进程数

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

- android:process 属性

>每个应用启动起来至少有一个进程, 进程名称默认和包名一样, 四大组件默认都在这个进程中运行.
系统提供了一个属性, 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();
        }


## 内存信息获取 ram ##

- 获取可用(剩余)内存, 使用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());
                }
            }
        };

 

转载于:https://my.oschina.net/u/2884845/blog/784007

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值