Widget 工作原理浅析
Android Widget 也称桌面小部件,是系统应用开发层面的一部分,可以把一个控件嵌入到另一个进程的串口里面。
一、自定义AppWidget
1 AppWidgetProvider
继承广播,用于更新widget信息,实际是一个广播接受者,核心方法
- onUpdate:更新时触发,直接参与到了widget界面的显示更新
- onReceiver:收到特定广播触发
主要功能接收AppWidgetService发出的相应广播,做出相应的处理
public class ThirdAppWidget extends AppWidgetProvider {
@Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); ComponentName componentName = new ComponentName(context, ThirdAppWidget.class); if ("btn_test".equals(intent.getAction())) { Toast.makeText(context, TAG, Toast.LENGTH_SHORT).show(); Log.i(TAG, "btntest i" + i); i++; RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.third_app_widget); views.setTextViewText(R.id.appwidget_text, "test" + i); appWidgetManager.updateAppWidget(componentName, views); } } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // There may be multiple widgets active, so update all of them this.appWidgetIds = appWidgetIds; Log.i(TAG, "onUpdate"); for (int appWidgetId : appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId); } }
updateAppWidget 可替换为 parttillyUpdateWidget()方法部分更新UI,提高效率
onUpdate遵循的基本流程
1、创建RemoteView
2、调用AppWidgetManager.updateAppWidget方法更新view
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { Log.i(TAG, "updateAppWidget"); CharSequence widgetText = context.getString(R.string.appwidget_text); // 1 Construct the RemoteViews object RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.third_app_widget); views.setTextViewText(R.id.appwidget_text, widgetText); Intent intent = new Intent(context, ThirdAppWidget.class); intent.setAction("btn_test"); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE); views.setOnClickPendingIntent(R.id.btn, pendingIntent); // 2 Instruct the widget manager to update the widget appWidgetManager.updateAppWidget(appWidgetId, views); }
2 编写配置文件
主要是widget默认布局和一些参数。对应类AppWidgetProviderInfo
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:initialKeyguardLayout="@layout/third_app_widget" android:initialLayout="@layout/third_app_widget" android:minWidth="140dp" android:minHeight="40dp" android:previewImage="@drawable/example_appwidget_preview" android:resizeMode="horizontal|vertical" android:updatePeriodMillis="20000"/>
3 清单文件中进行注册和配置
<receiver android:name=".ThirdAppWidget"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.MODE_UPDATE" /> <action android:name="btn_test" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/third_app_widget_info" /> </receiver>
二、AppWidget加载显示
具有系统应用权限的app才可绑定显示widget,system\app目录下
1 创建APPWidgetHost实列。其主要功能
(1)开启Widget更新监听
private fun init() { //1024 host_id 会被WidgetService记录,依据此id更新widget mAppWidgetHost = AppWidgetHost(applicationContext, 1024); mAppWidgetManager = AppWidgetManager.getInstance(applicationContext) mPackageManager = application.packageManager mUserManager = applicationContext.getSystemService(Context.USER_SERVICE) as UserManager mAllWidgetProviders = getAllProviders(); loadAllBindWidgetProviders() mAppWidgetHost!!.startListening() }
(2)申请appwidgetId,并通过AppWidgetService进行绑定
val appWidgetId = mAppWidgetHost!!.allocateAppWidgetId()
val success: Boolean = mAppWidgetManager!!.bindAppWidgetIdIfAllowed(
appWidgetId,
info.profile,
info.provider,
Bundle()
)
(3)创建AppWidgetHostView实例
AppWidgetHostView是真正加载视图的地方,通过AppWidgetService查询appWidgetId对应的RemoteViews,传递给AppWidgetHostView
private fun createWidgetView(provider: ComponentName): AppWidgetHostView? { val info: AppWidgetProviderInfo? = getAppWidgetProviderInfo(provider) if (info == null) { Log.i("aaaaaa", "getAppWidgetProviderInfo is null ") return null } val appWidgetId = mAppWidgetHost!!.allocateAppWidgetId() val success: Boolean = mAppWidgetManager!!.bindAppWidgetIdIfAllowed( appWidgetId, info.profile, info.provider, Bundle() ) Log.i("aaaaa", "appWidgetID:" + appWidgetId + "bindSuccess:" + success + "mWidgetProvider:" + info) if (!success) { mAppWidgetHost?.deleteAppWidgetId(appWidgetId); } val view = mAppWidgetHost!!.createView(applicationContext, appWidgetId, info) appWidgetHostViews!!.add(view) return view }
添加权限
<uses-permission android:name="android.permission.BIND_APPWIDGET" /> <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
2 获取AppWidgetProvideinfo
主要保存widget相关信息,如布局,大小,更新频率等,在widget所在工程,xml文件路径下配置:
third_app_widget_info.xml
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:initialKeyguardLayout="@layout/third_app_widget" android:initialLayout="@layout/third_app_widget" android:minWidth="140dp" android:minHeight="40dp" android:previewImage="@drawable/example_appwidget_preview" android:resizeMode="horizontal|vertical" android:updatePeriodMillis="20000"/>
参考:(15条消息) Android AppWidget系统框架_iteye_6233的博客-CSDN博客
(15条消息) android中AppWidgetManager_写Android的媛运气不会太差的博客-CSDN博客
实时更新widget_Android技巧_积木网(gimoo.net)
Android Widget工作原理浅析 - Hi ECARX云文档 (feishu.cn)