Widget调用四大组件

围巾🧣 2021年07月17日 467次浏览

Widget基本使用

  1. 编写布局文件

    src/main/res/layout/activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/gray"
        android:padding="@dimen/widget_margin"
        tools:context=".component.MainProcessActivity">
    
    
        <TextView
            android:id="@+id/tvService"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:text="Service"
            android:textSize="10sp"
            android:textStyle="bold" />
    
        <TextView
            android:id="@+id/tvServiceFromGetBroadcast"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tvService"
            android:text="getBroadcast()收到广播后创建"
            android:textSize="10sp" />
    
    
        <LinearLayout
            android:id="@+id/llSerFromBroadcast"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tvServiceFromGetBroadcast"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/btnSerWidgetFromBroad"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Widget进程"
                android:textSize="10sp" />
    
    
            <Button
                android:id="@+id/btnSerMainFromBroad"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="2dp"
                android:text="主进程"
                android:textSize="10sp" />
        </LinearLayout>
    
    </RelativeLayout>
    
  2. 编写AppWidgetProviderInfo

    src/main/res/xml/widget_test_info.xml

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:initialLayout="@layout/activity_main"
        android:minWidth="150dp"
        android:minHeight="300dp"
        android:previewImage="@drawable/preview"
        android:resizeMode="horizontal|vertical"
        android:updatePeriodMillis="2000"
        android:widgetCategory="home_screen"
        >
    
    </appwidget-provider>
    
  3. 实现AppWidgetProvider

    src/main/java/com/miui/widgettest/TestAppWidgetProvider.kt

    class TestAppWidgetProvider : AppWidgetProvider() {
        override fun onUpdate(
            context: Context?,
            appWidgetManager: AppWidgetManager?,
            appWidgetIds: IntArray?
        ) {
        }
    
        override fun onReceive(context: Context?, intent: Intent?) {
            super.onReceive(context, intent)
            Log.i("TAG", "onReceive: ${intent.toString()}")
    
        }
    }
    
  4. 在manifests清单中声明

    src/main/AndroidManifest.xml

            <receiver android:name="TestAppWidgetProvider">
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                </intent-filter>
                <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@xml/widget_test_info" />
            </receiver>
    

四大组件基本使用

Activity

相关知识查看Activity启动模式的文章

  1. 通信

    第一个Activity:startActivityForResult()启动、onResult() 接收结果

    第二个Activity: setResult()

Service

  1. 创建使用

    • 实现IntentService

      src/main/java/com/miui/widgettest/component/MainProcessService.kt

      class MainProcessService : IntentService(MainProcessService::class.simpleName) {
      
          private val TAG = javaClass.simpleName
          override fun onHandleIntent(intent: Intent?) {
              Toast.makeText(this, "MainProcessService", Toast.LENGTH_SHORT).show()
              Log.i(TAG, "onHandleIntent: MainProcessService")
          }
      }
      
    • 在清单文件中声明

              <service android:name=".component.MainProcessService" />
      
    • 使用

      startService(Intent(context, MainProcessService::class.java))
      
  2. 生命周期

    Service生命周期

