如何在Android实现桌面清理内存简单Widget小控件

我们经常会看到类似于360、金山手机卫士一类的软件会带一个widget小控件,显示在桌面上,上面会显示现有内存大小,然后会带一个按键功能来一键清理内存,杀死后台进程的功能,那么这个功能是如何实现的呢,我们今天也来尝试做一个类似的功能的小控件。
效果图:

一、UI部分的编写:

参照Google的文档,首先在建立一个类继承AppWidgetProvider

import android.appwidget.AppWidgetProvider;public class MyWidget extends AppWidgetProvider {}

然后在清单文件中申明它,我们必须注意到,AppWidgetProvider实际上是BroadcastReceiver,所以要注册成一个receiver,然后还有一些其他的东西需要注意:

 <receiver android:name="com.alexchen.widget.MyWidget" ><intent-filter><action android:name="android.appwidget.action.APPWIDGET_UPDATE" /></intent-filter><meta-dataandroid:name="android.appwidget.provider"android:resource="@xml/example_appwidget_info" /></receiver>

android.appwidget.action.APPWIDGET_UPDATE 表明这个receiver能够接受一个APPWIDGET_UPDATE的广播,而且在这里,只能加入这一个action。

android.appwidget.provider 表明数据类型时widget提供者提供的数据,example_appwidget_info表明这个widget的参数配置文件名和位置

那么接下来就需要在res目录下建立一个xml文件夹,并且在其中建立一个example_appwidget_info.xml的配置文件,Google的文档中给出了示例有很多参数,实际上关键的参数只有下面的4个:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"android:minWidth="94dp"android:minHeight="72dp"android:updatePeriodMillis="86400000"android:initialLayout="@layout/example_appwidget">
</appwidget-provider>

其中,minWidthminHeight代表这个widget控件所占据的最小空间,这个空间一般来讲不需要太大,因为太大的话,一个屏幕可能都没办法放下,Google的官方文档的说法是大于4x4的就可能无法显示。

updatePeriodMillis代表数据更新的时间,这里86400000毫秒实际上是24小时,可能最开始看到这个参数会想我能否将其设的很小,每一秒刷新很多次?,实际上对于updatePeriodMillis这个参数而言,即算你设的再小也没用,Google设定widget控件这个参数控制的最短update时间为30分钟,就算将其设置在30分钟以内也会以30分钟的频率来更新数据。

initialLayout参数代表的是本widget空间的布局文件。

那么下一步就是定义出一个对应的布局文件。我们可以简单的在layout目录下建立一个布局文件example_appwidget.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="200dp"android:layout_height="80dp"android:background="@android:color/white"android:gravity="center_vertical"android:orientation="vertical" ><TextViewandroid:id="@+id/tv_widget"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center"android:text="widget控件测试"android:textColor="@android:color/black"android:textSize="15sp" /><Buttonandroid:id="@+id/btn_clear"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="清理内存"android:textColor="#ff000000" /></LinearLayout>

在Google的文档中有指出,并非所有的布局组件都可以在上面的这个布局中生效,有效的组件或布局为:

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout
  • AnalogClock
  • Button
  • Chronometer
  • ImageButton
  • ImageView
  • ProgressBar
  • TextView
  • ViewFlipper
  • ListView
  • GridView
  • StackView
  • AdapterViewFlipper

至此,一个简单的widget控件就写好了,我们可以在模拟器上将其拖到桌面上看一看效果:

二、功能逻辑部分的实现

大部分的Widget小控件都会需要在特定情况下更新上面显示的数据,那么这个是如何实现的呢,我们经过上面的代码不难发现实际上这个widget控件并没有一个Activity,所以说这个控件的显示实际上不是本应用来实现的,它实际上是桌面这个应用来显示的,所以我们也不可能直接去更新它上面的数据。

回过头去看看上面我们写的那个receiver,实际上没有实现任何方法。实际上AppWidgetProvider里面有几个比较重要的方法:onReceive、onUpdate、onDisabled、onEnabled

