内容提供者故障

Content Provider fault

在我的应用程序中,我使用了内容提供程序。如您所知,内容提供者是客户端和 SQLite 之间的中间人。在我的例子中,我使用 volley 从服务器检索数据,将它们存储在 SQLite 中,最后使用 ContentResolver 对象和 LoaderManager 接口(具有 onCreateLoader、onLoadFinished、onLoaderReset)读取它们).我还使用了一项服务,因为我想在应用程序关闭时 运行 我的网络服务。

我的服务

  public class MyService extends IntentService {
  private final String LOG_TAG = MyService.class.getSimpleName();


  public MyService() {
    super("My Service");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    updateCityList();
  }
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }
  public void updateCityList() {

    cityList.clear();

    // Instantiate the RequestQueue.
    RequestQueue queue = Volley.newRequestQueue(this);

    // Request a string response from the provided URL.
    JsonArrayRequest jsObjRequest = new JsonArrayRequest(Request.Method.GET, 
   API.API_URL, new Response.Listener<JSONArray>() {

        @Override
        public void onResponse(JSONArray response) {

            Log.d(TAG, response.toString());
            //hidePD();

            // Parse json data.
            // Declare the json objects that we need and then for loop through the children array.
            // Do the json parse in a try catch block to catch the exceptions
            try {

                for (int i = 0; i < response.length(); i++) {

                    JSONObject post = response.getJSONObject(i);

                    MyCity item = new MyCity();
                    item.setName(post.getString("title"));
                    item.setImage(API.IMAGE_URL + post.getString("image"));
                    ContentValues imageValues = new ContentValues();

                    imageValues.put(MyCityContract.MyCityEntry._ID, post.getString("id"));
                    imageValues.put(MyCityContract.MyCityEntry.COLUMN_NAME, post.getString("title"));
                    imageValues.put(MyCityContract.MyCityEntry.COLUMN_ICON, post.getString("image"));

                    getContentResolver().insert(MyCityContract.MyCityEntry.CONTENT_URI, imageValues);
                    cityList.add(item);

                    cityList.add(item);

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

            // Update list by notifying the adapter of changes
            myCityAdpapter.notifyDataSetChanged();
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            VolleyLog.d(TAG, "Error: " + error.getMessage());
            //hidePD();
        }
    });
    queue.add(jsObjRequest);
}
static public class AlarmReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent sendIntent = new Intent(context, MyService.class);
        context.startService(sendIntent);
    }
   }
}

MainActivityFragment

 public class MainActivityFragment extends Fragment implements    

 LoaderManager.LoaderCallbacks<Cursor>{
 static public ArrayList<MyCity> cityList;
 public String [] MY_CITY_PROJECTIONS = {MyCityContract.MyCityEntry._ID,
        MyCityContract.MyCityEntry.COLUMN_NAME,
        MyCityContract.MyCityEntry.COLUMN_ICON};
private static final String LOG_TAG =    
MainActivityFragment.class.getSimpleName();
public static MyCityAdpapter myCityAdpapter;
private static final int CURSOR_LOADER_ID = 0;
private GridView mGridView;

public MainActivityFragment() {
    // Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Add this line in order for this fragment to handle menu events.
    setHasOptionsMenu(true);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.refresh, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_refresh) {

        return true;
    }
    return super.onOptionsItemSelected(item);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {

    super.onActivityCreated(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    // inflate fragment_main layout
    final View rootView = inflater.inflate(R.layout.fragment_main_activity, container, false);

    cityList = new ArrayList<>();

    // initialize our FlavorAdapter
    myCityAdpapter = new MyCityAdpapter(getActivity(), null, 0, CURSOR_LOADER_ID);
    // initialize mGridView to the GridView in fragment_main.xml
    mGridView = (GridView) rootView.findViewById(R.id.flavors_grid);
    // set mGridView adapter to our CursorAdapter
    mGridView.setAdapter(myCityAdpapter);
    Cursor c =
            getActivity().getContentResolver().query(MyCityContract.MyCityEntry.CONTENT_URI,
                    new String[]{MyCityContract.MyCityEntry._ID},
                    null,
                    null,
                    null);
    if (c.getCount() == 0){
        updateCityData();
    }
    // initialize loader
    getLoaderManager().initLoader(CURSOR_LOADER_ID, null, this);
    super.onCreate(savedInstanceState);
    return rootView;

}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args){
    return new CursorLoader(getActivity(),
            MyCityContract.MyCityEntry.CONTENT_URI,
            MY_CITY_PROJECTIONS,
            null,
            null,
            null);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    myCityAdpapter.swapCursor(data);
}


@Override
public void onLoaderReset(Loader<Cursor> loader){
    myCityAdpapter.swapCursor(null);
}

public void updateCityData() {

    Intent alarmIntent = new Intent(getActivity(), MyService.AlarmReceiver.class);


    //Wrap in a pending intent which only fires once.
    PendingIntent pi = PendingIntent.getBroadcast(getActivity(), 0,alarmIntent,PendingIntent.FLAG_ONE_SHOT);//getBroadcast(context, 0, i, 0);

    AlarmManager am=(AlarmManager)getActivity().getSystemService(Context.ALARM_SERVICE);

    //Set the AlarmManager to wake up the system.
    am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000, pi);
    }
 }

