AsynTask with RemoteViews in StackView Widget

2019-07-26 20:27发布

问题:

I've tried to search for information about StackView Widget and i've found a lot , but nobody used to get data from url . So the problem is , while i'm processing url , my RemoteViews are null , so they don't show nothing. Once the process is done i want to update that RemoteViews from asyntask (there are in the same class). How can i do that ? Or syncronize them ?

Here's my code for Widget service:

public class RSSService extends RemoteViewsService {

private ManagerRSS mr;
private Cursor c;
private int lenght;
private List<Provider> mWidgetItems = new ArrayList<Provider>();
private RemoteViews rv = null;
public ArrayList<Item> result = new ArrayList<Item>();
private boolean isFinished = false;

@Override
public void onCreate() {
    // TODO Auto-generated method stub
    mr = new ManagerRSS(this.getApplicationContext());
    mr.open();
    mr.openRead();
    c = mr.getCursor();
    lenght = c.getCount();
}

@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    c.close();
    mr.close();
    super.onDestroy();
}

@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
    // TODO Auto-generated method stub
    return new RSSRemoteViewsFactory(this.getApplicationContext(), intent);
}

class RSSRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory{

    private Context mContext;
    private int mAppWidgetId;


    public RSSRemoteViewsFactory(Context applicationContext, Intent intent) {
        // TODO Auto-generated constructor stub
        mContext = applicationContext;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return lenght;
    }

    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return arg0;
    }

    @Override
    public RemoteViews getLoadingView() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public RemoteViews getViewAt(int position) {
        if(isFinished){
            rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
            rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).getTitle());
            if(mWidgetItems.get(position).getTitle().equals("El Pais")){
                rv.setImageViewResource(R.id.iNotice, R.drawable.elpais);
            }else if(mWidgetItems.get(position).getTitle().equals("Marca")){
                rv.setImageViewResource(R.id.iNotice, R.drawable.marca);
            }else if(mWidgetItems.get(position).getTitle().equals("As")){
                rv.setImageViewResource(R.id.iNotice, R.drawable.as1);
            }else if(mWidgetItems.get(position).getTitle().equals("Ideal")){
                rv.setImageViewResource(R.id.iNotice, R.drawable.ideal);
            }else{
                rv.setImageViewResource(0, R.drawable.undefined);
            }
            rv.setTextViewText(R.id.tvLastNotice, result.get(position).getTitle());
            rv.setTextViewText(R.id.tvLastNoticePublish, result.get(position).getPubDate());
            Bundle extras = new Bundle();
            extras.putString(Widget.NAME_DIARY, mWidgetItems.get(position).getTitle());
            Intent fillInIntent = new Intent();
            fillInIntent.putExtras(extras);
            rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
            try {
                System.out.println("Loading view " + position);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return rv;
        }else{
            return null;
        }

    }

    @Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        return 1;
    }

    @Override
    public boolean hasStableIds() {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public void onCreate() {
        while(c.moveToNext()){
            String title = c.getString(c.getColumnIndex(Contrato.TableRSS.TITLE));
            String link = c.getString(c.getColumnIndex(Contrato.TableRSS.LINK));
            long _id = c.getLong(c.getColumnIndex(Contrato.TableRSS._ID));
            mWidgetItems.add(new Provider(_id, title, link));
            executeThread(link, c.getPosition());
        }
    }

    void executeThread(String type_link,int position){
        myThread exe = new myThread();
        exe.setOnlyFirst(true);
        exe.execute(new String[]{type_link});
    }

    private class myThread extends MyAsyncTask{

        @Override
        protected void onPostExecute(ArrayList<Item> result2) {
            // TODO Auto-generated method stub
            super.onPostExecute(result2);
            for(int i=0;i<result2.size();i++) result.add(result2.get(i));
            if(result.size()+1 == c.getCount()) isFinished = true;
        }
    }

    @Override
    public void onDataSetChanged() {

    }

    @Override
    public void onDestroy() {mWidgetItems.clear();}

}

}

And this is my WidgetProvider :

