因?yàn)楣卷?xiàng)目需求猩系,需要實(shí)現(xiàn)widget 來刷新數(shù)據(jù)拒名,稀里糊涂的做,然后踩了很多坑顾彰,寫個文章來總結(jié)下widget一些實(shí)際上遇到的坑极阅。
官方API地址:官方api(中文,最美語言)
(方便查看涨享,中文的筋搏,比較詳細(xì))
如果widget簡單實(shí)用就參考下面文章:基本使用?這篇文章更多幫你理理一些思路,或者理清楚一下
1.Widget(小組件)具體是啥
你的Widget(小組件)都需要繼承AppWidgetProvider 灰伟,然后這個呢拆又,是繼承BroadcastReceiver,所以呢栏账,也不用我多說帖族,Widget(小組件)就是一個廣播,廣播呢挡爵,就會有我開發(fā)過程中的第一個坑
Widget(小組件)說沒就沒竖般,數(shù)據(jù)說不刷新就不刷新,雖然系統(tǒng)給了個固定刷新機(jī)制茶鹃,但是實(shí)際測試中涣雕,會根據(jù)你當(dāng)前測試機(jī)的型號不同艰亮,得到的效果也各不相同(不得不吐槽某充電xx分鐘,通話xx小時的某國產(chǎn)手機(jī))挣郭。具體原因可以看下Widget無響應(yīng)迄埃。個人也是直接抄答案沒有細(xì)想,最后稍微帶著經(jīng)驗(yàn)猜測如下:
Widget(小組件)說白了就是個廣播兑障,廣播一般都是發(fā)完就不會管后續(xù)的呢侄非,所以如果你想一個廣播一直前臺更新數(shù)據(jù)挺有局限的呢,所以我開發(fā)過程中會碰到Widget(小組件)說沒就沒現(xiàn)象特別多流译,并且你壓根找不到原因是啥逞怨。所以Widget(小組件)真就一個廣播,廣播备T瑁活(小弟不才叠赦,學(xué)藝不精不會),所以在onReceive 持久化數(shù)據(jù)不得行革砸。
@Override
public void onReceive(final Context context, Intent intent) {
敲黑板了除秀,這里處理數(shù)據(jù)很大概率會碰到無響應(yīng),或者接收不到廣播
super.onReceive(context, intent);
}
解決辦法:在你的自身的Provider 也就是你按照xxWidgetProvider extend AppWidgetProvider的不要做數(shù)據(jù)處理业岁,開啟個服務(wù) 按照我如下操作就行?
package com.haixue.highendclass.widget;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import com.haixue.highendclass.common.Constants;
import com.haixue.highendclass.utils.Ln;
import com.haixue.yijian.BuildConfig;
public class HxWidgetProvider extends AppWidgetProvider {
// 更新 widget 的廣播對應(yīng)的action 這個其實(shí)是刷新所有頁面的
? ? public static final StringTAG="zzzz";
? ? /**
* 接收窗口小部件點(diǎn)擊時發(fā)送的廣播
*/
? ? @Override
? ? public void onReceive(final Context context, Intent intent) {
super.onReceive(context, intent);
? ? }
// 當(dāng) widget 被初次添加 或者 當(dāng) widget 的大小被改變時鳞仙,被調(diào)用
? ? @Override
? ? public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle
newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
? ? }
/**
* 當(dāng)小部件從備份恢復(fù)時調(diào)用該方法
*/
? ? @Override
? ? public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
super.onRestored(context, oldWidgetIds, newWidgetIds);
? ? }
/**
* 每次窗口小部件被點(diǎn)擊更新都調(diào)用一次該方法
*/
? ? @Override
? ? public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 每次 widget 被創(chuàng)建時,開啟服務(wù)笔时,后續(xù)服務(wù)處理業(yè)務(wù)邏輯
? ? ? ? Intent intent=new Intent(context,WidgetService.class);
? ? ? ? intent.setAction(Constants.ACTION_WIDGET_REFRESH_DATA);
? ? ? ? intent.putExtra(Constants.TAG_WIDGET_UPDATE_DATA,appWidgetIds);
? ? ? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
? ? ? ? }else {
context.startService(intent);
? ? ? ? }
super.onUpdate(context, appWidgetManager, appWidgetIds);
? ? }
/**
* 當(dāng)最后一個該窗口小部件刪除時調(diào)用該方法棍好,注意是最后一個
*/
? ? @Override
? ? public void onDisabled(Context context) {
// 在最后一個 widget 被刪除時,終止服務(wù)
? ? ? ? Intent intent =new Intent(context, WidgetService.class);
? ? ? ? context.stopService(intent);
? ? ? ? super.onDisabled(context);
? ? }
}
也就是基本上的生命周期只是做個擺設(shè)允耿,你直接去Service處理邏輯就行借笙,只能說這里有點(diǎn)不負(fù)責(zé)任,沒有給代碼较锡,這里注意下?
context.startForegroundService(intent);和context.startService(intent);區(qū)別业稼,startForegroundService導(dǎo)致部分機(jī)型有widget更新消息一直在消息欄,這里算是沒有解決的坑蚂蕴,所以Widget(小組件)低散,其實(shí)也就開了個Service ,剩下都在Service中處理?
我也是參考了這個時鐘例子(但是并不那么完美)
一般更新widget操作:
AppWidgetManager manager = AppWidgetManager.getInstance(context);
int[] idLs = manager.getAppWidgetIds(new ComponentName(context.getPackageName(), HxWidgetProvider.class.getName()));
//用于遍歷所有保存的widget的id
for (int i : idLs) {
int appID = i;
? ? // 獲取 layout_hx_widget.xml 對應(yīng)的RemoteViews 只顯示一條數(shù)據(jù)
? ? RemoteViews remoteView =new RemoteViews(context.getPackageName(), R.layout.layout_hx_widget);
? ? //widget點(diǎn)擊刷新按鈕骡楼,刷新數(shù)據(jù)
? ? remoteView.setOnClickPendingIntent(R.id.iv_course_refresh, getRefreshIntent(context));
? ? initViewByData(remoteView, context);
? ? // 更新 widget
? ? appWidgetManager.updateAppWidget(appID, remoteView);
}
踩的坑第二個:重點(diǎn)來了熔号,以下代碼其實(shí)在Activity和Service中也是可以執(zhí)行的,也就是widget更新并不局限你的xxWidgetProvider?extend?AppWidgetProvider 這個廣播類中鸟整,所以保持widget引镊,其實(shí)保持個Service也是可以做到實(shí)時更新數(shù)據(jù)的,所有就xxWidgetProvider 開啟個Service就能保活弟头,Service狈宰ィ活應(yīng)該比Broadcast簡單,官方建議也是如此
踩的坑第三個:widget點(diǎn)擊事件赴恨,上述基本使用文章已經(jīng)告知你疹娶,widget只支持基礎(chǔ)控件,并且你如果直接用個View伦连,也是會報(bào)錯的蚓胸。具體為啥呢?畢竟所有的類都是它的子類除师,它都能支持,就不存在不支持的View類扔枫。
一般點(diǎn)擊事件如下:
RemoteViews remoteView =new RemoteViews(context.getPackageName(), R.layout.layout_hx_widget);
//widget點(diǎn)擊刷新按鈕汛聚,刷新數(shù)據(jù)
remoteView.setOnClickPendingIntent(R.id.iv_course_refresh, getRefreshIntent(context));
initViewByData(remoteView, context);
//定義你的PendingIntent?
private PendingIntent getRefreshIntent(Context context) {
Intent intent =new Intent(context, WidgetService.class);
? ? intent.putExtra(Constants.TAG_WIDGET_UPDATE_DATA, mIds);
? ? intent.setAction(Constants.ACTION_WIDGET_REFRESH_DATA);
? ? PendingIntent pi;
? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
pi = PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
? ? }else {
pi = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
? ? }
return pi;
}
重點(diǎn):PendingIntent.FLAG_UPDATE_CURRENT 這個參數(shù)很重要,決定了你的點(diǎn)擊事件是否是可以持續(xù)點(diǎn)擊??
重點(diǎn)小技巧短荐,點(diǎn)擊widget某個地方直接開啟App倚舀,或者指定App某個activity比較簡單,但是有個業(yè)務(wù)場景就是你app已經(jīng)開啟忍宋,你得跟launcher界面點(diǎn)擊app圖標(biāo)進(jìn)行一樣的操作的情況痕貌,你按照以下代碼設(shè)置
private PendingIntentgetAppIntent(Context context) {
Intent intent =new Intent(Intent.ACTION_MAIN);
? ? intent.addCategory(Intent.CATEGORY_LAUNCHER);
? ? intent.putExtra(Constants.TAG_WIDGET_OPEN_APP,TYPE_OPEN_OTHER);
? ? intent.setComponent(new ComponentName("你的包名", "你的啟動activity"));
? ? intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
? ? PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
? ? return pi;
}
以上就是widget一些體會吧,并沒有提供太多代碼糠排,有不正確的地方還希望校正