之前在项目上碰到了一个问题:刚开机的时候点击主屏幕的全局搜索框没反应,需要等几十秒之后才会正常反应。
刚开始猜测是在等待什么初始化条件,但从应用栏里启动全局搜索却很快,因此可以排除这种情况,于是打算从源码层去看看。
在launcher里并未找到这个全局搜索的相关代码,因此怀疑这是一个AppWidget,在android代码中全局搜索“android.appwidget.action.APPWIDGET_UPDATE”,搜索这个的原因是按照的AppWidget的实现标准都需要在AndroidManifest.xml里进行声明,否则不会在AppWidget列表里显示,果然在QuickSearchBox里发现了有提供搜索的AppWidget。看下实现的类
public class SearchWidgetProvider extends BroadcastReceiver
{
......
}
直接继承的BroadcastReceiver ? 一般来讲,实现一个AppWidget都是继承至AppWidgetProvider,然后重写里面的一些函数,这里却直接继承了BroadcastReceiver,去看看AppWidgetProvider都干了什么。
public class AppWidgetProvider extends BroadcastReceiver {
/**
* Constructor to initialize AppWidgetProvider.
*/
public AppWidgetProvider() {
}
/**
* Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various
* other methods on AppWidgetProvider.
*
* @param context The Context in which the receiver is running.
* @param intent The Intent being received.
*/
// BEGIN_INCLUDE(onReceive)
public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
} else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
this.onDeleted(context, new int[] { appWidgetId });
}
} else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
&& extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS);
this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
appWidgetId, widgetExtras);
}
} else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
} else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
} else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (oldIds != null && oldIds.length > 0) {
this.onRestored(context, oldIds, newIds);
this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
}
}
}
}
// END_INCLUDE(onReceive)
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
}
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, Bundle newOptions) {
}
public void onDeleted(Context context, int[] appWidgetIds) {
}
public void onEnabled(Context context) {
}
public void onDisabled(Context context) {
}
public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
}
原来AppWidgetProvider也是继承至BroadcastReceiver,实现了onReceive方法,定义了一些自定义的函数。在onReceive方法中接收到不同的广播调用不同的函数。因此AppWidgetProvider并不是重点,直接跳过它也是可行的。
接下来看看AppWidget是如何生效的。
@Override
public void onReceive(Context context, Intent intent) {
if (DBG) Log.d(TAG, "onReceive(" + intent.toUri(0) + ")");
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
// nothing needs doing
} else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Log.d(TAG, "update received");
updateSearchWidgets(context);
}else {
if (DBG) Log.d(TAG, "Unhandled intent action=" + action);
}
}
收到ACTION_APPWIDGET_UPDATE广播后更新widget。
public void updateWidget(Context context,AppWidgetManager appWidgetMgr) {
if (DBG) Log.d(TAG, "Updating appwidget " + mAppWidgetId);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget);
setOnClickActivityIntent(context, views, R.id.search_widget_text,
mQueryTextViewIntent);
// Voice Search button
if (mVoiceSearchIntent != null) {
setOnClickActivityIntent(context, views, R.id.search_widget_voice_btn,
mVoiceSearchIntent);
views.setViewVisibility(R.id.search_widget_voice_btn, View.VISIBLE);
} else {
views.setViewVisibility(R.id.search_widget_voice_btn, View.GONE);
}
appWidgetMgr.updateAppWidget(mAppWidgetId, views);
}
通过layout生成AppWidget的显示画面,设置显示画面上的交互事件,这里有一个输入框和一个语音按钮,因此分比对两个控件设置的响应的Intent,最后调用AppWidgetManager对widget进行更新。可以看出appWidgetMgr.updateAppWidget(mAppWidgetId, views)这里才是真正让AppWidget生效的部分。
那AndroidMainefest中声明android.appwidget.action.APPWIDGET_UPDATE是干什么的呢?
有两个用途:
1. 接收AppWidget相关的几个广播
2. 将其声明成AppWidget,如果不按照这种方式,那AppWidget列表里将不会显示。
现在回到问题,为什么刚开机时,AppWidget不响应呢?其实就是刚开机时,还没有收到广播,未设定响应它的Intent。如果要解决它,有两种办法。
1. 将广播“android.appwidget.action.APPWIDGET_UPDATE”改为前台广播,提升广播的处理速度。
android/framework/base/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
intent.setComponent(provider.info.provider);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); //设置为前台广播
sendBroadcastAsUser(intent, provider.info.getProfile());
}
2. 通过其他手段来启动AppWidget设置流程。在其他流程不变的情况下,可以同时注册开机广播,在开机广播中做AppWidget设定的流程。当然也可以通过其它应用,比如launcher等启动比较早的应用里,主动调起提供AppWidget的应用并执行此流程。
@Override
public void onReceive(Context context, Intent intent) {
if (DBG) Log.d(TAG, "onReceive(" + intent.toUri(0) + ")");
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
// nothing needs doing
} else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
updateSearchWidgets(context);
} else if (ACTION_BOOT.equals(action)) {
updateSearchWidgets(context); //收到开机广播,执行appwidget更新流程
}else {
if (DBG) Log.d(TAG, "Unhandled intent action=" + action);
}
}
注:以上为个人分析心得,如有错误,欢迎指正。
原文地址: https://blog.csdn.net/chengchaooppo/article/details/97259706