其中onReceive方法跟大多数广播接收者的onReceive方法一样,但是在这里,onReceive方法的调用并不是我们可以决定的,它依赖于显示该widget控件的Host组件,在这里也就是Android桌面应用,所以我们会发现在不同的手机上,将widget控件拖到桌面上显示的时候onReceive可能调用的次数和先后顺序可能完全不一样,这依赖于Host组件是如何实现的。

所以在这里onReceive方法对于我们刷新widget数据基本没有什么帮助。

onUpdate方法则是由上面所说的updatePeriodMillis参数来控制的,经过上面的分析,我们都知道了,它的最小周期为30分钟。所以我们一般将这个参数设为0即可。那么在这个方法里,我们往往会在其中放置一些启动更新数据服务的功能,因为如果后台的更新数据的Service被意外停止了,那么每30分钟还会被重新启用,不至于一直启动不了了:

 @Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {super.onUpdate(context, appWidgetManager, appWidgetIds);// System.out.println("onUpdate");//每隔一段时间重新启动服务,防止服务中间被终止了之后没法重启Intent intent = new Intent(context, UpdateWidgetService.class);context.startService(intent);}

下面是比较重要的两个方法了: onDisabled和onEnabled

我们知道,widget小控件是可以拖动多个到桌面上的,而onEnabled方法会在第一个widget控件拖到桌面上的时候调用一次,onDisabled会在最后一个widget控件从桌面被删除时调用一次,那么我们需要做的就是在onEnabled这个方法中启用一个刷新widget数据的服务,在onDisabled方法中使用stopService方法来停止这个服务。

@Overridepublic void onDisabled(Context context) {super.onDisabled(context);System.out.println("onDisabled");//停止数据刷新服务Intent intent = new Intent(context, UpdateWidgetService.class);context.stopService(intent);}@Overridepublic void onEnabled(Context context) {super.onEnabled(context);System.out.println("onEnabled");//开启数据刷新服务Intent intent = new Intent(context, UpdateWidgetService.class);context.startService(intent);}

三、刷新数据的服务

那么下面的任务就只剩下UpdateWidgetService这个刷新数据的服务(Service)如何实现的问题了。

我们在这里的想法很简单,比如说每隔三秒钟来刷新一下widget中的数据。Android中定时执行任务的方法有很多,我们这里使用TimerTimerTask来实现,之后我们需要关心的就是具体如何实现刷新widget中的数据,毕竟这些数据是在桌面应用中显示的。

并且我们需要用到一个API--AppWidgetManager,它有一个实例方法AppWidgetManager.updateAppWidget(ComponentName provider, RemoteViews views)来实现更新widget数据,我们都知道,如果需要调用另外应用的方法,需要使用远程调用的方法来实现,在这里起到在我们的应用和桌面应用之间的桥梁作用的就是这第二个参数:RemoteViews views,它会将我们设置的数据传送到桌面应用来刷新widget上的数据,我们需要经过下面几步:

1、定义一个RemoteViews的实例:

RemoteViews views = new RemoteViews(getPackageName(),R.layout.process_widget);

2、设置views的内容,也就是刷新其中的数据,这里的方法名会比较奇怪, RemoteViews.setTextViewText(int viewId, CharSequence text)

其中viewId是在我们前面定义的widget布局文件中的子组件的id,也就是我们要刷新内容的对象,这里就是R.id.tv_test,第二个参数是我们要更新的内容

3、定义好第一个参数ComponentName provider之后,就可以调用AppWidgetManager.updateAppWidget(ComponentName provider, RemoteViews views)来实现更新数据

if (timer == null && task == null) {//AppWidgetManager对象,用于更新widget的数据awm = AppWidgetManager.getInstance(this);timer = new Timer();task = new TimerTask() {@Overridepublic void run() {ComponentName provider = new ComponentName(UpdateWidgetService.this, MyWidget.class);//远程view对象,用于在本应用和桌面应用中起传递数据的桥梁作用RemoteViews views = new RemoteViews(getPackageName(),R.layout.example_appwidget);views.setTextViewText(R.id.tv_widget, "想刷新的数据的内容");awm.updateAppWidget(provider, views);System.out.println("====刷新了widget====");}//设置循环时间timer.schedule(task, 0, 3000);
}

四、定时刷新可用内存和一键清理内存功能实现

要实现这个功能,我们需要再上面定时刷新数据服务中将定时刷新的内容改为当前内存所剩余的量,我们这里写一个工具类方法来实现返回内存剩余量;

另外我们还需要在widget控件的布局文件中添加一个button,并在更新widget数据的服务中,设置这个button的点击事件,但是这里也不像以前的点击事件,同样要应用到RemoteView对象,在这个点击事件中需要发送一个广播,Action为自定义的,我们这里设为:"com.alexchen.mobilesafeexercise.killall",之后,我们需要再写一个广播接收者,来接收这个广播,在onReceive方法中执行杀死后台进程的操作。这里也不能直接使用Intent,由于我们这个意图的Action不是由我们自己执行而是由其他应用程序(桌面应用)执行的,所以需要用到PendingIntent

刷新widget数据的服务代码:

package com.alexchen.widget.service;import java.util.Timer;
import java.util.TimerTask;import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.text.format.Formatter;
import android.widget.RemoteViews;import com.alexchen.widget.MyWidget;
import com.alexchen.widget.R;
import com.alexchen.widget.utils.SystemInfoUtils;public class UpdateWidgetService extends Service {private Timer timer;private TimerTask task;private AppWidgetManager awm;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();startTimer();}private void startTimer() {if (timer == null && task == null) {awm = AppWidgetManager.getInstance(this);timer = new Timer();task = new TimerTask() {@Overridepublic void run() {ComponentName provider = new ComponentName(UpdateWidgetService.this, MyWidget.class);RemoteViews views = new RemoteViews(getPackageName(),R.layout.example_appwidget);views.setTextViewText(R.id.tv_widget, "dd");views.setTextViewText(R.id.tv_widget,"可用内存:"+ Formatter.formatFileSize(getApplicationContext(),SystemInfoUtils.getAvailableMem(getApplicationContext())));// 自定义一个广播,杀死后台进程的事件Intent intent = new Intent();intent.setAction("com.alexchen.mobilesafeexercise.killall");// 描述一个动作,这个动作是由另外一个应用程序执行的PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);awm.updateAppWidget(provider, views);System.out.println("====刷新了widget====");}};timer.schedule(task, 0, 3000);}}@Overridepublic void onDestroy() {super.onDestroy();stopTimer();unregisterReceiver(offReceiver);unregisterReceiver(onReceiver);}private void stopTimer() {if (timer != null && task != null) {timer.cancel();task.cancel();task = null;timer = null;}}
}

按键清理内存的广播接收者:

package com.alexchen.widget.receiver;import java.util.List;import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;public class KillAllReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {System.out.println("自定义的广播消息接收到了...开始清理内存...");ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();for (RunningAppProcessInfo info : runningAppProcesses) {am.killBackgroundProcesses(info.processName);}}
}

获取可用内存的工具类方法:

/*** 获取手机可用的剩余内存* * @param context*            上下文* @return*/public static long getAvailableMem(Context context) {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);MemoryInfo outInfo = new MemoryInfo();am.getMemoryInfo(outInfo);return outInfo.availMem;}

如何在Android实现桌面清理内存简单Widget小控件相关推荐

  1. android 清理缓存动画,Android仿微信清理内存图表动画(解决surfaceView屏幕闪烁问题)demo实例详解...

    最近接了一个项目其中有功能要实现一个清理内存,要求和微信的效果一样.于是想到用surfaceView而不是继承view.下面小编给大家解析下实现思路. surfaceView是为了解决频繁绘制动画产生 ...

  2. Android之RemoteViews篇上————通知栏和桌面小控件

    Android之RemoteViews篇上----通知栏和桌面小控件 一.目录 文章目录 Android之RemoteViews篇上----通知栏和桌面小控件 一.目录 二.RemoteViews的概 ...

  3. android组件开关按钮,简单聊聊“开关”这个小控件

    开关虽然只是一个小控件,看起来很简单,但其实它的设计也有着大学问.本文和你一起探讨一下~ 一.开关是什么 开关,英文Switch,常被翻译为开关.滑动开关.切换开关,作为界面中可直接操作的元件,提供两 ...

  4. (超多图)基于Android studio开发的一个简单入门小应用(超级详细!!)(建议收藏)

    基于Android studio开发的一个简单入门小应用 一.前言 二.前期准备 三.开发一个小应用 五.运行应用 一.前言 在暑假期间,我学习JAVA基础,为了能早日实现自己用代码写出一个app的& ...

  5. android桌面时钟小控件开发记录

    所谓桌面小控件,就是能之间显示在Android系统桌面的小程序. 桌面小控件的实现是基于Broadcast的形式实现的,因此,每一个桌面小控件都对应于一个BroadcastReceiver类.Andr ...

  6. android 桌面图标的点击放大效果,Android仿英语流利说取词放大控件的实现方法(附demo源码下载)...

    本文实例讲述了Android仿英语流利说取词放大控件的实现方法.分享给大家供大家参考,具体如下: 1 取词放大控件 英语流利说是一款非常帮的口语学习app,在app的修炼页面长按屏幕,会弹出一个放大镜 ...

  7. Android UI设计之十一自定义ViewGroup,打造通用的关闭键盘小控件ImeObser

    2019独角兽企业重金招聘Python工程师标准>>> 转载请注明出处:http://blog.csdn.net/llew2011/article/details/51598682 ...

  8. Android 第十八课 强大的滚动控件 RecyclerView

    步骤: 一.添加依赖库 compile'com.android.support:recyclerview-v7:26.1.0' 二.在activity_mian.xml中,添加RecyclerView ...

  9. Android 打造完美的侧滑菜单/侧滑View控件

    概述 Android 打造完美的侧滑菜单/侧滑View控件,完全自定义实现,支持左右两个方向弹出,代码高度简洁流畅,兼容性高,控件实用方便. 详细 代码下载:http://www.demodashi. ...

最新文章

  1. 数据分析需求转型与商业模式重构
  2. 黑龙江专科大学计算机专业,黑龙江10大最好的专科学校,有你心仪的学校吗?...
  3. Android View绘制之旅
  4. MyEclipse 2013 新功能介绍
  5. SpringBoot整合Mybatis,使用通用mapper和PageHelper进行分页
  6. LeetCode第 3 场双周赛(2019.6.29)第一题:小于 K 的两数之和
  7. 配置spring整合jpa自动生成数据表
  8. html css图标怎么跟文字并排,CSS高级技巧:精灵图、字体图标、CSS三角做法、CSS用户界面样式、vertical-align属性应用、溢出文字省略号、常见布局技巧...
  9. 数据结构之栈实现中缀转后缀并计算结果
  10. netstat命令查看网络信息
  11. 整合TextBox与Label 创建新控件--EFLabelText
  12. qmap按插入顺序排序_C++语言排序算法之插入排序
  13. Oracle 11g Database和ASM默认的审计策略和相关操作
  14. Paper:LSTM之父眼中的深度学习十年简史《The 2010s: Our Decade of Deep Learning / Outlook on the 2020s》的解读
  15. 计算机需要英语水平的标准,计算机专业英语课程标准和要求
  16. Android Studio ADB 环境变量配置
  17. Scara机器人正逆运动学分析
  18. 前端工程师的第一个Flutter应用
  19. 如何删除Word文档中的空白页
  20. Android源代码中引用@hide类出现引用异常的问题error: cannot find symbol

热门文章

  1. PHP导出文件名乱码解决
  2. 【论文翻译】Multi-object Tracking via End-to-end Tracklet Searching and Ranking
  3. 牧牛区块链,区块链+隐私计算的作用方式
  4. 学习sift算法的原理和步骤_深度学习笔记47_你也可以成为梵高_风格迁移算法的原理
  5. 2008年最搞笑的10句话
  6. C语言数字图像处理---1.7HSV颜色空间与颜色变换
  7. 9.那些年我们一起经历过的痛-“15个最常见介词的深层次含义”
  8. IG夺冠那晚,全世界都在祝贺,你呢?
  9. 工作出错检讨书实用版
  10. 实验二:Arduino的三色灯实验