Broadcast

  1. 创建使用

    • 定义广播Action常量

      src/main/java/com/miui/widgettest/CONST.kt

      package com.miui.widgettest
      
      interface CONST {
          interface ACTION {
              companion object {
                  // 拉起 Activity
                  const val MAIN_PROCESS_ACTIVITY_ACTION =
                      "widgettest.broadcast.MAIN_PROCESS_ACTIVITY_ACTION"
                  const val WIDGET_PROCESS_ACTIVITY_ACTION =
                      "widgettest.broadcast.WIDGET_PROCESS_ACTIVITY_ACTION"
      
                  // 使用 Provider
                  const val MAIN_PROCESS_PROVIDER_ACTION =
                      "widgettest.broadcast.MAIN_PROCESS_PROVIDER_ACTION"
                  const val WIDGET_PROCESS_PROVIDER_ACTION =
                      "widgettest.broadcast.WIDGET_PROCESS_PROVIDER_ACTION"
      
      
                  // 启动 Service 广播
                  const val MAIN_PROCESS_SERVICE_ACTION =
                      "widgettest.broadcast.MAIN_PROCESS_SERVICE_ACTION"
                  const val WIDGET_PROCESS_SERVICE_ACTION =
                      "widgettest.broadcast.WIDGET_PROCESS_SERVICE_ACTION"
      
                  // 拉起 BroadcastReceiver
                  const val MAIN_PROCESS_BROADCAST_RECEIVER_ACTION =
                      "widgettest.broadcast.MAIN_PROCESS_BROADCAST_RECEIVER_ACTION"
                  const val WIDGET_PROCESS_BROADCAST_RECEIVER_ACTION =
                      "widgettest.broadcast.WIDGET_PROCESS_BROADCAST_RECEIVER_ACTION"
      
      
                  // getBroadcast触发后再发起的广播
                  const val WIDGET_PROCESS_ACTION = "widgettest.broadcast.WIDGET_PROCESS_ACTION"
                  const val MAIN_PROCESS_ACTION = "widgettest.broadcast.MAIN_PROCESS_ACTION"
      
      
              }
          }
      }
      
    • 实现 BroadcastReceiver

      class MainProcessBroadcastReceiver : BroadcastReceiver() {
          private val TAG = MainProcessBroadcastReceiver::class.simpleName
          override fun onReceive(context: Context?, intent: Intent?) {
              Log.i(TAG, "onReceive: ")
              Toast.makeText(context, "MainProcessBroadcastReceiver", Toast.LENGTH_SHORT).show()
          }
      }
      
    • 注册广播

      1. 静态注册

                <receiver android:name=".component.MainProcessBroadcastReceiver">
                    <intent-filter>
                        <action android:name="widgettest.broadcast.MAIN_PROCESS_ACTION" />
                    </intent-filter>
                </receiver>
        
      2. 上下文注册

                val br = MainProcessBroadcastReceiver()
                val intentFilter = IntentFilter(CONST.ACTION.MAIN_PROCESS_BROADCAST_RECEIVER_ACTION)
                registerReceiver(br, intentFilter)
        
    • 发起广播

      Android O之后广播Intent得指定ComponentName

      val intentBroadcast = Intent(CONST.ACTION.MAIN_PROCESS_ACTION).apply {
          component = ComponentName(
              context!!.packageName,
              MainProcessBroadcastReceiver::class.java.name
          )
      }
      context?.sendBroadcast(intentBroadcast)
      

ContentProvider

  1. 创建使用

    • 实现 ContentProvider

      private val TAG = MainProcessProvider::class.java.simpleName
      
      class MainProcessProvider : ContentProvider() {
      
      
          override fun onCreate(): Boolean {
              return true
          }
      
          override fun query(
              uri: Uri,
              projection: Array<out String>?,
              selection: String?,
              selectionArgs: Array<out String>?,
              sortOrder: String?
          ): Cursor? {
              return null
          }
      
          override fun getType(uri: Uri): String? {
              return "image/jpeg"
          }
      
          override fun insert(uri: Uri, values: ContentValues?): Uri? {
              Log.i(TAG, "insert: $TAG")
              Toast.makeText(WidgetApplication.context, "MainProcessProvider", Toast.LENGTH_SHORT).show()
              return uri
          }
      
          override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
              return 1
          }
      
          override fun update(
              uri: Uri,
              values: ContentValues?,
              selection: String?,
              selectionArgs: Array<out String>?
          ): Int {
              return 1
          }
      }
      
    • 在 清单中注册

              <provider
                  android:name=".component.MainProcessProvider"
                  android:authorities="com.miui.widgettest"
                  android:enabled="true"
                  android:exported="true"
                  android:permission="true" />
      
    • 使用

                      // 通过ContentResolver 根据URI 向ContentProvider中插入数据
                      try {
                          context?.contentResolver?.insert(
                              Uri.parse("content://com.miui.widgettest/emmm"),
                              ContentValues()
                          )
                      } catch (e: Exception) {
                          Log.e("TestAppWidgetProvider", " ${e.message}", e)
                      }
      

Widget调用四大组件

都是在AppWidgetProvider里onUpdate设置好

直接调用

