【问题标题】:onConfigurationChanged not called at rotation with configChanges="keyboardHidden|orientation" set?设置了 configChanges="keyboardHidden|orientation" 时未调用 onConfigurationChanged?
【发布时间】:2011-10-15 11:25:54
【问题描述】:

我不是第一个在轮换时遇到 onConfigurationChanged 问题的人。但是由于常见的解决方案是设置 configChanges="keyboardHidden|orientation" 在清单中,这对我没有帮助,我发布了这个问题,不管成千上万的“相似”问题。

这是一个简单的小部件,可在单击时打开和关闭铃声音量。它工作正常,直到屏幕旋转,然后点击什么都没有发生。

为了处理这个问题,我想捕捉 onConfigurationChanged,但它永远不会被调用! 我从来没有看到来自 onConfigurationChanged 的​​日志消息,但是我确实在日志中看到另一条消息“I/ActivityManager(63): Config changed:”,表明配置已更改,并且期望调用 onConfigurationChanged 是合理的.

请帮忙:)

清单:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="aa.android.x10.mini.mywidget" android:versionCode="1"  
    android:versionName="1.0">  
<application android:icon="@drawable/icon" 
             android:label="@string/app_name">  

    <receiver android:name="MyWidgetProvider" android:label="My Widget">  
        <intent-filter>  
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
            <action android:name="android.appwidget.action.ACTION_BATTERY_CHANGED" /> 
        </intent-filter>  
        <meta-data android:name="android.appwidget.provider"  
                   android:resource="@xml/widget_info" />  
    </receiver>  
    <service android:name=".UpdateWidgetService"
             android:configChanges="keyboardHidden|orientation"></service>  
    <activity android:name=".Test"></activity>  

    </application>  
    <uses-sdk android:minSdkVersion="7" />  

</manifest>   

小部件提供者: 包 aa.android.x10.mini.mywidget;

import android.app.PendingIntent;  
import android.appwidget.AppWidgetManager;  
import android.appwidget.AppWidgetProvider;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;
import android.widget.RemoteViews;  

public class MyWidgetProvider extends AppWidgetProvider 
{   
@Override  
public void onUpdate(Context context, AppWidgetManager appWidgetManager,  
        int[] appWidgetIds) 
{  
    Log.i("MyWidgetProvider onUpdate", "#### CALLED!");  

    RemoteViews remoteViews = new RemoteViews(context.getPackageName(),  
            R.layout.widget_layout);  
    Intent intent = new Intent(context.getApplicationContext(), UpdateWidgetService.class);  
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);  

    PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), 0, intent, 
                                                            PendingIntent.FLAG_UPDATE_CURRENT);

    remoteViews.setOnClickPendingIntent(R.id.TextView01, pendingIntent);  

    appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);  

    context.startService(intent);  
    }  
}  

服务: 包 aa.android.x10.mini.mywidget;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;

public class UpdateWidgetService extends Service 
{  
@Override  
public void onStart(Intent intent, int startId) 
{  
    Log.i("UpdateWidgetService", "#### CALLED!");  

    String state = null;  

    AudioManager mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

    if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL )
    {
        mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
        mAudioManager.setStreamVolume(AudioManager.STREAM_RING,mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING),AudioManager.VIBRATE_SETTING_ON);
        state = "ON"; 
    }
    else
    {
        mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
        mAudioManager.setStreamVolume(AudioManager.STREAM_RING,0,AudioManager.VIBRATE_SETTING_ON);
        state = "OFF"; 
    }

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this.getApplicationContext());  

    int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);  
    if (appWidgetIds.length > 0) 
    {  
        for (int widgetId : appWidgetIds) 
        {  
            RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.widget_layout);  
            remoteViews.setTextViewText(R.id.TextView01, "\n" +"         "+ state);  
                appWidgetManager.updateAppWidget(widgetId, remoteViews);  
            }  
            stopSelf();  
        }  
        super.onStart(intent, startId);  
    }  

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

    @Override 
    public void onConfigurationChanged(Configuration newConfig) 
    { 
        Log.i("onConfigurationChanged", "##### CALLED! #####");  
        super.onConfigurationChanged(newConfig);
    }
}  

旋转屏幕时的日志:

I/WindowManager(   63): Setting rotation to 1, animFlags=0 
I/ActivityManager(   63): Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/1 nav=3/1 orien=2 layout=17} 
I/UsageStats(   63): Unexpected resume of com.android.launcher while already resumed in com.android.launcher 

【问题讨论】:

    标签: android widget rotation


    【解决方案1】:

    乔恩,

    onConfigurationChanged() 并不是真正用于Services。由于Services 没有用户界面,因此无法保证onConfigurationChanged() 不会可靠地触发。最好把它放在Activity 中,因为Activities 保证有一个用户界面。如果您担心您的 Widget 会根据方向更改重新对齐,那最终是包含 Activity 处理的工作。这意味着如果 Activity 不旋转,您的 Widget 将不会自行重新定向。

    但是,如果您仍然需要自己管理配置更改,那么您需要手动检查 DefaultDisplay,如果方向更改,它会自动更改。这个简单的检查工作如下:

    Display display = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    if (display.getWidth() > display.getHeight())
        // In Landscape...
    else if (display.getWidth() < display.getHeight())
        // In Portrait...
    

    现在,您描述的问题,虽然它会在方向改变时表现出来,但它并不是具体的方向改变问题。如果您查看onUpdate() 的代码,您会发现您一次只处理一个小部件。这样做的问题是,方向更改的默认行为会破坏所有 UI 并重新创建它们。由于对 Widget 的多个副本的所有更新都是同时发生的,并且您无法确定保存您的 Widget 的 Activity 将如何处理方向更改,因此您应该考虑所有可能的 appWidgetId。以下可能会有所帮助:

    for (int i : appWidgetIds) {
        int widgetId = appWidgetIds[i];
    // Set the RemoteViews for the Widget
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
    // Set the Pending Intent
        Intent intent = new Intent(context.getApplicationContext(),
                                   UpdateWidgetService.class);  
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);  
    
        PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), 
                         0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    
        remoteViews.setOnClickPendingIntent(R.id.TextView01, pendingIntent);  
    // Update the individual widget
        appWidgetManager.updateAppWidget(widgetId, remoteViews);
    }
    

    希望这会有所帮助。

    模糊逻辑

    【讨论】:

    • 非常感谢您的回答!作为 Java 和 Android 菜鸟,我对此充满信心,因为我设法在我的服务中覆盖 onConfiurationChanged,它将被调用。我必须说我觉得在超类中有一个要覆盖的函数有点令人困惑,它是......不应该被使用......?但是,我真的在这里碰壁了,所以我很高兴你可以为我指出一个新的方向,我会尝试一种 Activity 方法!谢谢!
    • 并不是说它不“打算”被使用......它不打算被开发人员使用,因为它不是“保证”被使用的。那有意义吗?请参阅 ConfigurationChanged 很大程度上取决于实际更改的配置。甚至 Activity 生命周期也有几个部分被调用,但不保证会被调用并且不知道何时被调用,但会在此之前和之后,如果有的话......明白我吗?
    猜你喜欢
    • 1970-01-01
    • 2011-12-10
    • 2011-05-31
    • 1970-01-01
    • 2012-07-23
    • 1970-01-01
    • 2011-09-21
    • 2013-05-18
    • 1970-01-01
    相关资源
    最近更新 更多