Ошибка контент-провайдера

#android #service #android-contentprovider

#Android #Обслуживание #android-contentprovider

Вопрос:

В моем приложении я использую контент-провайдера. Как вы знаете, контент-провайдер является посредником между клиентом и. SQLite В моем случае я извлекаю данные с сервера с помощью volley, сохраняю их SQLite и, наконец, считываю их с помощью ContentResolver объекта и LoaderManager интерфейса (который имеет onCreateLoader,onLoadFinished,onLoaderReset). Я также использую сервис, так как хочу запустить свой веб-сервис, когда приложение закрыто.

MyService

   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 секунд. Это только для тестирования на самом деле. В любом случае, вот моя проблема. Когда я запускаю приложение в первый раз, на моем экране ничего не отображается. Однако, когда я выхожу и запускаю его снова, я вижу все изображения в своем gridview. Почему это происходит? Чтобы сделать более понятным

Когда я запускаю приложение в первый раз:

 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"}]
 

Для отображения данных мне нужно выйти из приложения и запустить его снова. Почему это происходит? Что-то не так с моим кодом?

Комментарии:

1. Интересно, какое отношение ваша проблема имеет к контент-провайдеру.

2. Итак, в первый раз изображения еще не загружены? И во второй раз изображения уже загружены?

3. @greenapps. Yes. Изображения необходимо загружать при первом запуске приложения. И как только вы это сделаете, вы снова откроете приложение, чтобы увидеть их.

4. и на самом деле с контент-провайдером все в порядке, так как я тоже немного поработал с ним.

5. опубликуйте свой пользовательский ContentProvider код, в вашей службе вызывайте только методы поставщика, чтобы вставлять / обновлять / удалять ваши данные

Ответ №1:

LoadManager не обрабатывает ваши изменения в базе данных, поскольку он не имеет к ней никакого отношения. Вы должны зарегистрировать observer для обработки этого материала.

В вашем 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 но вы должны делать здесь все, что связано с просмотром, или просто возвращать завышенный вид. И после этого вы можете сделать все остальное 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 будет вызван снова и обновит адаптер

Комментарии:

1. Боже! Сейчас он работает. Вы были правы. Я забыл setNotificationUri . Большое спасибо, сэр!

2. Еще раз извините. Я заметил кое-что еще. Если я ввожу новое изображение на своем сервере и запускаю приложение, оно не отображается в сетке! Чтобы увидеть это новое изображение, мне нужно зайти в настройки -> MyApp -> Очистить данные и запустить его снова.lol. Почему это происходит? Нужно ли что-то делать с методом обновления в ContentProvider? Спасибо.

3. или мне нужно создать syncadapter?

4. Пожалуйста, прочтите последнее предложение, которое я написал «если приложение откроется, загрузчик загрузит все, что у него есть (может быть 0 элементов), но если каких-либо элементов нет, он вызовет службу, чтобы загрузить больше и сохранить в БД». После первого запуска у вас уже есть какие-либо элементы, поэтому updateCityData они не запускаются. Попробуйте принудительно запустить его после вызова swapCursor , а после этого updateCityData перед разбором изображений удалите все из базы данных (или проверьте, что у вас есть, и вставьте только то, что является новым).

5. ДА. Теперь лучше. Но когда я удаляю изображение со своего сервера, это изображение все еще там. Пожалуйста, посмотрите мой обновленный код в github. Я также добавил syncadapter.