实时更新widget

来源:互联网 发布:淘宝卖家强制取消订单 编辑:程序博客网 时间:2024/06/15 20:04

widget注意事项:

widget是个特殊的receiver 

它重写了onReceiver方法

当发送广播到widget中时 不会响应 

只有当接收到

  • ACTION_APPWIDGET_UPDATE
  • ACTION_APPWIDGET_DELETED
  • ACTION_APPWIDGET_ENABLED
  • ACTION_APPWIDGET_DISABLED
这4个广播时才会调用 onreceiver 



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 <span style="color:#ff0000;">extras = intent.getExtras();</span>            if (extras != null) {                int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);                if (<span style="color:#ff0000;">appWidgetIds != null && appWidgetIds.length > 0</span>) {                    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);


可以看到 要实现更新 需要几个重要元素context,appWIdgetIds


因为无法通过广播来实时更新 所以想着通过一个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;}@Overridepublic 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);}@Overridepublic 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;<span style="color:#ff0000;">instance.appWidgetIds = appWidgetIds;instance.context=context;</span>init(context, appWidgetManager, appWidgetIds);}}/** * 初始化 *  * @param context * @param appWidgetManager * @param appWidgetIds */protected void init(final Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {new Thread(new Runnable() {@Overridepublic void run() {ActivityManager am = (ActivityManager) context.getSystemService("activity");List<RunningAppProcessInfo> 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);}}
精华是这两个 

 把这两个保存起来 再用他们来更新数据就行了

<span style="color:#ff0000;">instance.appWidgetIds = appWidgetIds;instance.context=context;</span>



尼玛 研究了半天才弄出来的 结果看了老师的源码 发现个更简洁的 

appWidgetManager.updateAppWidget(
new ComponentName(getApplicationContext(), TimeWidgetProvider.class), remoteView);

吐血身亡



又碰到一个怪问题 

响应widget的remoteView的点击事件时 或者onUpdate或onDelete时 

如果当前系统中没有widget对应的进程 则会创建一个进程

而这个进程中存放的activity 就是入口activity(MainActivity)

重点!!!!!!但此时的activity 比较特殊 它没有调用oncreate方法 没有界面  就是一个空壳而已






0 0