BuildContentProvider_3.zip


* query 후 화면


 



데이터를 쿼리할 때는 비동기적으로 처리해줘야 하므로, 백그라운드 스레드에서 데이터를 가져와야 한다.

-> AsyncTask Loader 사용한다.


AsyncTask Loader

- onStartLoading(): 데이터를 로드하는 콜백 메서드를 구현한다.

- loadInBackground(): query를 백그라운드에서 수행한다.

- deliverResult(): 데이터를 반환한다.



Provider code


1. 할일 데이터베이스에 대한 읽기 액세스 권한을 얻는다.

   Get readable access to the underlying task database.

2. 할일 디렉토리와 디폴트를 인식하기 위해, match와 switch-case 코드를 작성한다.

   Write match code and a switch-case to recognize the task directory and default case.

3. 할일 디렉토리를 조회한다.

   Query for the tasks directory.

4. 커서에 알림 URI를 설정한다.

   Set a notification URI on the Cursor.


data/TaskContentProvider.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.tistory.qlyh8.buildcontentprovider.data;
 
/*
 * Created by YUNHEE on 2018-01-09.
 */
 
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
 
import static com.tistory.qlyh8.buildcontentprovider.data.TaskContract.TaskEntry.TABLE_NAME;
 
public class TaskContentProvider extends ContentProvider {
 
    // 디렉토리일 경우 100, 200, 300 등으로 임의 지정한다.
    public static final int TASKS = 100;
    // 해당 디렉토리 안의 각 아이템일 경우 101, 102, 103 등으로 임의 지정한다.
    public static final int TASK_WITH_ID = 101;
 
    private static final UriMatcher sUriMatcher = buildUriMatcher();
    private TaskDbHelper mTaskDbHelper;
 
    // URI 를 상수값으로 매치 시켜주는 메서드
    public static UriMatcher buildUriMatcher() {...}
 
    // 데이터 소스를 설정하는 데 필요한 것을 초기화 해야한다.
    // 이 경우 SQLite 데이터베이스로 작업하기 때문에 DbHelper 를 초기화해야 액세스 할 수 있다.
    @Override
    public boolean onCreate() {...}
 
    // 요청된 데이터를 담고 있는 하나 이상의 로우에 대한 Cursor 객체를 반환하는 메서드.
    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        final SQLiteDatabase db = mTaskDbHelper.getReadableDatabase();
        int match = sUriMatcher.match(uri);
        Cursor retCursor;
 
        switch (match){
            case TASKS:
                retCursor = db.query(TABLE_NAME, projection, selection, selectionArgs,
                        nullnull, sortOrder);
                break;
 
            case TASK_WITH_ID:
                // URI 로 부터 id를 가져온다. URI: content://<authority>/tasks/#
                // 인덱스 0번은 path 의 tasks 부분이며, 인덱스 1번은 바로 옆의 다음 부분이 된다.
                String id = uri.getPathSegments().get(1);
                
                String mSelection = "_id=?";
                String[] mSelectionArgs = new String[]{id};
                
                retCursor =  db.query(TABLE_NAME, projection, mSelection, mSelectionArgs,
                        nullnull, sortOrder);
                break;
 
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
 
        retCursor.setNotificationUri(getContext().getContentResolver(), uri);
 
        return retCursor;
    }
 
    // 반환될 내용의 MIME 타입을 반환하는 메서드. MIME 타입은 내용이 어떤 형식으로 되어있는지 식별한다.
    @Override
    public String getType(@NonNull Uri uri) {...}
 
    /**
     * 데이터 추가 메서드
     * @param uri: 데이터를 추가할 디렉토리를 알려주는 컨텐트 URI
     * @param values: 추가할 데이터를 담고있는 ContentValues 객체
     * @return 새로 추가된 데이터의 위치를 알려주는 컨텐트 URI, 실패시 -1
     */
    @Override
    public Uri insert(@NonNull Uri uri, ContentValues values) {...}
 
    // 데이터 삭제 메서드.
    // 어느 행을 삭제할 지 알려주는 URI 를 인자로 받아야 하며, 삭제된 행의 개수를 반환한다.
    @Override
    public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {...}
 
    // 데이터 갱신 메서드.
    // insert 의 인자들을 인자로 받아, 어느 URI 에 어떤 ContentValues 에 담긴 데이터로 갱신할 것인지 알 수 있다.
    // 갱신된 행의 개수를 반환환다.
    @Override
    public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {...}
}
 
cs



UI code


5. ContentResolver를 사용하여 모든 할일 데이터를 조회하고, 해당 데이터를 우선 순위별로 분류한다.

   Use a ContentResolver to query for all of the task data, and sort that data by priority.


MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.tistory.qlyh8.buildcontentprovider;
 
import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.View;
 
import com.tistory.qlyh8.buildcontentprovider.data.TaskContract;
 
public class MainActivity extends AppCompatActivity implements
        android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor>{
 
    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int TASK_LOADER_ID = 0;
    private CustomCursorAdapter mAdapter;
 
    RecyclerView mRecyclerView;
    FloatingActionButton fabButton;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {...}
 
    // 일시중지되거나 재시작된 후에 메소드가 호출된다.
    // 보통 새로운 데이터가 AddTaskActivity 를 통해 삽입 된 후이다.
    // 이렇게하면 변경 사항에 대한 기본 데이터를 다시 쿼리하기 위해 로더가 다시 시작된다.
    @Override
    protected void onResume() {...}
 
    // 지정된 ID로 새로운 AsyncTaskLoader 를 인스턴스화하고 반환한다.
    // 이 로더는 작업 데이터를 커서 또는 오류가 발생하면 null 로 반환한다.
    // 로딩의 모든 단계에서 데이터 로딩을 처리하기 위해 필요한 콜백을 구현한다.
    @SuppressLint("StaticFieldLeak")
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new AsyncTaskLoader<Cursor>(this) {
 
            Cursor mTaskData = null;
 
            // 로더가 먼저 데이터를 로드할 때 호출된다.
            @Override
            protected void onStartLoading() {...}
 
            // 데이터의 비동기 로딩을 수행한다.
            @Override
            public Cursor loadInBackground() {
                try {
                    return getContentResolver().query(TaskContract.TaskEntry.CONTENT_URI,
                            nullnullnull,
                            TaskContract.TaskEntry.COLUMN_PRIORITY);
                } catch (Exception e){
                    Log.e(TAG, "Failed to asynchronously load data.");
                    e.printStackTrace();
                    return null;
                }
            }
 
            // 로드와 커서의 결과를 등록된 리스너에 보낸다.
            @Override
            public void deliverResult(Cursor data) {...}
        };
    }
 
    // 이전에 작성된 로더가 로드를 완료하면 호출된다.
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {...}
 
    // 이전에 작성된 로더가 재설정될 때 호출되어 해당 데이터를 사용할 수 없게 만든다.
    // 로더의 데이터에 대해 갖는 모든 참조를 제거한다.
    @Override
    public void onLoaderReset(Loader<Cursor> loader) {...}
}
 
cs





Android Course of Udacity - Lesson 9

+ Recent posts