1. Nimm jetzt an unserem Uhans - 3. ADVENT - Gewinnspiel teil - Alle Informationen findest Du hier!

ContentProvider - Grundsätzliche Verständnisfragen - Und einiges im Detail

Dieses Thema im Forum "Android App Entwicklung" wurde erstellt von mradlmaier, 28.03.2012.

  1. mradlmaier, 28.03.2012 #1
    mradlmaier

    mradlmaier Threadstarter Gewerbliches Mitglied

    Beiträge:
    158
    Erhaltene Danke:
    17
    Registriert seit:
    28.10.2011
    Leider ist die SDK Doku nicht sonderlich ergiebig, was ContentProvider betrifft.
    Der Doku kann ich entnehmen:
    1. ContentProvider sind dafür da, Daten an andere Apps im Android Ökosystem zu exponieren
    2. Für interne Verwendung sind sie nicht notwendig
    3. Es gibt 4 Methoden, die die übliche CRUD Datenbankanbindung ermöglichen: query(), insert(), update(), delete()
    4. URIs werden benutzt, um innerhalb dieser Methoden zu unterscheiden, welche Queries exekutiert werden, sofern es mehr als z.B. einen SELECT-query im DatabaseAdapter gibt.
    5. Das impliziert auch, das ich komplexe Datenbank-Operationen via DatabaseAdapter durchführen kann, wie z.b. eine Transaktion, die erst per SELECT überprüft, ob schon ein Datensatz existiert und in Abhängigkeit davon, entweder einen INSERT oder UPDATE Query ausführt.
    6. Ob die Daten aus einer DB oder Dateien kommen, spielt keine Rolle, prinzipiell kann der ContentProvider seine Daten vom einem DatabaseAdapter oder auch aus Dateien holen
    7. Die Signature der 4 Methoden gibt den Type an der zurückgegeben wird.
    8. Abgesehen davon kann Ich die Daten herholen wo ich will, wie ich will, solange ich sie in den entsprechende Typ wrappe, im Falle von query() ein Cursor oder eine Unterklasse von Cursor, z.B. MatrixCursor
    9. In den MatrixCursor kann ich jede beliebige Datenstruktur packen
    10. Wenn keine Permissions im Manifest definiert sind, ist jeder Zugriff erlaubt

    Ist das so korrekt?
    Bitte um Kommentare.

    Hintergrund ist der dass ich einen Custom ContentProvider implementiert habe, der bei einfachen SELECT-Queries funktioniert, bei komplexen Tarnsaktionen (ohne Fehler im LogCat) aber nicht funktioniert:

    Folgenden Code habe ich auch auf stackoverflow.com android - Execute "hybrid" transactional queries through the ContentProvider class? Is this possible? - Stack Overflow wegen der besseren Lesbarkeit des Codes duch Syntaxhighlighting gepostet.

    Hier meine query() Methode aus dem Contentprovider (Merke, die URIs SAINTS, SAINT_ID, SAINTS_OF_DAY funktionieren problemlos):

    Code:
    	public Cursor query(Uri uri, String[] projection, String selection,
    			String[] selectionArgs, String sortOrder) {
    		// TODO Auto-generated method stub
    		Log.d(TAG, "*******");
    		Log.d(TAG, "query()");
    		Log.d(TAG, "*******");
    		Log.d(TAG, "uri: " + uri.toString());
    		Log.d(TAG, "sURIMatcher.match(uri) == SAINTS: " + Boolean.toString(sURIMatcher.match(uri) == SAINTS));
    		Log.d(TAG, "sURIMatcher.match(uri) == SAINTS_OF_DAY: " + Boolean.toString(sURIMatcher.match(uri) == SAINTS_OF_DAY));
    		Log.d(TAG, "sURIMatcher.match(uri) == SAINT_ID: " + Boolean.toString(sURIMatcher.match(uri) == SAINT_ID));
    		Log.d(TAG, "sURIMatcher.match(uri) == LAYOUTS4X1: " + Boolean.toString(sURIMatcher.match(uri) == LAYOUTS4X1));
    		Log.d(TAG, "sURIMatcher.match(uri) == LAYOUTS4X1_ID: " + Boolean.toString(sURIMatcher.match(uri) == LAYOUTS4X1_ID));
    		SQLiteDatabase db = dbAdapter.openDatabaseForWrite();
    		Cursor resultCursor = null;
    		if(sURIMatcher.match(uri) == SAINT_ID || sURIMatcher.match(uri) == SAINTS || sURIMatcher.match(uri) == SAINTS_OF_DAY){
    
    			SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
    			qBuilder.setTables("saints");
    			if(sURIMatcher.match(uri) == SAINT_ID){
    				qBuilder.appendWhere("_id" + uri.getLastPathSegment());
    			}
    			resultCursor = qBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder, null);
    		} else if(sURIMatcher.match(uri) == LAYOUTS4X1_ID) {
    			resultCursor = dbAdapter.getOrInsertDefaultLayoutId3(
    					Integer.parseInt(selectionArgs[0]), 
    					Integer.parseInt(selectionArgs[1]), 
    					Integer.parseInt(selectionArgs[2]), 
    					Integer.parseInt(selectionArgs[3]));
    		} else if (sURIMatcher.match(uri) == LAYOUTS4X1){
    			Log.d(TAG, "query for all layout_id in the layouts4x1 table");
    		}
    		return resultCursor;
    	}
    Hier die entsprechnde Methode im DatabaseAdapter:
    Code:
        public void insertOrUpdate3(int widgetId, int layoutId, int textColor, int borderColor){
        	Log.d(TAG, "***************");
        	Log.d(TAG, "insertOrUpdate3");
        	Log.d(TAG, "***************");
        	Cursor selectCursor = null;
        	Cursor insertCursor = null;
        	Cursor updateCursor = null;
        	db = openDatabaseForWrite();
        	//db.beginTransaction();
            try {
            	String selectSql = "SELECT layout_id FROM layouts4x1 WHERE widget_id=" + Integer.toString(widgetId);
        		Log.d(TAG, selectSql);
            	selectCursor = db.rawQuery(selectSql, null);
            	Log.d(TAG, "selectCursor.getCount(): " + Integer.toString(selectCursor.getCount()));
            	if(selectCursor.getCount() == 0){
            		// insert defaultLayoutId
            		String insertSql = "INSERT INTO layouts4x1 (widget_id, layout_id, text_color, border_color) VALUES(" 
            				+ Integer.toString(widgetId) + ", "
                    		+ Integer.toString(layoutId) + ", "
                            + Integer.toString(textColor) + ", "
            				+ Integer.toString(borderColor) + ")";
            		Log.d(TAG, "insertSql: " + insertSql);
                	insertCursor = db.rawQuery(selectSql, null);
                	insertCursor.close();
                	Log.d(TAG, "Integer.toString(defaultLayoutId): " + Integer.toString(layoutId));
            	}else{
            		// update layout_id
            		String updateSql = "UPDATE layouts4x1 SET layout_id =" + Integer.toString(layoutId)
            				+ ", text_color=" + Integer.toString(textColor)
            				+ ", border_color=" + Integer.toString(borderColor)
            				+  " WHERE widget_id ="+ Integer.toString(widgetId);
            		Log.d(TAG, updateSql);
                	updateCursor = db.rawQuery(updateSql, null);
                	updateCursor.close();
                	Log.d(TAG, "Integer.toString(layoutId): " + Integer.toString(layoutId));
            	}
                //db.setTransactionSuccessful();
            } catch (RuntimeException e) {
                Log.e("TransactionalActivity", "Error in transaction", e);
                throw e;
            } finally {
                //db.endTransaction();
            	selectCursor.close();
                db.close();
            	Log.d(TAG, "*******************");
            	Log.d(TAG, "end insertOrUpdate3");
            	Log.d(TAG, "*******************");
            }
        }
    Die Transaktion-Statements sind zwecks Test auskommentiert, also kann es daran nicht liegen.
    Auf stackoverflow.com finde sich die Aussage, das per default, ContentProvider keine Zugriffsrestriktionen unterliegen sofern nicht anders im Manifest definiert (bei mir sind keine Permissions im Manifest festgelegt) database - ContentProvider android permission - Stack Overflow

    Nachdem es keine Exceptions, keine Errors im Logcat gibt, und sich alles kompiliert, weiss ich gar nicht wo ich suchen anfangen soll, zumal, die Tasache, dass die einfachen SELECT Queries für SAINT etc. URIs funktionieren. Das belegt, das der Zugriff auf den ContentProvider prinzipiell funktioniert.

    Wenn ich die DB vom Gerät runterlade und mit SQLite Expert angucke, sehe ich dass, keinerlei Daten geschrieben worden sind.

    Bin um jeden Hinweis dankbar.
     
    Zuletzt bearbeitet: 28.03.2012
  2. mradlmaier, 28.03.2012 #2
    mradlmaier

    mradlmaier Threadstarter Gewerbliches Mitglied

    Beiträge:
    158
    Erhaltene Danke:
    17
    Registriert seit:
    28.10.2011
    [GELÖST]
    Das Problem lag ganz woanders. db.rawQuery() hat nicht funktioniert. Stattdessen db.insert() und db.update() benutzt, funktioniert.

    Im übrigen heisst das natürlich, das obige Aussagen über ContentProvider wohl richtig sind.
     

Diese Seite empfehlen