Android Widget 基礎介紹以及常見問題

本文是 Android Widget(小部件) 系列的第一篇拇砰,主要是對 Android widget (小部件)基本原理、開發(fā)流程、以及常見問題做了簡單的介紹。
本系列的目的是通過對Android 小部件的梳理,了解小部件刷新流程、恢復流程、以及系統(tǒng)發(fā)生變化時,小部件是如何適配的常挚,解決在開發(fā)小部件過程中遇到的問題。系列文章大部份來自源碼的解讀稽物,內(nèi)容非常多奄毡,也非常容易遺忘,因此記錄分享贝或。

系列文章
Android Widget 基礎介紹以及常見問題
安卓小部件刷新源碼解析一非列表
安卓小部件(APPWidget)刷新源碼解析一列表

一吼过、Android Widget 原理常見問題

1、小部件是什么咪奖?

image.png

App widgets are miniature application views that can be embedded in other applications (such as the home screen) and receive periodic updates盗忱。
通俗解釋:一個能夠定期刷新并且加到其他應用上的微型視圖。
官網(wǎng)

2羊赵、小部件的運行機制是什么趟佃?

image.png
  • 通過 AppWidgetProvider 定義小部件的行為
  • 通過 RemoteView 和布局文件定義小部件的UI
  • 通過AppWidgetManager 更新視圖
  • 在manifeset 里注冊 AppWidgetProvider(繼承于廣播)扇谣,設置監(jiān)聽的action以及配置文件

3、RemoteView如何工作闲昭?

RemoteView 繼承于Parcelable罐寨,可在進程間傳遞。RemoteView 會將每一個設置的行為轉換成相應的Action序矩。在Host 測時再將Action 翻譯成對應的行為鸯绿。

4、小部件運行在什么進程簸淀?

小部件的運行邏輯需要分為三部分:AppWidgetProvider 中的邏輯運行在小部件所在應用進程瓶蝴。小部件查找以及權限校驗的邏輯運行在system_process中。小部件渲染邏輯在host 進程中租幕。

二舷手、開發(fā)中常見問題

1、開發(fā)一個小部件有哪必要流程令蛉?

  1. 新建一個類繼承AppWidgetProvider用于定義主要的邏輯和行為
public class ExampleAppWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
     // 更新邏輯
     }
}
  1. 新建一個配置文件描述AppWidgetProviderInfo 信息
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="40dp" // 最小寬聚霜,用于計算橫向網(wǎng)格數(shù)
        android:minHeight="40dp" // 最小高狡恬,用于計算縱向網(wǎng)格數(shù)
        android:updatePeriodMillis="86400000" // 刷新時間間隔珠叔,最小為30min
        android:previewImage="@drawable/preview" //定義預覽圖片
        android:initialLayout="@layout/example_appwidget" 定義初始化布局,remoteView 布局未加載結束前視圖
        android:configure="com.example.android.ExampleAppWidgetConfigure" //定義設置頁
        android:resizeMode="horizontal|vertical" //定義尺寸模式
        android:widgetCategory="home_screen">  //定義種類弟劲,有桌面祷安、鎖屏、輸入法
    </appwidget-provider>
  1. 在AndroidManifest.xml 中注冊
<receiver android:name="ExampleAppWidgetProvider" >
         // 監(jiān)聽更新的acion
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data android:name="android.appwidget.provider"
                   android:resource="@xml/example_appwidget_info" />
</receiver>

2兔乞、如何設置minWidth 和 minHeight

minWidth 和 minHeight 主要用于計算橫向和縱向所占格子數(shù)汇鞭,不通廠商計算方式不同,但大概率都會符合谷歌規(guī)范規(guī)范

image.png

  • 4*2 橫向范圍 250~320 縱向是110~180
  • 2*2 橫向范圍110~180 縱向是110~180

3庸追、如何AppWidgetProvider 如何更新小部件霍骄?

// appWidgetManager和widgetId 從 onUpdate 方法中獲取
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
          R.layout.xxx);
appWidgetManager.updateAppWidget(widgetId, remoteViews);

4、應用里如何更新小部件淡溯?

RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.xxx);
AppWidgetManager appWidgetManager = AppWidgetManager.*getInstance*(context);
// NormalExampleWidgetProvider 為小部件組件名字读整,這里僅示例
ComponentName componentName = new ComponentName(context, NormalExampleWidgetProvider.class);
appWidgetManager.updateAppWidget(componentName, remoteViews);

5、如何設置點擊事件咱娶?

// 生成PendingIntent
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//生成 RemoteViews 關聯(lián) PendingIntent
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// 關聯(lián) widget 和 RemoteViews
appWidgetManager.updateAppWidget(appWidgetId, views);