public class Widget  extends AppWidgetProvider{

public static final String ON_TOUCH = "com.cool.nightrss.ON_TOUCH";
public static final String ID_DIARY = "com.cool.nightrss.ID_DIARY";
public static final String ID_NOTICE = "com.cool.nightrss.ID_NOTICE";
public static final String NAME_DIARY = "com.cool.nightrss.NAME_DIARY";
public static final String LINK_NOTICE = "com.cool.nightrss.LINK_NOTICE";

@Override
public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub
    if (intent.getAction().equals(ON_TOUCH)) {
        int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
        String name = intent.getStringExtra(NAME_DIARY);
        Toast.makeText(context, "Touched view " + name +" ID "+appWidgetId, Toast.LENGTH_SHORT).show();
    }
    super.onReceive(context, intent);
}

@SuppressWarnings("deprecation")
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds) {
    // TODO Auto-generated method stub
    for (int i = 0; i < appWidgetIds.length; ++i) {

        Intent intent = new Intent(context, RSSService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);

        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);

        Intent toastIntent = new Intent(context, Widget.class);
        toastIntent.setAction(Widget.ON_TOUCH);
        toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);

        appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
    }
    super.onUpdate(context, appWidgetManager, appWidgetIds);
}

}

回答1:

You can use the method onDataSetChanged in class RSSRemoteViewsFactory. 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.

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private final String TAG = StackRemoteViewsFactory.class.getSimpleName();

private Context mContext;
private int mAppWidgetId;

private List<Game> mGameItems = new ArrayList<Game>();

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

public void onCreate() {
}

public void onDestroy() {
    mGameItems.clear();
}

public int getCount() {
    return mGameItems.size();
}

public RemoteViews getViewAt(int position) {
    Game game = mGameItems.get(position);

    Bundle extras = new Bundle();
    extras.putString(StackWidgetProvider.EXTRA_ITEM, game.getGameUrl());
    Intent fillInIntent = new Intent();
    fillInIntent.putExtras(extras);
    rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);


    Bitmap bitmap = null;
    try {
        URL imageUrl = new URL(game.getImageUrl());
        bitmap = BitmapFactory.decodeStream(imageUrl.openConnection().getInputStream());
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    rv.setImageViewBitmap(R.id.widget_item, bitmap);
    return rv;
}

public RemoteViews getLoadingView() {
    return null;
}

public int getViewTypeCount() {
    return 1;
}

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

public boolean hasStableIds() {
    return true;
}

public void onDataSetChanged() {

    final String uri = <set your url>

    DefaultHttpClient httpClient = new DefaultHttpClient();
    HttpGet httpGet = new HttpGet(uri);

    String jsonData = null;

    /**
     * Fetch JSON from web service
     * TODO: try to call a AsyncTask
     */
    try {
        HttpResponse httpResponse = httpClient.execute(httpGet);
        HttpEntity httpEntity = httpResponse.getEntity();
        InputStream inputStream = httpEntity.getContent();

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder data = new StringBuilder();
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            data.append(line);
        }
        jsonData = new String(data);
        Log.d(TAG, String.format("JSON: %s", jsonData));

    } catch (IOException e) {
        e.printStackTrace();
    }

    /**
     * Mapping JSON String to objects
     * TODO: Probably move the code to a dedicated class and use GSON to do the mapping?????
     */
    if(jsonData != null && !jsonData.equals("")) {
        try {
            JSONArray jsonArray = new JSONArray(jsonData);
            for(int index = 0; index < jsonArray.length(); index++) {
                JSONObject jsonObject = jsonArray.getJSONObject(index);
                String id = jsonObject.getString("id");
                String imageUrl = jsonObject.getString("imageUrl");
                String gameUrl = jsonObject.getString("gameUrl");

                mGameItems.add(
                        new Game(id, imageUrl, gameUrl)
                );
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    Log.d(TAG, String.format("List of games: %s", mGameItems));
}

}

In the following example, I'm fetching a json from a webserver and then I fetch the images from each item. The onDataSetChanged is used to fetch the json content and the in the getViewAt I fetch the image.

Check the comments at the StackView example provided by Google - http://docs.huihoo.com/android/3.0/resources/samples/StackWidget/index.html

Hope it helps you