android - 如何每分钟更新一个 Android 应用小部件

标签 android kotlin widget android-widget alarmmanager

备注 :我知道之前有人问过这个问题,但是发布的少数解决方案要么已过时和/或对我不起作用

嗨,我正在尝试制作一个时钟应用小部件(倒计时时钟),它将显示 Android 的不同倒计时持续时间的列表,但是我在尝试让它每分钟更新一次时遇到了麻烦。我试过使用 AlarmManager但是由于某种原因它不起作用。

这是我所拥有的一切:

EventWidget.kt

/**
 * Implementation of App Widget functionality.
 */
class EventWidget : AppWidgetProvider() {

    class UpdateTimeService : Service() {

        private val intentFilter = IntentFilter().apply {
            addAction(Intent.ACTION_TIME_TICK)
            addAction(Intent.ACTION_TIME_CHANGED)
            addAction(Intent.ACTION_TIMEZONE_CHANGED)
        }

        private val receiver = object: BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent?) = update(context)
        }


        override fun onCreate() {
            super.onCreate()
            registerReceiver(receiver, intentFilter)
        }

        override fun onBind(intent: Intent?): IBinder? = null

        override fun onDestroy() {
            super.onDestroy()
            unregisterReceiver(receiver)
        }

        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            super.onStartCommand(intent, flags, startId)

            if (intent != null && UPDATE_TIME == intent.action) update(this)

            return START_STICKY
        }

        companion object {
            const val UPDATE_TIME = "com.richardrobinson.countdown2.action.UPDATE_TIME"
        }

    }

    private lateinit var pending: PendingIntent

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        appWidgetIds.forEach {
            val intent = Intent(context, EventWidgetService::class.java).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, it)
                data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
            }

            val rv = RemoteViews(context.packageName, R.layout.event_widget).apply {
                setRemoteAdapter(R.id.listView, intent)
                setEmptyView(R.id.listView, R.id.emptyView)
            }

            appWidgetManager.updateAppWidget(it, rv)
        }

        update(context)

        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listView)

        super.onUpdate(context, appWidgetManager, appWidgetIds)
    }

    override fun onEnabled(context: Context) {
        // Enter relevant functionality for when the first widget is created
        super.onEnabled(context)
        val pending = PendingIntent.getService(context, 0, Intent(context, UpdateTimeService::class.java), 0)

        val interval: Long = 1000 * 60

        (context.getSystemService(Context.ALARM_SERVICE) as AlarmManager).apply {
            cancel(pending)
            setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), interval, pending)
        }

        update(context)
    }

    override fun onReceive(context: Context, intent: Intent?) {
        super.onReceive(context, intent)
        update(context)
    }

    override fun onDisabled(context: Context) {
        // Enter relevant functionality for when the last widget is disabled
    }
}

fun update(context: Context) {
    val rv = RemoteViews(context.packageName, R.layout.event_widget).apply {
        setRemoteAdapter(R.id.listView, Intent(context, EventWidgetService::class.java))
        setEmptyView(R.id.listView, R.id.emptyView)
    }

    val component = ComponentName(context, EventWidget::class.java)

    AppWidgetManager.getInstance(context).apply {
        updateAppWidget(component, rv)

        val ids = getAppWidgetIds(component)
        ids.forEach { updateAppWidget(it, rv) }

        notifyAppWidgetViewDataChanged(ids, R.id.listView)
    }

}

EventWidgetService.kt
class EventWidgetService : RemoteViewsService() {
    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory =
            EventRemoteViewsFactory(this.applicationContext, intent)
}

class EventRemoteViewsFactory(private val context: Context, intent: Intent) : RemoteViewsService.RemoteViewsFactory {
    private val prefsName = "FlutterSharedPreferences"

    override fun onCreate() {
        MiniModel.initialize(context.getSharedPreferences(prefsName, Context.MODE_PRIVATE))
    }

    override fun getLoadingView(): RemoteViews = RemoteViews(context.packageName, R.id.emptyView)

    override fun getItemId(position: Int): Long = position.toLong()

    override fun onDataSetChanged() {
    }

    override fun hasStableIds(): Boolean = true

    @ExperimentalTime
    override fun getViewAt(position: Int): RemoteViews {
        return RemoteViews(context.packageName, R.layout.widget_item).apply {
            val event = MiniModel.events[position]
            val remaining = event.secondsRemaining.inSeconds / (event.end.epochSecond - event.start.epochSecond).toDouble()

            setProgressBar(R.id.progressBar, 100, (remaining * 100).toInt(), false)

            setTextViewText(R.id.item_text, event.title)

            setTextViewText(R.id.details_text, "whatever")
        }
    }


    override fun getCount(): Int = MiniModel.events.count()

    override fun getViewTypeCount(): Int = 1

    override fun onDestroy() {
    }

}

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.richardrobinson.countdown2">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:name="io.flutter.app.FlutterApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="Hourglass">
        <receiver android:name=".EventWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/event_widget_info" />
        </receiver>

        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:windowSoftInputMode="adjustResize">

            <!--
                 This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme).
            -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".EventWidgetService"
            android:permission="android.permission.BIND_REMOTEVIEWS"/>
        <service android:name=".EventWidget$UpdateTimeService">
            <intent-filter>
                <action android:name="com.richardrobinson.countdown2.action.UPDATE_TIME"/>
            </intent-filter>
        </service>
    </application>

</manifest>

最佳答案

//试试计时器:

private Timer myTimer = new Timer(); 
myTimer.schedule(new TimerTask() {
@Override
public void run() {
    // use runOnUiThread(Runnable action)
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
           // do your task
        }
    });
}
}, timeInterval);

关于android - 如何每分钟更新一个 Android 应用小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59571653/

相关文章:

android - 绘制固定长度曲线

kotlin - 在 Ktor 中,如何将 InputStream 流式传输到 HttpClient 请求的正文中?

qt - 如何在 QLabel 中调整图像大小

ios - URLImage 永远不会在 SwiftUI 的 Widget 应用程序中加载图像

java - 在用作小部件文本之前增加整数

Android玩市场推荐了解

android - Ksoap2 android - 传递简单的 SOAP 头

android - 是否有可能用函数文字序列化 Kotlin Data 类?

java - 如何在 CameraX 预览上设置一个框,以便使用 Java 中的 ImageAnalysis 对其进行处理?

android - 压缩纹理在 Android 中变为白色