【问题标题】:Widget not working well in Android 5.1 but perfect in 4.1, 4.4 etc小部件在 Android 5.1 中运行不佳,但在 4.1、4.4 等中完美
【发布时间】:2015-09-22 22:38:20
【问题描述】:

几年前我开发了一个小部件。到目前为止,该小部件可以完美运行。它在 Android 5.1.1 设备中无法正常运行,但在 Android 4.4 及更低版本中运行良好。小部件显示操作系统给出的三个数值。这些值是正确获得的,因为我可以在测试应用程序的这个小部件之外显示它们,并且它们在该测试非小部件应用程序中得到更新。

在 android 5.1.1 中,小部件菜单中没有显示图标,它显示的是默认的 android 图标。另外,小部件没有更新!!!我不知道出了什么问题。我需要一个必须在 4.1、4.4 和 5.1 设备上运行的小部件。

必须实现的 Android 5.1.1 小部件发生了哪些变化?我在文档中找不到任何不同的地方

这是我的小部件:

清单:

<receiver android:name="com.myapp.CPUWidgetProvider" android:label="@string/app_name" android:icon="@drawable/icon128">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <intent-filter>
                <action android:name="AUTO_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_provider" />
        </receiver>

        <service android:name="com.myapp.UpdateService"/>

我的 widget_provider.xml 文件:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   android:minWidth="40dp"
   android:minHeight="40dp"
   android:initialLayout="@layout/widget_layout"
   android:updatePeriodMillis="0"
/>

我的 widget_layout.xml 文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fondo"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/widget_background"    
    android:paddingLeft="1dp"
    android:paddingRight="1dp"
    android:paddingTop="10dp"
    android:paddingBottom="10dp">

    <TextView
    android:id="@+id/freq"  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="@string/loading"          
    android:textSize="12sp"
    android:textColor="#EE08B608"
    android:layout_weight="1"/>     

    <TextView
    android:id="@+id/usage"  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="..."
    android:textSize="12sp"
    android:textColor="#EE08B608"
    android:layout_weight="1"/>             

    <TextView
    android:id="@+id/temp"  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="27ºC 80ºF"
    android:textSize="11sp"
    android:textColor="#EE08B608"
    android:layout_weight="1"/>         
</LinearLayout>

我的小部件警报:

public class WidgetAlarm{
    private final int ALARM_ID = 0;
    private final int INTERVAL_MILLIS = 5000;
    private Context mContext;

    public WidgetAlarm(Context context){
        mContext = context;
    }

    public void startAlarm(){      
        Intent alarmIntent = new Intent(CPUWidgetProvider.ACTION_AUTO_UPDATE);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);        
        alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), INTERVAL_MILLIS, pendingIntent); // RTC does not wake the device up
    }

    public void stopAlarm(){
        Intent alarmIntent = new Intent(CPUWidgetProvider.ACTION_AUTO_UPDATE);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(pendingIntent);
    }
}

我的更新服务:

public class UpdateService extends Service implements SensorEventListener { 
    private SensorManager mSensorManager;
    private Sensor mTempSensor;
    double auxBatteryTemp;
    String systemTemp=null;
    String systemTempFarenheit=null;
    String batteryTemp="25";
    String batteryTempFarenheit="80";
    int processorMaxFreq=-1;
    DecimalFormatSymbols symbols;
    DecimalFormat tempDecimalFormat;
    boolean serviceDestroyed=false;

    Context ctx;
    PowerManager pm;
    RemoteViews remoteViews;
    ComponentName thisWidget;

