android 主屏幕小部件重新启动/内容在设备旋转时自行删除

android homescreen widget restarts/ content remove itself when device rotates

设备旋转时 android 小部件内容突然消失。

旋转设备时是否必须重新填充它们?但是小部件没有要监听的 onconfiguration 更改。

我还使用带有服务的 alarmmanager 来填充新应用程序的小部件(添加到 viewflipper)每 1 分钟显示一次。

小部件代码:

@Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {

        try{            
            // create intent service per APP id
            for(int i = 0; i < appWidgetIds.length; i++)
            {
                Log.e("onUpdate -- Widget_2_6", "onUpdate -- Widget_2_6: widget ID: " + appWidgetIds[i]);
                // check if alarms not created, create 1
                if(!GlobalStorage.tempStorage.containsKey(Widget_2_6.class.toString() + appWidgetIds[i]))
                {
                    createAlarmService(context, appWidgetIds[i]);

                    // put to hash for alarms created               
                    GlobalStorage.tempStorage.put(Widget_2_6.class.toString() + appWidgetIds[i], 0);

                    //CustomAndroidTools.popToast(Widget_2_6.class.toString() + appWidgetIds[i], context);
                }
            }
        }
        catch(Exception e)
        {
            Log.e("onUpdate -- Widget_2_6", "onUpdate -- Widget_2_6: error: " + e.toString());
        }

        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }   

private void createAlarmService(Context context, int id)
    {       
        if(alarm == null)
        {
            createAlarm(context);
        }
        // each new alarm will repeat in increasing half minute: so first is 1 min next is 1 min and 30 sec ...
        alarm.setRepeating(AlarmManager.RTC, 0, (1000 * (60 + (GlobalStorage.tempStorage.size() * 30))), createPendingIntent(context, id));
        Log.e("createAlarmService -- Widget_2_6", "createAlarmService -- Widget_2_6, creating alarm for widget id: " + id);
    }

服务代码:

if(CustomAndroidTools.checkDeviceIsInteractive(context))
            {
                new Thread(new Runnable() {
                    public void run() {
                        try{
                            ArrayList<AppInfo> appInfos = init(context, _class, widgetID);
                            buildUpdate(context, _class, widgetID, appInfos);
                            // update the limit offset here instead so that to ensure no exception came from building the UI of the widget
                            updateLimitOffset(_class, widgetID);
                        }
                        catch(Exception e)
                        {
                            Log.e("Thread -- onStartCommand -- WidgetUpdateService", "Thread -- onStartCommand -- WidgetUpdateService: error: " + e.toString());
                        }
                    }
                }).start();
            }

