ContentProvider 기능이 필요해서 가져다 사용 하려는데,
현재 검색되는 코드들은 대부분 되는 것들이 없어서 (코드 자체로는 전혀 문제가 없었지만 추가사항이 있어서..) 코드 공유 합니다.
기본적인 설명으로는 여기를
https://developer.android.com/guide/topics/providers/content-provider-creating.html?hl=ko
이외 코드 관련 부분은 아래에서 참고하였습니다.
참고URL : https://www.tutorialspoint.com/android/android_content_providers.htm
주의사항!
studio가 불안정한건지, 아님 저의 툴 설정이 문제인건지, 여러번 빌드시 이전 provider 설정들이 남아 있는건지
정상 동작하지 않던 문제가 있었습니다.
저같은 경우는 항상 A,B 앱을 uninstall 후에 빌드 시 제대로된 결과을 얻을 수 있었습니다.
(Permission Denial: opening provider com.app.first.myContentProvider from ..... 등등이 나온다면...)
1. A앱 작업 Mainfests (퍼미션을 추가합니다. 프로바이더 내에서도 readPermission과 writePermission , exprted를 명시합니다.)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.app.first"> <!-- 퍼미션 추가 --> <permission android:name="com.app.first.READ_DATABASE" android:protectionLevel="normal" /> <permission android:name="com.app.first.WRITE_DATABASE" android:protectionLevel="normal" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 프로바이더 등록 --> <provider android:name=".myContentProvider" android:authorities="com.app.first.myContentProvider" android:exported="true" android:readPermission="com.app.first.READ_DATABASE" android:writePermission="com.app.first.WRITE_DATABASE"/> </application> </manifest>
activity_main.xml (레이아웃 만들기)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" tools:context="com.app.first.MainActivity"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="데이터 불러오기" android:id="@+id/bt_renew"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_below="@id/bt_renew" android:textSize="15sp" android:text="Hello World!" android:id="@+id/tv_text"/> </RelativeLayout>
MainActivity.java (메인 화면 처리)
package com.app.first; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { DBHelper mDatabase; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDatabase = new DBHelper(getBaseContext()); // 데이터 입력 mDatabase.setDelete(); List<ItemRow> mList = new ArrayList<>(); mList.add(new ItemRow("식사","미역국",5000)); mList.add(new ItemRow("간식","우유",4000)); mList.add(new ItemRow("간식","바나나",3000)); mList.add(new ItemRow("식사","오이",2000)); mList.add(new ItemRow("식사","당근",1000)); for(ItemRow item : mList) { mDatabase.setItem(item.contents, item.name, item.num); } Button bt_renew = (Button) findViewById(R.id.bt_renew); bt_renew.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 데이터 불러오기 loadData(); } }); } private void loadData() { StringBuilder sb = new StringBuilder(); List<ItemRow> row = mDatabase.getItem(); sb.append("Total count : "+row.size()+"\n\n"); for(ItemRow item : row) { sb.append(item.contents+" , "+item.name+" , "+item.num+"\n"); } TextView tv_text = (TextView) findViewById(R.id.tv_text); tv_text.setText(sb.toString()); } @Override protected void onDestroy() { super.onDestroy(); if(mDatabase != null) mDatabase.close(); } } class ItemRow { public String contents; public String name; public int num; public ItemRow(String contents, String name, int num) { this.contents = contents; this.name = name; this.num = num; } }
DBHelper.java (디비 로직 작업)
package com.app.first; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; import java.util.ArrayList; import java.util.List; public class DBHelper extends SQLiteOpenHelper { private static final int db_version = 1; // Database Version private static final String DB_FILE_NAME = "UserData.db"; private static final String [] COLUMNS = {"contents TEXT", "name TEXT", "num INTEGER"}; public static String TABLE_NAME = "my_table"; public DBHelper(Context context) { // TODO http://stackoverflow.com/questions/4547461/closing-the-database-in-a-contentprovider super(context, DB_FILE_NAME, null, db_version); } @Override public void onCreate(SQLiteDatabase db) { String sql = "create table " + TABLE_NAME + " (" + BaseColumns._ID + " integer primary key autoincrement "; for (int i = 0; i < COLUMNS.length; i++) { sql += ", " + COLUMNS[i]; } sql += " ) "; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } public List<ItemRow> getItem() { List<ItemRow> list = new ArrayList<>(); try { beginTransaction(); Cursor c = getAll(TABLE_NAME); if (c != null) { int total = c.getCount(); if (total > 0) { c.moveToFirst(); while (!c.isAfterLast()) { String contents = c.getString(1); String name = c.getString(2); int num = c.getInt(3); list.add(new ItemRow(contents, name, num)); c.moveToNext(); } } c.close(); } } catch (SQLiteException e) { e.printStackTrace(); } finally { endTransaction(); } return list; } public void setItem(String contents, String name, int num) throws SQLiteException{ ContentValues values = new ContentValues(); values.put("contents", contents); values.put("name", name); values.put("num", num); insert(TABLE_NAME, values); } public void setDelete() { AllDelete(TABLE_NAME); } protected Cursor getAll(String tableName) throws SQLiteException { return getReadableDatabase().query(tableName, null, null, null, null, null, /*"date desc"*/null); } protected void beginTransaction() { getWritableDatabase().beginTransaction(); } protected void endTransaction() { getWritableDatabase().setTransactionSuccessful(); // db 속도 향상 getWritableDatabase().endTransaction(); } protected void insert(String tableName, ContentValues values) throws SQLiteException { getWritableDatabase().insert(tableName, null, values); } protected void AllDelete(String tableName) throws SQLiteException{ getWritableDatabase().delete(tableName, null, null); } }
myContentProvider.java (컨텐트프로바이더 내의 쿼리 처리)
package com.app.first; import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import java.util.HashMap; public class myContentProvider extends ContentProvider { private SQLiteDatabase mDatabase; static final String PROVIDER_NAME = "com.app.first.myContentProvider"; private static HashMap<String, String> STUDENTS_PROJECTION_MAP; static final int GET_ALL = 1; static final int INSERT = 2; static final int UPDATE = 3; static final int DELETE = 4; static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "getAll", GET_ALL); uriMatcher.addURI(PROVIDER_NAME, "insert", INSERT); } @Override public boolean onCreate() { DBHelper dbHelper = new DBHelper(getContext()); mDatabase = dbHelper.getWritableDatabase(); return (mDatabase == null)? false:true; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { Log.d("test","uri : "+uri); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables("my_table"); //switch (uriMatcher.match(uri)) { // case STUDENTS: // qb.setProjectionMap(STUDENTS_PROJECTION_MAP); // break; // case STUDENT_ID: // qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); // break; // default: // } qb.setProjectionMap(STUDENTS_PROJECTION_MAP); if (sortOrder == null || sortOrder == ""){ sortOrder = /*NAME*/"contents"; } Cursor c = qb.query(mDatabase, projection, selection, selectionArgs,null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } }
2. B앱 Mainfests (퍼미션 설정을 추가합니다.)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.app.second"> <!-- 퍼미션 설정 --> <uses-permission android:name="com.app.first.READ_DATABASE" /> <uses-permission android:name="com.app.first.WRITE_DATABASE" /> <!-- 퍼미션 설정 --> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.app.second.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:id="@+id/text" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
package com.app.second; import android.database.Cursor; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.TextView; public class MainActivity extends AppCompatActivity { String authority = "com.app.first.myContentProvider"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Cursor c = getContentResolver().query(Uri.parse("content://"+authority+"/getdataall"), null, null, null, null); if(c == null) return; Log.e("test","aaabbb "+c.getCount()); StringBuilder aa = new StringBuilder(); while(c.moveToNext()) { String str = c.getLong(0) + " , " + c.getString(1)+" , "+c.getString(2)+" , "+c.getInt(3); System.out.println(str); aa.append(str+"\n"); } c.close(); TextView text = (TextView) findViewById(R.id.text); text.setText(aa); } }
3. 결과 화면
A앱 B앱
'Android' 카테고리의 다른 글
[안드로이드] 갤럭시 S8(18.5:9), LG G6(18:9) 화면비 적용하기 (0) | 2017.06.09 |
---|---|
[안드로이드] android canvas triangle, star, heart 예제 (TouchEvent) (0) | 2017.05.22 |
[안드로이드] android keypad 내의 Done(enter,search,...) 눌렀을 경우 이벤트 (0) | 2017.05.14 |
[안드로이드] Android studio vector image 사용하기 (0) | 2017.05.12 |
[안드로이드] App cash data 앱 캐시 지우기 예제 (1) | 2017.05.01 |