    private BroadcastReceiver mBatInfoReceiver;     //Broadcast receiver for the battery temperature
    private BroadcastReceiver screenoffReceiver;    //Broadcast receiver for stop the alarm when the screen is off

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {      
        //System.out.println("XXXXX SERVICE START COMMAND");

        /* receivers */     
        mBatInfoReceiver = new BroadcastReceiver(){
            @Override
            public void onReceive(Context arg0, Intent intent) {
                try {
                    auxBatteryTemp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
                    batteryTemp = ""+(int) (auxBatteryTemp / 10);
                    auxBatteryTemp=auxBatteryTemp/10;
                    batteryTempFarenheit = ""+(int)(((auxBatteryTemp*((float)9/5))+32));
                    updateTemperatures();
                } catch (Exception e) {e.printStackTrace();}
            }
        };  

        screenoffReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                    WidgetAlarm widgetAlarm = new WidgetAlarm(context.getApplicationContext());
                    widgetAlarm.stopAlarm();
                }
                else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
                    WidgetAlarm widgetAlarm = new WidgetAlarm(context.getApplicationContext());
                    widgetAlarm.startAlarm();
                }
                return;
            }
        };

        symbols = new DecimalFormatSymbols(Locale.GERMAN);
        symbols.setDecimalSeparator('.');
        tempDecimalFormat = new DecimalFormat("#.#", symbols);

        this.ctx = this;
        pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE); 
        remoteViews = new RemoteViews(ctx.getPackageName(), R.layout.widget_layout);
        thisWidget = new ComponentName( ctx, CPUWidgetProvider.class );

        //Temperature sensors
        mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
        if (Build.VERSION.SDK_INT >= 14)
            mTempSensor = mSensorManager.getDefaultSensor(13); //13 = Sensor.TYPE_AMBIENT_TEMPERATURE //this constant required Android 4.0 
        else
            mTempSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE);

        mSensorManager.registerListener(this, mTempSensor, SensorManager.SENSOR_DELAY_FASTEST);

        //adding receivers
        registerReceiver(this.mBatInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(screenoffReceiver, filter);

        //return super.onStartCommand(intent, flags, startId);
        return Service.START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            mSensorManager.unregisterListener(this);
            unregisterReceiver(this.mBatInfoReceiver);
            unregisterReceiver(this.screenoffReceiver);
            serviceDestroyed=true;
        } catch (Exception e) {e.printStackTrace();}
        //System.out.println("XXXXX SERVICE DESTROY");
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }   

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {}

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (Build.VERSION.SDK_INT >= 14){
            if (event.sensor.getType() == 13) { //13 = Sensor.TYPE_AMBIENT_TEMPERATURE //this constant required Android 4.0 
                getTemperature(event);
            }
        }else{
            if (event.sensor.getType() == Sensor.TYPE_TEMPERATURE) {
                getTemperature(event);
            }
        }       
    }

    public void getTemperature(SensorEvent event){
        float values[] = event.values;
        if (values.length>0){
            systemTemp = ""+(int)values[0]; 
            systemTempFarenheit = ""+(int)((values[0]*((float)9/5))+32);
            updateTemperatures();
        }
    }

    public void updateTemperatures(){
        if (pm.isScreenOn() && serviceDestroyed==false){                                                                            
            //si hemos podido obtener la temperatura del sistema, la mostramos, si no, mostramos la temperatura de la bateria.
            if (systemTemp!=null){
                remoteViews.setTextViewText(R.id.temp,systemTemp+"ºC"+" "+systemTempFarenheit+"ºF");
            }
            else{
                remoteViews.setTextViewText(R.id.temp,batteryTemp+"ºC"+" "+batteryTempFarenheit+"ºF");
            }

            AppWidgetManager.getInstance( ctx).updateAppWidget( thisWidget, remoteViews );
            //System.out.println("XXXXX SERVICE UPDATE TEMPERATURES");
        }       
    }
}

我的应用小部件提供商:

public class CPUWidgetProvider extends AppWidgetProvider{           
    public static final String ACTION_WIDGET_CLICK = "ActionWidgetClick"; 
    public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";

    DecimalFormatSymbols symbols;
    DecimalFormat tempDecimalFormat;
    int processorMaxFreq=-1;
    String cpuCurrentFreq=null;
    int freq = 0;
    float usage =-1;
    BufferedReader freqBufferedReader;
    PowerManager pm;
    RemoteViews remoteViews;
    ComponentName thisWidget;   
    WidgetAlarm widgetAlarm;
    ActivityManager manager;

    Intent active;
    PendingIntent actionPendingIntent;

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        context.stopService(new Intent(context, UpdateService.class));

        //stop alarm
        widgetAlarm = new WidgetAlarm(context.getApplicationContext());
        widgetAlarm.stopAlarm();

