开发者文档中详细介绍了Widget的使用方法
file:///D:/Program%20Files%20(x86)/Andriod/android-sdks/docs/guide/topics/appwidgets/index.html
最终效果展示:
1. 首先建立AppWidgetProvider的实体类(AppWidgetProvider
class implementation)MyWidget
public classAppWidgetProviderextends BroadcastReceiver
2. AppWidgetProvider 继承 BroadcastReceiver,所以要在清单文件中进行注册(Declaring an App Widget in the Manifest)
<receiver android:name="com.itheima.mobilesafe.receiver.MyWidget" ><intent-filter><action android:name="android.appwidget.action.APPWIDGET_UPDATE" /></intent-filter><meta-dataandroid:name="android.appwidget.provider"android:resource="@xml/appwidget_info" /></receiver>
3. android:resource="@xml/appwidget_info" 为Widget控件的资源描述(Adding the AppWidgetProviderInfo Metadata)
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/process_widget"
android:minHeight="72dp"
android:minWidth="294dp"
android:updatePeriodMillis="1800000" >
</appwidget-provider>
4. android:initialLayout="@layout/process_widget" 为Widget的布局文件(Creating the App Widget Layout)
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:background="@drawable/widget_bg_portrait"android:gravity="center_vertical" ><LinearLayoutandroid:layout_width="0.0dip"android:layout_height="fill_parent"android:layout_marginLeft="5.0dip"android:layout_weight="1.0"android:background="@drawable/widget_bg_portrait_child"android:gravity="center_vertical"android:orientation="vertical"android:paddingBottom="3.0dip"android:paddingTop="3.0dip" ><TextViewandroid:id="@+id/process_count"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10.0dip"android:text="正在运行的软件:15个"android:textAppearance="@style/widget_text" /><ImageViewandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_marginBottom="1.0dip"android:layout_marginTop="1.0dip"android:background="@drawable/widget_bg_portrait_child_divider" /><TextViewandroid:id="@+id/process_memory"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10.0dip"android:text="可用内存:391.80MB"android:textAppearance="@style/widget_text" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center_horizontal"android:orientation="vertical" ><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center_vertical" ><ImageViewandroid:layout_width="20.0dip"android:layout_height="20.0dip"android:src="@drawable/ic_launcher" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/app_name"android:textColor="@color/textColorPrimary" /></LinearLayout><Buttonandroid:id="@+id/btn_clear"android:layout_width="90.0dip"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginTop="5.0dip"android:background="@drawable/function_greenbutton_selector"android:text="一键清理"android:textColor="@color/function_greenbutton_textcolor_selector" /></LinearLayout></LinearLayout>
5. 在服务(UpdateWidgetService)中更新Widget(Using the AppWidgetProvider Class)
服务为四大组件之一,需要在清单文件注册.
<service android:name="com.itheima.mobilesafe.service.UpdateWidgetService" />
服务开启时设置定时更新Widget
@Overridepublic void onCreate() {// 获取Widget的管理器awm = AppWidgetManager.getInstance(this);startWidgetUpdate();super.onCreate();}
定时5秒更新桌面Widget
/**
* 定时5秒更新Widget
*/
private void startWidgetUpdate() {
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
Log.i(TAG, "更新Widgit");
// 更新另外一个进程的操作。ipc调用,进程间通讯(inner process communication)
RemoteViews views = new RemoteViews(getPackageName(), R.layout.process_widget);
// 指定要更新的是哪一个widget
ComponentName provider = new ComponentName(getApplicationContext(), MyWidget.class);
views.setTextViewText(R.id.process_count, "正在运行进程数:"+SystemInfoUtils.getRunningProcessCount(getApplicationContext())+"个");
views.setTextViewText(R.id.process_memory, "可用内存:"+Formatter.formatFileSize(getApplicationContext(), SystemInfoUtils.getAvailRam(getApplicationContext())));
//设置"一键清理"按钮的点击事件,因为在不同的进程之间,所以采用延期意图
//一个延期的意图 发送自定义的广播
Intent intent = new Intent();
intent.setAction("com.itheima.mobilesafe.killall");
//FLAG_UPDATE_CURRENT如果两次点击第一次没有执行,第二次将第一次覆盖
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
awm.updateAppWidget(provider, views);
}
};
timer.schedule(task, 0, 5000);// 每隔5秒钟执行一次。
}
服务关闭时关闭定时器,释放资源
@Overridepublic void onDestroy() {//服务停止关闭屏幕广播接受者unregisterReceiver(offReceiver);unregisterReceiver(onReceiver);offReceiver = null;onReceiver = null;super.onDestroy();}
6. 点击按钮“一键清理”的时候,因为该空间属于桌面程序,所以只能通过延期意图的方式进程间通讯
发送自定义广播intent.setAction("com.itheima.mobilesafe.killall");
7. 编写广播接受者KillAllReceiver,接受广播,执行操作
广播为四大组件之一,在清单文件注册
<receiver android:name="com.itheima.mobilesafe.receiver.KillAllReceiver"><intent-filter><action android:name="com.itheima.mobilesafe.killall"/></intent-filter></receiver>
当广播到来,杀死所有能杀死的进程
@Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, "接收到自定义广播,杀死所有进程");ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();for (RunningAppProcessInfo info : infos) {am.killBackgroundProcesses(info.processName);Log.i(TAG, info.processName+"被杀死了");}}
8. 那么就带来一个问题,我们在什么时候开启服务(UpdateWidgetService)比较合适?
当然可以程序一运行就开启服务,如果不添加桌面Widget这不就是一种资源浪费?那么来研究下Widget的生命周期:
第一个widget被创建
06-25 07:50:00.923: I/System.out(8130): onEnabled (第一个widget被创建的时候 执行 onenable 适合做widget初始化)
06-25 07:50:00.923: I/System.out(8130): onreceiver
06-25 07:50:00.923: I/System.out(8130): onUpdate 只要有新的widget被创建都会调用update方法。 当时间片到了。
06-25 07:50:00.923: I/System.out(8130): onreceiver
第二个widget被创建 (新的创建)
06-25 07:51:11.605: I/System.out(8130): onUpdate
06-25 07:51:11.605: I/System.out(8130): onreceiver
移除一个widget
06-25 07:51:56.966: I/System.out(8130): onDeleted
06-25 07:51:56.966: I/System.out(8130): onreceiver
最后一个被移除
06-25 07:52:19.936: I/System.out(8130): onDeleted
06-25 07:52:19.936: I/System.out(8130): onreceiver
06-25 07:52:19.936: I/System.out(8130): onDisabled 最后一个widget被移除 适合 应用程序的销毁 反初始化 擦屁股
06-25 07:52:19.936: I/System.out(8130): onreceiver
发现第一个创建会执行onEnabled,最后一个移除会执行onDisabled,则可以在onEnable中开启更新服务,在onDisable中关闭更新服务。
@Overridepublic void onEnabled(Context context) {// 创建桌面Widget的时候开启更新服务Log.i(TAG, "开启Widget更新服务");Intent intent = new Intent(context, UpdateWidgetService.class);context.startService(intent);super.onEnabled(context);}
@Overridepublic void onDisabled(Context context) {// 移除所有Widget的时候关闭更新服务Log.i(TAG, "关闭Widget更新服务");Intent intent = new Intent(context, UpdateWidgetService.class);context.stopService(intent);super.onDisabled(context);}
以防服务更新死掉,在定时30分钟更新时再次将服务激活
@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {// 设定时间更新Widget时,再次激活服务Log.i(TAG, "定时Widget更新服务");Intent intent = new Intent(context, UpdateWidgetService.class);context.startService(intent);super.onUpdate(context, appWidgetManager, appWidgetIds);}
9. 那么当手机处于待机状态下我们是不需要更新桌面Widget的,则应当监听屏幕锁闭和屏幕开启的广播
在屏幕锁闭的情况下,关闭定时器释放资源,屏幕开启的广播到来开启定时器,定时更新。
在代码内注册广播接受者
//屏幕锁定内部类广播接受者private class InnerScreenOffReceiver extends BroadcastReceiver {private static final String TAG = "InnerScreenOffReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, "屏幕锁定");// 屏幕锁定取消定时器if(timer != null && task != null) {timer.cancel();task.cancel();timer = null;task = null;}}}//屏幕锁定内部类广播接受者private class InnerScreenOnReceiver extends BroadcastReceiver {private static final String TAG = "InnerScreenOnReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, "屏幕解锁");// 屏幕开启建立定时器if(timer == null && task == null) {startWidgetUpdate();}}}
服务开启时注册广播
@Overridepublic void onCreate() {//注册屏幕锁闭广播接受者offReceiver = new InnerScreenOffReceiver();IntentFilter offFilter = new IntentFilter();offFilter.addAction(Intent.ACTION_SCREEN_OFF);registerReceiver(offReceiver, offFilter);//注册屏幕开启广播接受者onReceiver = new InnerScreenOnReceiver();IntentFilter onFilter = new IntentFilter();onFilter.addAction(Intent.ACTION_SCREEN_ON);registerReceiver(onReceiver, onFilter);// 获取Widget的管理器awm = AppWidgetManager.getInstance(this);startWidgetUpdate();super.onCreate();}
服务停止时反注册广播
@Overridepublic void onDestroy() {// 服务停止关闭定时操作if(timer != null && task != null) {timer.cancel();task.cancel();timer = null;task = null;}// 服务停止关闭屏幕广播接受者unregisterReceiver(offReceiver);unregisterReceiver(onReceiver);offReceiver = null;onReceiver = null;super.onDestroy();}
自此,桌面Widget设置已完成,下面是UpdateWidgetService用到的得到运行进程数以及得到可用内存的工具类
/*** 得到系统内存信息的工具类** @author zwenkai**/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*/public static long getAvailRam(Context context) {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);MemoryInfo outInfo = new MemoryInfo();am.getMemoryInfo(outInfo);return outInfo.availMem;}}