6米间、Widget 中 List 設置了setRemoteAdapter,第二次添加該小部件時膘侮,為什么沒有調(diào)用onGetViewFactory 屈糊?

原因可能是RemoteViewsAdapter 復用,系統(tǒng)認為沒有數(shù)據(jù)改變琼了,導致沒有回調(diào)onGetViewFactory逻锐,這個在google demo 也有說明。

  1. 原因分析
class AbsListView {
    public void setRemoteViewsAdapter(Intent intent, boolean isAsync) {
    // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
    // service handling the specified intent.
    if (mRemoteAdapter != null) {
        Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
        Intent.FilterComparison fcOld = new Intent.FilterComparison(
                mRemoteAdapter.getRemoteViewsServiceIntent());
        // 比較兩個是否        
        if (fcNew.equals(fcOld)) {
            return;
        }
    } 
    mDeferNotifyDataSetChanged = false;
    // Otherwise, create a new RemoteViewsAdapter for binding
    mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this, isAsync);
    if (mRemoteAdapter.isDataReady()) {
        setAdapter(mRemoteAdapter);
    }
}
}
class Intent {   
    public boolean equals(@Nullable Object obj) {
        if (obj instanceof FilterComparison) {
            Intent other = ((FilterComparison)obj).mIntent;
            return mIntent.filterEquals(other);
        }
        return false;
    }
    public boolean filterEquals(Intent other) {
        if (other == null) {
            return false;
        }
        if (!Objects.equals(this.mAction, other.mAction)) return false;
        if (!Objects.equals(this.mData, other.mData)) return false;
        if (!Objects.equals(this.mType, other.mType)) return false;
        if (!Objects.equals(this.mIdentifier, other.mIdentifier)) return false;
        if (!(this.hasPackageEquivalentComponent() && other.hasPackageEquivalentComponent())
                && !Objects.equals(this.mPackage, other.mPackage)) {
            return false;
        }
        if (!Objects.equals(this.mComponent, other.mComponent)) return false;
        if (!Objects.equals(this.mCategories, other.mCategories)) return false;
        return true;
    }
}
  1. 解決方案
// Here we setup the intent which points to the StackViewService which will
// provide the views for this collection.
Intent intent = new Intent(context, StackWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
// When intents are compared, the extras are ignored, so we need to embed the extras
// into the data so that the extras will not be ignored.
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
https://android.googlesource.com/platform/development/+/master/samples/StackWidget/src/com/example/android/stackwidget/StackWidgetProvider.java

到這里,Android Widget 基本使用以及常見問題就已經(jīng)說完了昧诱。但使用中你可能會遇到各種各樣的問題慷丽,而要解決問題,就需要你對相應的流程熟悉鳄哭。因此才會有這一些列的文章要糊。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妆丘,隨后出現(xiàn)的幾起案子锄俄,更是在濱河造成了極大的恐慌,老刑警劉巖勺拣,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奶赠,死亡現(xiàn)場離奇詭異,居然都是意外死亡药有,警方通過查閱死者的電腦和手機毅戈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愤惰,“玉大人苇经,你說我怎么就攤上這事』卵裕” “怎么了扇单?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奠旺。 經(jīng)常有香客問我蜘澜,道長,這世上最難降的妖魔是什么响疚? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任鄙信,我火速辦了婚禮,結果婚禮上忿晕,老公的妹妹穿的比我還像新娘装诡。我一直安慰自己,他們只是感情好杏糙,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布慎王。 她就那樣靜靜地躺著,像睡著了一般宏侍。 火紅的嫁衣襯著肌膚如雪赖淤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天谅河,我揣著相機與錄音咱旱,去河邊找鬼确丢。 笑死,一個胖子當著我的面吹牛吐限,可吹牛的內(nèi)容都是我干的鲜侥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼诸典,長吁一口氣:“原來是場噩夢啊……” “哼描函!你這毒婦竟也來了?” 一聲冷哼從身側響起狐粱,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤舀寓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肌蜻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互墓,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年蒋搜,在試婚紗的時候發(fā)現(xiàn)自己被綠了篡撵。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡豆挽,死狀恐怖育谬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祷杈,我是刑警寧澤斑司,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站但汞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏互站。R本人自食惡果不足惜私蕾,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胡桃。 院中可真熱鬧踩叭,春花似錦、人聲如沸翠胰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽之景。三九已至斤富,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锻狗,已是汗流浹背满力。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工焕参, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人油额。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓叠纷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親潦嘶。 傳聞我的和親對象是個殘疾皇子涩嚣,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容