## 进程管理 ##
- 顶部布局和软件管理一样是写好的 组合控件
- 写一个类 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());
}
}
};