widget注意事项:
widget是个特殊的receiver
它重写了onReceiver方法
当发送广播到widget中时 不会响应
只有当接收到
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
widget的生命周期:
1.onEnabled方法:此方法在Widget第一次被创建的时候调用,并且只调用一次,此方法中常放入初始化数据,服务的操作。
2.onReceive方法:通BroadcastReceiver的OnReceive方法,但是这里有所不同的是,当接收到Widget操作时首先调用的是OnReceive方法,然后才是相关的操作方法。这也很好理解,Widget的是运行在桌面运用程序中的小控件,当自己的应用程序需要调用Widget是,就需要发送广播事件去调用。
3.onUpdate:Widget在固定的时间里更新时调用的方法。
4.onDeleted:Widget被删除时调用的方法。
5.onDisabled:所用Widget被删除是调用的方法,同onEnabled方法相对。
这里做下说明 测试机是android4.0.3
onEnabled onDisabled 发现从来没有调用过 不知道是什么问题
而.onUpdate在onReceive之前被调用了
AppWidgetManager.ACTION_APPWIDGET_UPDATE不会调用onUpdate方法
可以看源码
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);
}
}
}
明白了吧 我们发的intent没有extra
结合这次做的音乐播放器 分析一下
需求是 在activity中改变播放状态 同时让widget更新状态(文字 图片等) 需要从activity中获取信息
本来的想法是在activity中发送广播到widget 通过intent的传递信息
可发现widget比较特殊 只有接收到那4大广播才会响应onReceiver
尝试这一个intent设置了两个action(脑袋错乱了 intent只能有一个action 后者会覆盖前者)
所以这条路失败
然后去看了下系统自带音乐播放器源码 它是通过得到一个Widget对象 来实现更新内容的
这里说明一下我的更新方式
我是用了remoteView来实现自定义widget视图的
它的更新方式如下:
AppWidgetManager instance = AppWidgetManager.getInstance(this.context);
rv = new RemoteViews(context.getPackageName(), R.layout.widget);
String title = mp3Info.getTitle();
String artist =mp3Info.getArtist();
rv.setTextViewText(R.id.tv_name, title);
rv.setTextViewText(R.id.tv_musician, artist);
if(!pause){
rv.setImageViewResource(R.id.iv_stop, R.drawable.btn_playback_pause);
}else {
rv.setImageViewResource(R.id.iv_stop, R.drawable.btn_playback_play);
}
instance.updateAppWidget(this.appWidgetIds[0], rv);
因为无法通过广播来实时更新 所以想着通过一个widget对象来更新
context,appWIdgetIds则由自己来传入
一开始 传入了是MainActivity.this 和appWidgetManager.getAppWidgetIds(name)结果不行
然后 开始找问题 先从context下手
这里先说一下 widgetManager管理widget 每当接受到一个广播都会创建一个新的widget对象
但是每个对象中传入的context都是同一个
于是尝试这把这个context保存起来 再调用更新方法 结果还是不行
那么肯定是appWIdgetIds也有问题了
打印了一下appWidgetManager.getAppWidgetIds(name) 和一开始 onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 传入的appWidgetIds
结果发现 不是一个对象 好吧问题解决了
把系统创建widget对象传入的context和appWidgetIds保存起来 再调用更新方法就好了
贴一下源码
public class Widget extends AppWidgetProvider {
private RemoteViews rv;
private AppWidgetManager appWidgetManager;
private int[] appWidgetIds;
private static Widget instance;
public Context context;
static synchronized Widget getInstance() {
if (instance == null) {
instance = new Widget();
}
return instance;
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
this.context = context;
}
public void update(Mp3Info mp3Info,boolean pause, Context con){
AppWidgetManager instance = AppWidgetManager.getInstance(this.context);
rv = new RemoteViews(context.getPackageName(), R.layout.widget);
String title = mp3Info.getTitle();
String artist =mp3Info.getArtist();
rv.setTextViewText(R.id.tv_name, title);
rv.setTextViewText(R.id.tv_musician, artist);
if(!pause){
rv.setImageViewResource(R.id.iv_stop, R.drawable.btn_playback_pause);
}else {
rv.setImageViewResource(R.id.iv_stop, R.drawable.btn_playback_play);
}
instance.updateAppWidget(this.appWidgetIds[0], rv);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
if (instance == null) {
instance = new Widget();
}
if (rv == null) {
this.appWidgetManager = appWidgetManager;
instance.appWidgetIds = appWidgetIds;
instance.context=context;
init(context, appWidgetManager, appWidgetIds);
}
}
/**
* 初始化
*
* @param context
* @param appWidgetManager
* @param appWidgetIds
*/
protected void init(final Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
new Thread(new Runnable() {
@Override
public void run() {
ActivityManager am = (ActivityManager) context.getSystemService("activity");
List runningAppProcesses = am.getRunningAppProcesses();
for (RunningAppProcessInfo i : runningAppProcesses) {
if (context.getPackageName().equals(i.processName))
return;
}
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}).start();
rv = new RemoteViews(context.getPackageName(), R.layout.widget);
CharSequence title = "";
CharSequence artist = "";
rv.setTextViewText(R.id.tv_name, title);
rv.setTextViewText(R.id.tv_musician, artist);
Intent playIntent = new Intent("com.dlj.musicplayer.MainActivity.ComplateBoardcast.play");
Intent nextIntent = new Intent(
"com.dlj.musicplayer.MainActivity.ComplateBoardcast.playNext");
Intent openIntent = new Intent(context, MainActivity.class);
PendingIntent stopPIntent = PendingIntent.getBroadcast(context, 0, playIntent, 0);
PendingIntent nextPIntent = PendingIntent.getBroadcast(context, 0, nextIntent, 0);
PendingIntent openPIntent = PendingIntent.getBroadcast(context, 0, openIntent, 0);
rv.setOnClickPendingIntent(R.id.iv_stop, stopPIntent);
rv.setOnClickPendingIntent(R.id.iv_next, nextPIntent);
rv.setOnClickPendingIntent(R.id.rl_notify, openPIntent);
appWidgetManager.updateAppWidget(appWidgetIds[0], rv);
}
}
精华是这两个
把这两个保存起来 再用他们来更新数据就行了
instance.appWidgetIds = appWidgetIds;
instance.context=context;
尼玛 研究了半天才弄出来的 结果看了老师的源码 发现个更简洁的
appWidgetManager.updateAppWidget(
new ComponentName(getApplicationContext(), TimeWidgetProvider.class), remoteView);
吐血身亡
又碰到一个怪问题
当响应widget的remoteView的点击事件时 或者onUpdate或onDelete时
如果当前系统中没有widget对应的进程 则会创建一个进程
而这个进程中存放的activity 就是入口activity(MainActivity)
重点!!!!!!但此时的activity 比较特殊 它没有调用oncreate方法 没有界面 就是一个空壳而已