我刚刚设置了一个警报管理器,让我的服务在 5 秒后 运行。这真的只是为了测试。无论如何,这是我的问题。当我第一次启动该应用程序时,我的屏幕上没有显示任何内容。但是,当我退出并再次启动它时,我可以在我的网格视图中看到所有图像。为什么会这样?为了更清楚

当我第一次启动应用程序时:

10-16 12:07:00.799 16685-16685/theo.testing.androidcustomloaders D/ContentValues: [{"id":"15","title":"The Gate of Larissa","image":"larissa17.png"},{"id":"14","title":"Larissa Fair","image":"larissa14.png"},{"id":"13","title":"Larissa Fair","image":"larissa13.png"},{"id":"12","title":"AEL FC Arena","image":"larissa12.png"},{"id":"11","title":"AEL FC Arena","image":"larissa11.png"},{"id":"10","title":"Alcazar Park","image":"larissa10.png"},{"id":"9","title":"Alcazar Park","image":"larissa9.png"},{"id":"8","title":"Church","image":"larissa8.png"},{"id":"7","title":"Church","image":"larissa7.png"},{"id":"6","title":"Old trains","image":"larissa6.png"},{"id":"5","title":"Old trains","image":"larissa5.png"},{"id":"4","title":"Munipality Park","image":"larissa4.png"},{"id":"3","title":"Munipality Park","image":"larissa3.png"},{"id":"2","title":"Ancient Theatre - Larissa","image":"larissa2.png"},{"id":"1","title":"Ancient Theatre - Larissa","image":"larissa1.png"}]

为了显示数据,我需要退出应用程序并重新启动它。为什么会这样?我的代码有问题吗?

LoadManager 不处理您在数据库中的更改,因为它与数据库没有任何连接。你必须注册观察者才能处理这些东西。

在您的 myCityProvider 中,在 query(...) 方法中缺少方法 setNotificationUri。应该设置在最后。

这里修改了你的query方法:

@Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor retCursor;

        switch (sUriMatcher.match(uri)) {
            // All Flavors selected
            case MY_CITY: {
                retCursor = myCityDbHelper.getReadableDatabase().query(
                        MyCityContract.MyCityEntry.TABLE_MY_CITY,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder);
                break;
            }
            // Individual flavor based on Id selected
            case MY_CITY_WITH_ID: {
                retCursor = myCityDbHelper.getReadableDatabase().query(
                        MyCityContract.MyCityEntry.TABLE_MY_CITY,
                        projection,
                        MyCityContract.MyCityEntry._ID + " = ?",
                        new String[]{String.valueOf(ContentUris.parseId(uri))},
                        null,
                        null,
                        sortOrder);
                break;
            }

            default: {
                // By default, we assume a bad URI
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }

        if (retCursor != null) {
            retCursor.setNotificationUri(getContext().getContentResolver(), uri);
        }

        return retCursor;
    }

我检查了您的 git 存储库,我认为您应该修复您的 MainActivityFragment。你在 onCreateView 中做所有事情,但你应该在这里做所有与视图相关的事情或只是 return 膨胀视图。之后,您可以在 onViewCreated.

中完成剩下的工作

你应该这样做:

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main_activity, container, false);
    }

    @Override
    public void onViewCreated(View rootView, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(rootView, savedInstanceState);

        myCityAdpapter = new MyCityAdpapter(getActivity(), null, 0);
        mGridView = (GridView) rootView.findViewById(R.id.flavors_grid);
        mGridView.setAdapter(myCityAdpapter);

        getLoaderManager().initLoader(CURSOR_LOADER_ID, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {

        switch (id) {
            case CURSOR_LOADER_ID:
                return new CursorLoader(getActivity(),
                        MyCityContract.MyCityEntry.CONTENT_URI,
                        null,
                        null,
                        null,
                        null);
            default:
                throw new IllegalArgumentException("id not handled!");
        }
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        switch (loader.getId()) {
            case CURSOR_LOADER_ID:
                if (data == null || data.getCount() == 0) {
                    updateCityData();
                } else {
                    myCityAdpapter.swapCursor(data);
                }
                break;
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        myCityAdpapter.swapCursor(null);
    }

多亏了:

  • 如果应用程序打开,加载程序将加载他拥有的所有内容(可以是 0 个项目),但如果没有任何项目,它将调用服务下载更多内容并存储在数据库中
  • 如果您通过服务添加任何数据,将再次调用 onLoadFinished 并刷新适配器