实时更新widget

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 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);


可以看到 要实现更新 需要几个重要元素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;
	}

	@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方法 没有界面  就是一个空壳而已






你可能感兴趣的:(安卓)