怎样在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-data
android: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" > <TextView
android: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" /> <Button
android: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的文档中有指出,并不是全部的布局组件都能够在上面的这个布局中生效,有效的组件或布局为:

至此。一个简单的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分钟还会被又一次启用。不至于一直启动不了了:

	@Override
public 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方法来停止这个服务。

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

三、刷新数据的服务

那么以下的任务就仅仅剩下UpdateWidgetService这个刷新数据的服务(Service)怎样实现的问题了。

我们在这里的想法非常easy。比方说每隔三秒钟来刷新一下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() {
@Override
public 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; @Override
public IBinder onBind(Intent intent) {
return null;
} @Override
public void onCreate() {
super.onCreate();
startTimer();
} private void startTimer() {
if (timer == null && task == null) {
awm = AppWidgetManager.getInstance(this);
timer = new Timer();
task = new TimerTask() { @Override
public 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);
}
}
@Override
public 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 { @Override
public 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 之 桌面的小控件AppWidget

    AppWidget是创建的桌面窗口小控件,在这个小控件上允许我们进行一些操作(这个视自己的需要而定).作为菜鸟,我在这里将介绍一下AppWeight的简单使用. 1.在介绍AppWidget之前,我们 ...

  2. ParentWindow属性及其一系列函数的作用——适合于那些不需要父控件管理内存释放的子控件

    TWinControl = class(TControl) property ParentWindow: HWnd read FParentWindow write SetParentWindow; ...

  3. Android 自定义支持快速搜索筛选的选择控件(一)

    Android 自定义支持快速搜索筛选的选择控件 项目中遇到选择控件选项过多,需要快速查找匹配的情况. 做了简单的Demo,效果图如下: 源码地址:https://github.com/whieenz ...

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

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

  5. (转载) Android RecyclerView 使用完全解析 体验艺术般的控件

    Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏  ...

  6. ANDROID L——Material Design详解(UI控件)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lolli ...

  7. Android判断Touch为滑动事件还是操作控件

    Android判断Touch为滑动事件还是操作控件 因为在项目中要判断WebView是否处于滚动状态,但它不像ListView有onScrollStateChanged方法来监听,要实现就得手动监听它 ...

  8. 【VS开发】 自己编写一个简单的ActiveX控件——详尽教程

    最近开始学ActiveX控件编程,上手不太容易,上网想找相关教程也没合适的,最后还是在师哥的指导下完成了第一个简单控件的开发,现在把开发过程贴出来与大家分享一下~ (环境说明--平台:vs2005:语 ...

  9. [MFC] 梳理一个简单的图片处理桌面软件中用到的MFC控件技巧

     前言 前些天应好友之拖,帮忙设计一个简单的图像处理的小软件.朋友把核心算法封装好了,但是是用openCV类似于console的编程环境,要我在此基础上改成MFC桌面程序.下图是做成之后的效果: 我是 ...

随机推荐

  1. javascript笔记整理(事件)

    一.事件驱动 1.事件javascript侦测到的用户的操作或是页面的一些行为(怎么发生的) 2.事件源引发事件的元素(发生在谁的身上) 3.事件处理程序对事件处理的程序或是函数 (发生了什么事) 二 ...

  2. java正则去掉小数点后多余0

    需求:已知字符串为一数字字符形式,多为float,double转换过来,将其后多余的0与.去掉. package test; /** * 去掉多余的.与0 * @author Hust * @Time ...

  3. java.lang.NoClassDefFoundError: org/apache/lucene/analysis/synonym/SynonymFilter

    2013-6-24 13:28:51 org.apache.solr.common.SolrException log 严重: java.lang.NoClassDefFoundError: org/ ...

  4. SSH-Struts(一)——基本原理

    简单介绍 Struts框架是MVC的一个实现,它非常好的结合了JSP.Servlet.JavaBean.Taglib等技术.它为MVC的各层提供了良好的支持,就像房地产商盖房子时先盖的大楼框架. 仅仅 ...

  5. 设计模式(Abstract Factory)抽象工厂

    1. 需求: 设计一个电脑组装程序,对于组装品牌电脑. 用零件组装(主板.硬盘.显示器)由品牌提供的所有. 让我们组装一台联想电脑,板子.由联想提供. (眼下仅仅有Lenovo和Dell两种品牌) 2 ...

  6. FindChildControl与FindComponent

    前两天编码遇到了要使用FindChildControl方法获取指定名称的TSpeedButton按钮,结果折腾了半天就是没得结果(基础不扎实,呵呵),于是赶紧搜索了下,补习关于这两个方法的用法. TW ...

  7. android——写xml

    在PersonService的基础上,加上savePersons(·····),这时的PersonService为: package com.njupt.xml; import java.io.Fil ...

  8. android面试题目大全<完结部分>,android笔试题目集锦

    1. 下列哪些语句关于内存回收的说明是正确的? (b ) A. 程序员必须创建一个线程来释放内存   B.内存回收程序负责释放无用内存    C.内存回收程序允许程序员直接释放内存    D.内存回收 ...

  9. Entity - 使用EF框架进行增删改查 - 数据库先行

    数据库先行:先创建数据库,然后进行增删查该操作. 要操作的表结构(表名:Tb_Category): 创建一个控制台程序: 添加一个ADO.NET实体数据模型: 1.对控制台程序右键 2.选择ADO.N ...

  10. 也谈C#之Json,从Json字符串到类代码

    原文:也谈C#之Json,从Json字符串到类代码  阅读目录 json转类对象 逆思考 从json字符串自动生成C#类  json转类对象 自从.net 4.0开始,微软提供了一整套的针对json进 ...