Why do my android home screen widgets becoming slow?

708 Views Asked by At

I've developped some homescreen widgets displaying an adapterviewflipper layout. They automatically start and have a flip interval of 3 seconds. They display a small collection with custom animation in and out animation (total time 2 seconds). The collection data is refreshed each minute.

After some indeterminate time the flip interval is getting bigger and can take about 17 seconds to animate. I dont understand why.

Is there an android limitation ? Memory leak issue ? If i restart the phone, time is respected but i will encounter the issue in a near future.

Widget Layout

<AdapterViewFlipper
        android:id="@+id/calendar_widget_adapterViewFlipper"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:autoStart="true"
        android:flipInterval="3000"
        android:animateLayoutChanges="true"
        android:inAnimation="@animator/slide_in_bottom"
        android:outAnimation="@animator/fade_out"
        />

AppWidget Provider

 @Override
    public void onReceive(Context context, Intent intent) {

        //Log.d(LOG_TAG, "Received intent " + intent);

        switch (intent.getAction()){
            case ACTION_MESSAGE_DATA_UPDATE :

                ComponentName thisAppWidget = new ComponentName(context.getPackageName(), getClass().getName());
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                int ids[] = appWidgetManager.getAppWidgetIds(thisAppWidget);
                for (int appWidgetID: ids) {
                    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetID, R.id.calendar_widget_adapterViewFlipper);
                }

                break;
...

App Widget Provider configuration file

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialKeyguardLayout="@layout/calendar_widget"
    android:initialLayout="@layout/calendar_widget"
    android:autoAdvanceViewId="@id/calendar_widget_adapterViewFlipper"
    android:minWidth="250dp"
    android:minHeight="50dp"
    android:minResizeWidth="250dp"
    android:minResizeHeight="50dp"
    android:previewImage="@drawable/calendar_ori_portrait"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="0"
    android:widgetCategory="home_screen|keyguard"></appwidget-provider>

CalendarWidgetRemoteViewsService

public class CalendarWidgetRemoteViewsService extends RemoteViewsService {

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new CalendarWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
    }

}

class CalendarWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

    private Context mContext;
    private int mAppWidgetId;

    private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();

    public CalendarWidgetRemoteViewsFactory(Context context, Intent intent) {
        mContext = context;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    @Override
    public void onCreate() {
        // In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
        // for example downloading or creating content etc, should be deferred to onDataSetChanged()
        // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.

    }

    @Override
    public void onDataSetChanged() {
        // This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
        // on the collection view corresponding to this factory. You can do heaving lifting in
        // here, synchronously. For example, if you need to process an image, fetch something
        // from the network, etc., it is ok to do it here, synchronously. The widget will remain
        // in its current state while work is being done here, so you don't need to worry about
        // locking up the widget.

        //Log.d("CWRVS", "onDataSetChanged begins");

        final int[] ErrorCode = {0};
        mWidgetItems.clear();


        if (!UtilsHelper.isOnline(mContext)){
            ErrorCode[0] = 1;
        }

        if (ErrorCode[0] == 0)
        {
            final CountDownLatch latchSignal = new CountDownLatch(1);

            AuthenticationHelper authenticationHelper = AuthenticationHelper.getInstance(mContext);
            authenticationHelper.acquireTokenSilentlyWithAccountCheck(new AuthenticationCallback() {

                @Override
                public void onSuccess(AuthenticationResult authenticationResult) {
                    //Log.d("AUTH", "onSuccess");
                    final GraphHelper graphHelper = GraphHelper.getInstance();

                    graphHelper.getAllCalendarsEventsSupEqToday(authenticationResult.getAccessToken(), new ICallback<List<WidgetItem>>() {
                        @Override
                        public void success(List<WidgetItem> lwi) {
                            mWidgetItems = lwi;
                            latchSignal.countDown();
                        }

                        @Override
                        public void failure(ClientException ex) {
                            //Log.e("GRAPH", "Error getting events", ex);
                            latchSignal.countDown();
                        }
                    });
                }

                @Override
                public void onError(MsalException exception) {
                    //Log.d("AUTH", "onError");

                    //if ( exception.getErrorCode() == AuthenticationHelper.NO_CLIENT_ACCOUNT_REGISTERED  ){

                    //}
                    ErrorCode[0] = 2;
                    latchSignal.countDown();
                }

                @Override
                public void onCancel() {
                    //Log.d("AUTH", "onCancel");

                    latchSignal.countDown();
                }
            });
            //}



            try{
                latchSignal.await(15, TimeUnit.SECONDS);
            }
            catch (InterruptedException e){
                Log.e("CDN", "CountDownException", e);
            }
        }


        //Sending BroadCast for Error
        Intent ErrorIntent = new Intent(mContext, CalendarWidgetProvider.class);
        ErrorIntent.setAction(CalendarWidgetProvider.ACTION_CALENDAR_DATA_PARTIAL_UPDATE);

        Bundle extras = new Bundle();
        extras.putInt(CalendarWidgetProvider.EXTRA_ERROR_CODE, ErrorCode[0]);
        ErrorIntent.putExtras(extras);
        mContext.sendBroadcast(ErrorIntent);


        //Log.d("CWRVS", "onDataSetChanged ends");



    }


    @Override
    public void onDestroy() {
        // In onDestroy() you should tear down anything that was setup for your data source,
        // eg. cursors, connections, etc.
        mWidgetItems.clear();
    }

    @Override
    public int getCount() {
        return mWidgetItems.size();
    }

    @Override
    public RemoteViews getViewAt(int position) {
        // position will always range from 0 to getCount() - 1.
        // We construct a remote views item based on our widget item xml file, and set the
        //

        if (this.getCount() == 0){
            return null;
        }

        WidgetItem wi =  mWidgetItems.get(position);
        RemoteViews rv = wi.BuildRemoteViews(mContext, position, this.getCount());

        // You can do heaving lifting in here, synchronously. For example, if you need to
        // process an image, fetch something from the network, etc., it is ok to do it here,
        // synchronously. A loading view will show up in lieu of the actual contents in the
        // interim.

        // Return the remote views object.
        return rv;
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }


}

I expect the flipinterval respect its value.

Regards

0

There are 0 best solutions below