        //System.out.println("XXXXX onDISABLED");
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);

        //System.out.println("XXXXX onDELETED");
    }   

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);   

        //System.out.println("XXXXX onENABLED");
    }   

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        if (isMyServiceRunning(context)==false){
            context.startService(new Intent(context, UpdateService.class));
        }

        remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        thisWidget = new ComponentName(context, CPUWidgetProvider.class );

        //preparamos el evento para cuando el usuario presiona en el widget, ese evento será recibido en onReceive()
        active = new Intent(context, CPUWidgetProvider.class);
        active.setAction(ACTION_WIDGET_CLICK);
        actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
        remoteViews.setOnClickPendingIntent(R.id.fondo, actionPendingIntent);

        AppWidgetManager.getInstance(context).updateAppWidget( thisWidget, remoteViews );       

        // start alarm
        widgetAlarm = new WidgetAlarm(context.getApplicationContext());
        widgetAlarm.startAlarm();

        //System.out.println("XXXXX onUPDATE");
    }       

    @Override
    public void onReceive(Context context, Intent intent){
        if (intent.getAction().equals(ACTION_WIDGET_CLICK)){
            switchWidget(context);
        }else if(intent.getAction().equals(ACTION_AUTO_UPDATE)){

            symbols = new DecimalFormatSymbols(Locale.GERMAN);
            symbols.setDecimalSeparator('.');
            tempDecimalFormat = new DecimalFormat("#.#", symbols);      

            pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 
            remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            thisWidget = new ComponentName(context, CPUWidgetProvider.class );

            if (pm.isScreenOn()){
                try{                
                    freqBufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"))));
                    freq = Integer.valueOf(freqBufferedReader.readLine()).intValue();   
                    freqBufferedReader.close();
                    freqBufferedReader = null;
                    cpuCurrentFreq=freq / 1000 + " Mhz";                    
                }
                catch (Exception Exception){
                    cpuCurrentFreq=null;
                }                                           

                remoteViews.setTextViewText(R.id.freq,cpuCurrentFreq);

                if (processorMaxFreq==-1)
                    getProcessorMaxFreq();

                usage=(float) (freq/processorMaxFreq);                  
                remoteViews.setTextViewText(R.id.usage,tempDecimalFormat.format(usage / 10.0D)+" %");

                //preparamos el evento para cuando el usuario presiona en el widget, ese evento será recibido en onReceive()
                active = new Intent(context, CPUWidgetProvider.class);
                active.setAction(ACTION_WIDGET_CLICK);
                actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
                remoteViews.setOnClickPendingIntent(R.id.fondo, actionPendingIntent);

                AppWidgetManager.getInstance(context).updateAppWidget( thisWidget, remoteViews );
                //System.out.println("XXXXX UPDATING WIDGET");

            }

        }else{
            super.onReceive(context, intent);
        }

        //System.out.println("XXXXX onRECEIVE: "+intent.getAction());
    }

    public void switchWidget(Context context){
        symbols = new DecimalFormatSymbols(Locale.GERMAN);
        symbols.setDecimalSeparator('.');
        tempDecimalFormat = new DecimalFormat("#.#", symbols);      

        remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        thisWidget = new ComponentName(context.getPackageName(), CPUWidgetProvider.class.getName());
        widgetAlarm = new WidgetAlarm(context.getApplicationContext());
        if (isMyServiceRunning(context)==true){ //si el widget está encendido la apagamos
            context.stopService(new Intent(context, UpdateService.class));          
            widgetAlarm.stopAlarm();
            remoteViews.setTextViewText(R.id.freq,"");
            remoteViews.setTextViewText(R.id.usage,"OFF");
            remoteViews.setTextViewText(R.id.temp,"");
        }
        else{ //si el widget está apagado lo encendemos
            context.startService(new Intent(context, UpdateService.class)); 
            widgetAlarm.startAlarm();
            remoteViews.setTextViewText(R.id.freq,"-----");
            remoteViews.setTextViewText(R.id.usage,"--");
            remoteViews.setTextViewText(R.id.temp,"");
        }
        AppWidgetManager.getInstance(context).updateAppWidget(thisWidget,remoteViews);
    }

    private boolean isMyServiceRunning(Context context) {
        manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (UpdateService.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    public void getProcessorMaxFreq(){
        int freq = 0;
        try{            
            freqBufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"))));
            freq = Integer.valueOf(freqBufferedReader.readLine()).intValue();
            freqBufferedReader.close(); 
            freqBufferedReader=null;
            processorMaxFreq=(freq / 1000);                 
        }
        catch (Exception e){e.printStackTrace();}
    }
}

【问题讨论】:

  • 小部件菜单由单独的启动器控制:与 Android 版本无关。您是否有理由不提供previewImage?当您说“不更新”时,您的意思是您的 UpdateService 根本没有运行吗?或者它运行但小部件没有更新?
  • 我尝试了使用 previewImage 而没有使用 previewImage。它们都不起作用。我的意思是没有收到更新,或者至少非常低,每 X 分钟有一个。在 android 4.4 及更低版本中,它可以完美运行,每 5 或 10 秒更新一次。

标签: android widget android-widget android-appwidget appwidgetprovider


【解决方案1】:

根据this blog postassociated bug closed as Working as Intended

低轮询周期四舍五入到 60000 毫秒(一分钟)

你会看到一条 logcat 消息:

可疑的短间隔 5000 毫秒;扩展到 60 秒

当被问及替代方案时,post #9 on the bug 表示:

如果您尝试比每 5 秒更频繁地跑步,那么警报是错误的做法。唤醒通常对电池寿命极为不利的设备。如果您有需要不断更新的实时 UI,请使用唤醒锁,然后在处理程序上安排您的活动。这实际上比每秒设置一次警报更电池效率。

请记住,小部件只能偶尔运行;操作系统将提供的最小 updatePeriodMillis 是 30 分钟。

【讨论】:

    猜你喜欢
    • 2013-01-06
    • 1970-01-01
    • 2019-09-27
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    相关资源
    最近更新 更多