private ArrayList<AppInfo> init(Context context, Class<?> _class, int widgetID)
    {
        ArrayList<AppInfo> appInfos = new ArrayList<AppInfo>();
        try{
            appListManager alm = new appListManager(context);

            int skip = 0;
            //int limit = 10;
            if(GlobalStorage.tempStorage.containsKey(_class.toString() + widgetID))
            {
                skip = GlobalStorage.tempStorage.get(_class.toString() + widgetID);
            }
            if(skip == 0) // first initialize
            {
                ///TODO: for testing, get the first 5 used by banner
                appInfos.add(alm.getAppById(30));
                appInfos.add(alm.getAppById(16));
                appInfos.add(alm.getAppById(13));
                appInfos.add(alm.getAppById(12));
                appInfos.add(alm.getAppById(6));
            }
            //appInfos = alm.getAllAppsUnderTheCategory(-1, skip, limit);

        }
        catch(Exception e)
        {
            Log.e("init -- WidgetUpdateService", "init -- WidgetUpdateService: error: " + e.toString());
        }

        return appInfos;
    }

    private void updateLimitOffset(Class<?> _class, int widgetID){
        int skip = 0;
        int limit = 10;

        if(GlobalStorage.tempStorage.containsKey(_class.toString() + widgetID))
        {
            skip = GlobalStorage.tempStorage.get(_class.toString() + widgetID);
        }
        if(skip != 0)
        {
            limit = 5;
        }

        GlobalStorage.tempStorage.put(_class.toString() + widgetID, skip + limit); // update offset limit here
    }

    private void buildUpdate(Context context, Class<?> _class, int widgetID, ArrayList<AppInfo> appInfos) throws Exception
    {
        RemoteViews views = null;
        appListManager alm = new appListManager(context);

        try{            
            if(_class.equals(Widget_1_6.class) || _class.equals(Widget_1_6_2.class))
            {
                views = new RemoteViews(getPackageName(), R.layout.widget_1_6);
            }
            else if(_class.equals(Widget_2_6.class) || _class.equals(Widget_2_6_2.class))
            {
                views = new RemoteViews(getPackageName(), R.layout.widget_2_6);
            }

            if(alm.getTotalApps(-1) < 1)
            {
                views.setViewVisibility(R.id.app, View.GONE);
                views.setViewVisibility(R.id.sorryText, View.VISIBLE);
                views.setViewVisibility(R.id.loadingText, View.GONE);
            }
            else
            {
                views.setViewVisibility(R.id.app, View.VISIBLE);
                views.setViewVisibility(R.id.sorryText, View.GONE);
                views.setViewVisibility(R.id.loadingText, View.GONE);

                if(appInfos != null)
                {
                    for(int i = 0; i < appInfos.size(); i++)
                    {
                        views.addView(R.id.appInfo, inflateLayout(appInfos.get(i), context, _class, widgetID));
                    }
                }

                Intent refreshWidgetIncrement = new Intent(context, WidgetControlService.class);
                refreshWidgetIncrement.putExtra(WidgetControlService.INCREMENT, true);
                refreshWidgetIncrement.putExtra(WidgetControlService.DECREMENT, false);
                refreshWidgetIncrement.putExtra(WidgetControlService.CLASS, _class);
                refreshWidgetIncrement.putExtra(WidgetControlService.WIDGET_ID, widgetID);
                refreshWidgetIncrement.setData(Uri.withAppendedPath(Uri.parse("BFR://widget/id/#togetituniqie" + WidgetControlService.class.toString()), UUID.randomUUID().toString()));
                PendingIntent pIntentIncrement = PendingIntent.getService(context, 0, refreshWidgetIncrement, PendingIntent.FLAG_CANCEL_CURRENT);
                views.setOnClickPendingIntent(R.id.arrowRight, pIntentIncrement);

                Intent refreshWidgetDecrement = new Intent(context, WidgetControlService.class);
                refreshWidgetDecrement.putExtra(WidgetControlService.DECREMENT, true);
                refreshWidgetDecrement.putExtra(WidgetControlService.INCREMENT, false);
                refreshWidgetDecrement.putExtra(WidgetControlService.CLASS, _class);
                refreshWidgetDecrement.putExtra(WidgetControlService.WIDGET_ID, widgetID);
                refreshWidgetDecrement.setData(Uri.withAppendedPath(Uri.parse("BFR://widget/id/#togetituniqie" + WidgetControlService.class.toString()), UUID.randomUUID().toString()));
                PendingIntent pIntentDecrement = PendingIntent.getService(context, 0, refreshWidgetDecrement, PendingIntent.FLAG_CANCEL_CURRENT);
                views.setOnClickPendingIntent(R.id.arrowLeft, pIntentDecrement);
            }

            AppWidgetManager manager = AppWidgetManager.getInstance(this);
            manager.updateAppWidget(widgetID, views);
        }
        catch(Exception e)
        {
            Log.e("buildUpdate -- WidgetUpdateService", "buildUpdate -- WidgetUpdateService: error: " + e.toString());
        }
    }

    private RemoteViews inflateLayout(AppInfo appInfo, Context context, Class<?> _class, int widgetID) throws IOException
    {
        RemoteViews view = null;
        if(_class.equals(Widget_1_6.class) || _class.equals(Widget_1_6_2.class))
        {
            view = new RemoteViews(getPackageName(), R.layout.widget_1_6_body_fragment);
            view = inflateWidget1_6(view, appInfo, context, _class, widgetID);
        }
        else if(_class.equals(Widget_2_6.class) || _class.equals(Widget_2_6_2.class))
        {
            view = new RemoteViews(getPackageName(), R.layout.widget_2_6_body_fragment);
            view = inflateWidget2_6(view, appInfo, context, _class, widgetID);
        }

        return view;
    }

    private RemoteViews inflateWidget1_6(RemoteViews view, AppInfo appInfo, Context context, Class<?> _class, int widgetID) throws IOException
    {
        view.setTextViewText(R.id.appTitle, appInfo.title);
        view.setTextViewText(R.id.appDesc, appInfo.shortDesc);

        File file = new File(Constants.saveImageLocation + appInfo.iconLoc);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 1;
        Bitmap bitmap = null;
        if(file.exists()) // if exists in local
        {
            bitmap = BitmapFactory.decodeFile(Constants.saveImageLocation + appInfo.iconLoc, options);
        }
        else if (CustomAndroidTools.isImageFoundInAssets(appInfo.iconLoc, context))
        {
            bitmap = CustomAndroidTools.getImageFromAssets(appInfo.iconLoc, context);
        }
        else
        {
            bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.broken, options);
        }
        view.setImageViewBitmap(R.id.appIcon, bitmap);      

        view.setOnClickPendingIntent(R.id.appIcon, null);
        view.setOnClickPendingIntent(R.id.appDetail, null);

        Intent openApp = new Intent(context, AppDetailViewActivity.class);                      
        openApp.putExtra(AppDetailViewActivity.LAST_SELECTED_APP, appInfo);
        Log.e("current App", "current App: name: " + appInfo.title + ", category index: " + appInfo.category + ". For Widget type: " + _class.toString() + ", id: " + widgetID);
        openApp.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
        openApp.setData(Uri.withAppendedPath(Uri.parse("BFR://widget/id/#togetituniqie" + new ComponentName(context, _class)), UUID.randomUUID().toString()));
        PendingIntent pIntent = PendingIntent.getActivity(context, 0, openApp, 0);                      

        view.setOnClickPendingIntent(R.id.appIcon, pIntent);                    
        view.setOnClickPendingIntent(R.id.appDetail, pIntent);

        return view;
    }

    private RemoteViews inflateWidget2_6(RemoteViews view, AppInfo appInfo, Context context, Class<?> _class, int widgetID) throws IOException
    {
        File file = new File(Constants.saveImageLocation + appInfo.bannerLoc);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 1;
        Bitmap bitmap = null;
        if(file.exists()) // if exists in local
        {
            bitmap = BitmapFactory.decodeFile(Constants.saveImageLocation + appInfo.bannerLoc, options);
        }
        else if (CustomAndroidTools.isImageFoundInAssets(appInfo.bannerLoc, context))
        {
            bitmap = CustomAndroidTools.getImageFromAssets(appInfo.bannerLoc, context);
        }
        else
        {
            bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.broken, options);
        }
        view.setImageViewBitmap(R.id.appBanner, bitmap);        

        view.setOnClickPendingIntent(R.id.appBanner, null);

        Intent openApp = new Intent(context, AppDetailViewActivity.class);                      
        openApp.putExtra(AppDetailViewActivity.LAST_SELECTED_APP, appInfo);
        Log.e("current App", "current App: name: " + appInfo.title + ", category index: " + appInfo.category + ". For Widget type: " + _class.toString() + ", id: " + widgetID);
        openApp.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
        openApp.setData(Uri.withAppendedPath(Uri.parse("BFR://widget/id/#togetituniqie" + new ComponentName(context, _class)), UUID.randomUUID().toString()));
        PendingIntent pIntent = PendingIntent.getActivity(context, 0, openApp, 0);                      

        view.setOnClickPendingIntent(R.id.appBanner, pIntent);  

        return view;
    }

有什么我遗漏的吗?

好的。成功了,问题出在 RemoteView 的行为上。(注意:RemoteView 会将最新更改发送到需要 refresh/update 的视图)

所以我没有使用 manager.updateAppWidget(widgetID, views);在所有场合。

我使用了一个标志来知道何时使用 updateAppWidget 或 partiallyUpdateAppWidget。

下面是上面清单中 buildUpdate 函数下的修改代码,替换了 manager.updateAppWidget(widgetID, views);进入:

if(!GlobalStorage.tempStorage.containsKey(_class.toString() + widgetID) || GlobalStorage.tempStorage.get(_class.toString() + widgetID) == 0)
            {
                Log.e("buildUpdate -- WidgetUpdateService", "buildUpdate -- WidgetUpdateService: either not in collection or is 0: do full update");
                manager.updateAppWidget(widgetID, views);
            }
            else
            {
                Log.e("buildUpdate -- WidgetUpdateService", "buildUpdate -- WidgetUpdateService: is not 0: do partial update");
                manager.partiallyUpdateAppWidget(widgetID, views);
            }

使用修改后的逻辑,当视图需要 refresh/update 时(我们无法控制大部分时间,例如方向更改)。它不会覆盖整个视图,只会覆盖部分视图。