在添加Widget添加时直接设置好点击操作

  1. Activity

           appWidgetIds?.forEach { appWidgetId ->
                // 不同case的pendingIntent
                // 直接启动activity
                val mpActPendingIntent =
                    Intent(context, MainProcessActivity::class.java).let { intent ->
                        PendingIntent.getActivity(context, 0, intent, 0)
                    }
    
                // 设置监听事件
                val views: RemoteViews = RemoteViews(
                    context?.packageName,
                    R.layout.activity_main
                ).apply {
                    // activity 监听器
                    setOnClickPendingIntent(R.id.btnActMainFromActivity, mpActPendingIntent)
                }
    
                appWidgetManager?.updateAppWidget(appWidgetId, views)
    
  2. Service

    跟Activity类似

                val mpSerPendingIntent = Intent(context, MainProcessService::class.java).let {
                    PendingIntent.getService(context, 0, it, 0)
                }
    

收到广播后调用

与直接调用不用, 这里都是getBroadcast方式收到广播后再调起

  1. Activity

    • 设置action常量

                  // 拉起 Activity
                  const val MAIN_PROCESS_ACTIVITY_ACTION =
                      "widgettest.broadcast.MAIN_PROCESS_ACTIVITY_ACTION"
                  const val WIDGET_PROCESS_ACTIVITY_ACTION =
                      "widgettest.broadcast.WIDGET_PROCESS_ACTIVITY_ACTION"
      
    • 清单中 appWidgetProvider 添加action

              <receiver android:name="TestAppWidgetProvider">
                  <intent-filter>
                      <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
      
      
                      <action android:name="widgettest.broadcast.MAIN_PROCESS_ACTIVITY_ACTION" />
                      <action android:name="widgettest.broadcast.WIDGET_PROCESS_ACTIVITY_ACTION" />
      
                  </intent-filter>
                  <meta-data
                      android:name="android.appwidget.provider"
                      android:resource="@xml/widget_test_info" />
              </receiver>
      
    • AppWidgetProvider 设置广播触发与回调

      src/main/java/com/miui/widgettest/TestAppWidgetProvider.kt

          private fun getPendingIntent(context: Context?, action: String): PendingIntent {
              return Intent(action).apply {
                  component = ComponentName(
                      context!!.packageName,
                      this@TestAppWidgetProvider::class.java.name
                  )
              }.let {
                  PendingIntent.getBroadcast(context, 0, it, PendingIntent.FLAG_UPDATE_CURRENT)
              }
          }
      
                  // onUpdate 方法里面设置 activity pendingIntent
                  val mpActPiFromGetBroadcast =
                      getPendingIntent(context, CONST.ACTION.MAIN_PROCESS_ACTIVITY_ACTION)
      
                      setOnClickPendingIntent(R.id.btnActMainFromBroad, mpActPiFromGetBroadcast)
      
      		// onReceive方法设置回调
              when (intent?.action) {
                  CONST.ACTION.MAIN_PROCESS_ACTIVITY_ACTION -> {
                      context?.startActivity(
                          Intent(
                              context, MainProcessActivity::class.java
                          ).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
                  }
              }
      
  2. Service

                CONST.ACTION.MAIN_PROCESS_SERVICE_ACTION -> {
                    context?.startService(Intent(context, MainProcessService::class.java))
                }
    
  3. Provider

                CONST.ACTION.MAIN_PROCESS_PROVIDER_ACTION -> {
                    // 通过ContentResolver 根据URI 向ContentProvider中插入数据
                    try {
                        context?.contentResolver?.insert(
                            Uri.parse("content://com.miui.widgettest/emmm"),
                            ContentValues()
                        )
                    } catch (e: Exception) {
                        Log.e("TestAppWidgetProvider", " ${e.message}", e)
                    }
                }
    
  4. Broadcast Receiver

                CONST.ACTION.MAIN_PROCESS_BROADCAST_RECEIVER_ACTION -> {
                    val intentBroadcast = Intent(CONST.ACTION.MAIN_PROCESS_ACTION).apply {
                        component = ComponentName(
                            context!!.packageName,
                            MainProcessBroadcastReceiver::class.java.name
                        )
                    }
                    context?.sendBroadcast(intentBroadcast)
                }