在AndroidManifest.xml中进行如下配置:
<receiver android:name="com.yu.receiver.SaferAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/safer_appwidget_info" />
receiver>
定义部件需要的AppWidgetProvider (本质上是一个Receiver)
package com.yu.receiver;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import com.yu.service.KillProcesWidgetService;
public class SaferAppWidgetProvider extends AppWidgetProvider {
/**
* 在每次操作的结束被调用
*/
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
/**
* 只要有新的桌面小控件创建时就会调用
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
// 开启更新小部件的服务
context.startService(new Intent(context,KillProcesWidgetService.class));
}
/**
* 每次删除桌面小控件时调用
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
}
/**
* 第一次创建小控件时才会调用
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
}
/**
* 当所有的桌面小控件都删除后调用
*/
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
// 关闭更新小部件的服务
context.startService(new Intent(context,KillProcesWidgetService.class));
}
/**
* Called in response to the AppWidgetManager.ACTION_APPWIDGET_RESTORED broadcast
* when instances of this AppWidget provider have been restored from backup
*/
@Override
public void onRestored(Context context, int[] oldWidgetIds,
int[] newWidgetIds) {
super.onRestored(context, oldWidgetIds, newWidgetIds);
}
}
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_process_safer"
android:minHeight="75.0dip" -minHeight不宜过大,否则widget无法显示-->
android:minWidth="294.0dip"
android:updatePeriodMillis="0" /><!-设置为0,手动处理更新时间-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/widget_bg_portrait"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/widget_bg_portrait_child"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_count_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运行的程序" />
<TextView
android:id="@+id/tv_freeMem_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="可用内存" />
LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:padding="5dp"
android:orientation="vertical" >
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="安全卫士" />
<Button
android:id="@+id/bt_clean"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/bt_selector"
android:layout_marginTop="6dp"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:textColor="#000"
android:text="一键清理" />
LinearLayout>
LinearLayout>
1、 通过AppWidgetManger的getInstance方法获得widget管理器
2、 实例化Timer对象,并用TimerTask创建一个线程,通过timer的schedule方法开启一个定时任务
3、在TimerTask的run方法中创建widget所需的RemoteViews,并设置相应的控件内容和事件监听
4、创建Component组件,将RemoteViews和ComponentName 设置给widget,并更新widget
package com.yu.service;
import java.util.Timer;
import java.util.TimerTask;
import com.yu.receiver.SaferAppWidgetProvider;
import com.yu.safer.R;
import com.yu.utils.SystemInfoUtils;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.text.format.Formatter;
import android.widget.RemoteViews;
/**
* 进程清理小控件
* @author Administrator
*
*/
public class KillProcesWidgetService extends Service {
AppWidgetManager awm;
ComponentName appWidgetProvider;
Timer timer;
TimerTask task;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 获得widget管理者
awm = AppWidgetManager.getInstance(this);
// 开启定时任务 每隔5秒更新widget
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
// 初始化一个远程的view(RemoteViews)
RemoteViews views = new RemoteViews(getPackageName(), R.layout.widget_process_safer);
// 获取正在运行的进程数
int count = SystemInfoUtils.getRunningAppCount(KillProcesWidgetService.this);
// 获取可用的内存大小
String freeMem = Formatter.formatFileSize(KillProcesWidgetService.this,
SystemInfoUtils.getFreeMemoryInfo(KillProcesWidgetService.this));
// 设置views内容
views.setTextViewText(R.id.tv_count_widget, "运行的程序:"+count+"个");
views.setTextViewText(R.id.tv_freeMem_widget, "可用内存:"+freeMem);
Intent i = new Intent();
//设置一个隐式意图
i.setAction("com.yu.safer.widget");
// 通过PendingIntent 开启一个广播 用于清理进程
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, i, 0);
// 设置点击事件
views.setOnClickPendingIntent(R.id.bt_clean, pendingIntent );
appWidgetProvider = new ComponentName(getApplicationContext(), SaferAppWidgetProvider.class);
awm.updateAppWidget(appWidgetProvider, views);
}
};
timer.schedule(task, 1000, 5000);
return super.onStartCommand(intent, flags, startId);
}
}
① AppWidgetManager$updateAppWidget
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
if (mService == null) {
return;
}
try { // mService 即 AppWidgetServiceImpl
mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
② 接着跨进程调用AppWidgetServiceImpl的updateAppWidgetIds方法,该方法内部调用重载方法如下
private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
RemoteViews views, boolean partially) {
final int userId = UserHandle.getCallingUserId();
if (appWidgetIds == null || appWidgetIds.length == 0) {
return;
}
// Make sure the package runs under the caller uid.
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) { // 同步操作
ensureGroupStateLoadedLocked(userId);
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) { // 遍历更新可以查找到的Widget组件
final int appWidgetId = appWidgetIds[i];
// NOTE: The lookup is enforcing security across users by making
// sure the caller can only access widgets it hosts or provides.
Widget widget = lookupWidgetLocked(appWidgetId,
Binder.getCallingUid(), callingPackage);
if (widget != null) {
updateAppWidgetInstanceLocked(widget, views, partially);
}
}
}
}
③ 接下来继续调用AppWidgetServiceImpl的updateAppWidgetInstanceLocked方法来更新
private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
boolean isPartialUpdate) {
if (widget != null && widget.provider != null
&& !widget.provider.zombie && !widget.host.zombie) {
if (isPartialUpdate && widget.views != null) { // 部分更新还是更新全部
// For a partial update, we merge the new RemoteViews with the old.
widget.views.mergeRemoteViews(views);
} else {
// For a full update we replace the RemoteViews completely.
widget.views = views;
}
int memoryUsage;
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
(widget.views != null) &&
((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) {
widget.views = null;
throw new IllegalArgumentException("RemoteViews for widget update exceeds"
+ " maximum bitmap memory usage (used: " + memoryUsage
+ ", max: " + mMaxWidgetBitmapMemory + ")");
}
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
}
④ 继续调用AppWidgetServiceImpl的scheduleNotifyUpdateAppWidgetLocked
private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
long requestId = REQUEST_COUNTER.incrementAndGet();
if (widget != null) {
widget.updateRequestIds.put(ID_VIEWS_UPDATE, requestId);
}
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
args.arg3 = (updateViews != null) ? updateViews.clone() : null;
args.arg4 = requestId;
args.argi1 = widget.appWidgetId;
mCallbackHandler.obtainMessage(
CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,
args).sendToTarget(); // 发送通知来异步处理
}
⑤ 在mCallbackHandler中处理消息,调用handleNotifyUpdateAppWidget方法
private final class CallbackHandler extends Handler {
public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1;
public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
public static final int MSG_NOTIFY_PROVIDERS_CHANGED = 3;
public static final int MSG_NOTIFY_VIEW_DATA_CHANGED = 4;
public CallbackHandler(Looper looper) {
super(looper, null, false);
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_NOTIFY_UPDATE_APP_WIDGET: {
SomeArgs args = (SomeArgs) message.obj;
Host host = (Host) args.arg1;
IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
RemoteViews views = (RemoteViews) args.arg3;
long requestId = (Long) args.arg4;
final int appWidgetId = args.argi1;
args.recycle();
handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views, requestId);
} break;
...
⑥ 在handleNotifyUpdateAppWidget方法中调用AppWidgetHost的updateAppWidget方法来更新
private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
int appWidgetId, RemoteViews views, long requestId) {
try {
callbacks.updateAppWidget(appWidgetId, views);
host.lastWidgetUpdateRequestId = requestId;
} catch (RemoteException re) {
synchronized (mLock) {
Slog.e(TAG, "Widget host dead: " + host.id, re);
host.callbacks = null;
}
}
}
⑦ 接下来调用了AppWidgetHost$updateAppWidget,继续发送消息来更新
public void updateAppWidget(int appWidgetId, RemoteViews views) {
if (isLocalBinder() && views != null) {
views = views.clone();
}
Handler handler = mWeakHandler.get();
if (handler == null) {
return;
}
Message msg = handler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
msg.sendToTarget();
}
⑧ 在AppWidgetHost的UpdateHandler中处理更新消息
class UpdateHandler extends Handler {
public UpdateHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case HANDLE_UPDATE: { // 更新
updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
break;
}
...
}
}
}
⑨ 在UpdateHandler中调用updateAppWidgetView来更新,最终调用AppWidgetHostView的updateAppWidget更新view
void updateAppWidgetView(int appWidgetId, RemoteViews views) {
AppWidgetHostView v;
synchronized (mViews) {
v = mViews.get(appWidgetId);
}
if (v != null) {
v.updateAppWidget(views);
}
}
⑩ 接下来调用到了AppWidgetHostView的updateAppWidget方法,这个方法调用了applyRemoteViews方法
public void updateAppWidget(RemoteViews remoteViews) {
applyRemoteViews(remoteViews);
}
11. applyRemoteViews方法最终调用了RemoteViews的apply/reapply来更新,apply/reapply方法最终通过反射调用了view的属性来更新
protected void applyRemoteViews(RemoteViews remoteViews) {
...
boolean recycled = false;
View content = null;
Exception exception = null;
...
if (remoteViews == null) {
if (mViewMode == VIEW_MODE_DEFAULT) {
// We've already done this -- nothing to do.
return;
}
content = getDefaultView();
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
if (mAsyncExecutor != null) {
inflateAsync(remoteViews);
return;
}
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
mRemoteContext = getRemoteContext();
int layoutId = remoteViews.getLayoutId();
// If our stale view has been prepared to match active, and the new
// layout matches, try recycling it
if (content == null && layoutId == mLayoutId) { // 已经加载过了则更新
try {
remoteViews.reapply(mContext, mView, mOnClickHandler); // reapply只更新界面
content = mView;
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
} catch (RuntimeException e) {
exception = e;
}
}
// Try normal RemoteView inflation
if (content == null) {
try {
content = remoteViews.apply(mContext, this, mOnClickHandler); // apply需要加载布局并更新界面
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
}
}
mLayoutId = layoutId;
mViewMode = VIEW_MODE_CONTENT;
}
applyContent(content, recycled, exception);
updateContentDescription(mInfo);
}
12 接下来调用RemoteViews的apply/performApply方法,这两个方法都会调用performApply来执行Action的apply
/** @hide */
public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
RemoteViews rvToApply = getRemoteViewsToApply(context);
View result = inflateView(context, rvToApply, parent);
loadTransitionOverride(context, handler);
rvToApply.performApply(result, parent, handler);
return result;
}
13 performApply方法遍历要执行的动作集合(每一次更新操作都对应一个Action),然后调用Action的apply方法来执行更新
private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
if (mActions != null) {
handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
a.apply(v, parent, handler);
}
}
}
14 看Action的一个实现ReflectionAction的apply的执行,很明显通过反射来执行更新
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final View view = root.findViewById(viewId);
if (view == null) return;
Class> param = getParameterType(); // 获取Class类型,如int.class , Intent.class
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
try {
getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value)); // 反射调用相关方法
} catch (ActionException e) {
throw e;
} catch (Exception ex) {
throw new ActionException(ex);
}
}
总结:
更新流程为: AppWidgetManager$updateAppWidget --> AppWidgetServiceImpl$updateAppWidgetIds ..--> AppWidgetHost$updateAppWidget ..--> AppWidgetHostView$updateAppWidget ..--> remoteViews$apply/reapply ..--> Action$apply
RemoteViews相关内容可参看安卓学习笔记之RemoteViews