6 - Content Provider
Android uygulamaları birbirlerinin verilerine doğrudan
ulaşamazlar. Bu, hem güvenlik hem de uygulamaların birbirinden
soyutlanabilmesini sağlar. Bir veriyi birden fazla uygulamada kullanabilmek
için "Content Provider" kullanılır.
https://www.tutorialspoint.com/android/android_content_providers.htm
A content provider component supplies
data from one application to others on request.( Bir
uygulamanın verilerine diğer uygulamalardan erişebilmek için content provider
kullanılmalıdır. Örneğin A uygulaması için bir content provider tanımladık
diyelim, bu content provider sayesinde A uygulamasının verilerine diğer
uygulamalardan erişebiliriz. )
Such requests are handled by the methods
of the ContentResolver class. (Diğer uygulamalar, A
uygulamasının content provider'ına istek yapmak için ContentResolver class'ını
kullanır. )
A content provider can use different
ways to store its data and the data can be stored in a database, in files, or
even over a network. ( A uygulaması için bir
content provider tanımlayacağız. Diğer uygulamalar A uygulamasının verilerine bu
content provider'ı kullanarak erişebilecekler. Bu content provider, A
uygulamasının verilerini saklamak, hafızada tutmak için farklı yollar
kullanabilir, A uygulamasının verileri bir veritabanında, dosyada veya bir
network üzerinde bir yerde tutuluyor olabilir. )
Sometimes it is required to share data
across applications. This is where content providers become very useful.( Content provider, uygulamalar arasında veri paylaşımını
sağlar. Örneğin,Rehber uygulaması ve WhatsApp uygulaması arasındaki veri
paylaşımın content provider sağlar. )
Content providers let you centralize
content in one place and have many different applications access it as needed. ( Content provider sayesinde bir uygulmanın verilerine
diğer uygulamalar'ın erişebilmesini sağlamış oluruz. )
A content provider behaves very much
like a database where you can query it, edit its content, as well as add or
delete content using insert(), update(), delete(), and query() methods. ( Bir content provider, bir veri tabanına benzer. Nasıl
ki bir veritabanına sorgulayabilirsek(select sorgusu atabilirsek), veritanının
içeriğini değiştirebilirsek, veritabanına yeni bir kayıt ekleyebilir veya
varolan bir kaydı silebilirsek, benzer şekilde aynı işlemleri content provider
ile de yapabiliriz.
Yani content
provider'a select sorgusu atarak bir uygulamanını verilerine erişebiliriz, content
provider'ı kullanarak bir uygulamadaki verilerin içeriğini değiştirebiliriz, content
provider'ı kullanarak bir uygulamanın verilerine yeni bir veri ekleyebiliriz
veya silebiliriz. )
In most cases this data is stored in an
SQlite database. ( Çoğu case'de, content provider'ı
kullanarak erişeceğimiz uygulama verisi SQLite veritabanında saklanır. )
A content provider is implemented as a
subclass of ContentProvider class and must implement a standard set of APIs
that enable other applications to perform transactions. ( Bir content provider, ContentProvider class'ının subclass'ıdır. )
public
class My Application extends
ContentProvider
{
}
( Varolan content provider class'ları bu şekilde tanımlıdır.
Kendi ContentProvider class'ımızı tanımlamak istersek de bu şekilde ContentProvider'ı
extend eden bir class tanımlarız. )
Content URIs
To query a content provider, you specify
the query string in the form of a URI which has following format: <prefix>://<authority>/<data_type>/<id>
( Bir content
provider'a sorgu atmak için URI sentax'ını yani şu sentax'ı kullanırız :
<prefix>://<authority>/<data_type>/<id> . URI syntax'ı
4 kısımdan oluşur. Şimdi bunları inceleyelim: )
prefix : This is always set to content:// (
1. kısım prefix'dir, bu kısıma genellikle content:// yazılır. )
authority : This specifies the name of the content provider, for example
contacts, browser etc. For third-party content providers, this could be the
fully qualified name, such as com.tutorialspoint.statusprovider. (2. kısma
authority denir. Authority kısmında sorgulamak istediğimiz content provider'ın
ismini yazarız. Örneğin sorgulamak istediğimiz content provider'ın ismi
şunlardan biri olabilir : contacts, browser etc. Bunlar telefonda default
olarak bulunan content provider'lardır. Third-party content provider'lar için,
mesela kendi content provider class'ımızı tanımladık diyelim bu content
provider'a sorgu atmak istiyorsak, URI syntax'ının authority kısmına bu content
provider class'ının fully qualified name'ini, örneğin com.tutorialspoint.statusprovider
yazmamız tavsiye edilmiştir.
Authority'ye kendi
tanımladığımız provider class'ının full path'ini vermemiz tavsiye edilir. Ancak
bu zorunlu değildir başka bir isim de verebiliriz authority'ye ancak bu isim
unique olmalıdır. Authority'nin unique olmasını sağlamak için şirketimizin
domain ismi + provider class ismini authority değeri olarka kullanabiliriz.
Manifest.xml dosyasında, <application> tag'ı altında <provider>
tag'ının android:authority attribute'üne authority değerini veririz. Ayrıca,
<provider> tag'ının android:name attribute'üne de tanımladığımız provider
class'ının full path'İni veririz. authority'yi content provider'ın mantıksal
ismi, dışarıdan görünen ismi gibi düşünebilirsin. URI'da geçen authority'nin
hangi provider class'ına map edeceğini işte manifest.xml dosyasına yazdığımız
provider element ile belirleriz. provider element'İnin authority attribute'üne istediğimiz
bir unique isim verebiliriz, başka uygulamalar bu unique ismi kullanarak bizim
content provider'ımıza erişirler. provider element'inin name attribute'üne ise provider
class'ının path'i karşılık gelir. )
data_type : This indicates the type of data that this particular provider
provides. For example, if you are getting all the contacts from the Contacts
content provider, then the data path would be people and URI would look like
this content://contacts/people ( Sorgulamak
istediğimiz content provider'daki hangi type'daki verileri sorgulamak
istediğimizi URI form'unun 3.kısmında yani data_type kısmında yazarız. Örnek :
Contacts isimli content provider'daki people type'ındaki tüm kayıtları
sorgulamak istiyorsak URI form'unu şöyle yazmalıyız : content://contacts/people
böylece rehberde kayıtlı tüm kayıtlar sorgulanır select * from contacts sorgusu
atmışız gibi düşün. )
id : This specifies the specific record requested. For example, if you
are looking for contact number 5 in the Contacts content provider then URI
would look like this content://contacts/people/5. ( Content
provider'daki belirli bir unique id'ye sahip olan bir kaydı getirmek,elde etmek
istiyorsak, URI form'unun 4.kısmında yani id kısmında unique id'yi yazarız.
Örnek : content://contacts/people/5 )
Create Content Provider
https://www.tutorialspoint.com/android/android_content_providers.htm
This involves number of simple steps to
create your own content provider. (Kendi content
provider class'ımızı tanımlamak istiyorsak, şu adımları izmeliyiz.)
First of all you need to create a
Content Provider class that extends the ContentProviderbaseclass. ( ContentProvider class'ının extend eden bir
class tanımlarız. )
Second, you need to define your content
provider URI address which will be used to access the content. (
Uygulamamızdaki verilere erişmek için kullanılacak olan content provider'ın URI
addres'ini tanımlarız. )
Next you will need to create your own
database to keep the content. Usually, Android uses SQLite database and
framework needs to override onCreate() method which will use SQLite Open Helper
method to create or open the provider's database. When your application is
launched, the onCreate() handler of each of its Content Providers is called on
the main application thread.( Uygulamamızdaki verileri saklamak, tutmak için kendi
veritabanımızı yaratırız bunun için genellikle SQLite database kullanılır. )
Next you will have to implement Content
Provider queries to perform different database specific operations.
Finally register your Content Provider
in your activity file using <provider> tag. (
Tanımladımız Content Provider class'ını <provider> tag'ını kullanarak
register etmeliyiz ama nasıl anlamadım? )
Here is the list of
methods which you need to override in Content Provider class to have your
Content Provider working: onCreate(), query(), insert(), delete(), update(),
getType() . Let's study these methods : ( Tanımladımız Content Provider class'ında şu method'ları
override etmeliyiz. Bu method'lar base method'umuz olan ContentProvider'dan
gelir, ContentProvider'ın subclass'larında override edilmelidirler :
onCreate(), query(), insert(), delete(), update(), getType(). Hadi Bu
method'ları inceleyelim. )
onCreate() : This method is called when the provider is
started.( Provider çalışmaya başladığında otomatik
olarak onCreate() method'u çalışır. )
query() : This method receives a request from a
client. The result is returned as a Cursor object.
( Bu method çağırılırsa, content provider'a bir select sorgusu atılır, böylece
varolan satırlar getirilir, elde edilir. )
insert() : This method inserts a new record into the
content provider. ( Bu method çağırılırsa, content
provider'a yeni bir record(row, satır) eklenir. )
delete() : This method deletes an existing record from
the content provider. ( Bu method çağırılırsa,
content provider'dan varolan bir record(row, satır) silinir. )
update() : This method updates an existing record from
the content provider. ( Bu method çağırılırsa,
content provider'daki varolan bir record(row, satır) güncellenir. )
getType() : This method returns the MIME type of the
data at the given URI. (?)
Accessing a
provider
https://developer.android.com/guide/topics/providers/content-provider-basics.html#ContractClasses
An application accesses
the data from a content provider with a ContentResolver client object. ( Bir uygulama bir ContentProvider'ı
contentResolver(diğer bir deyişle contentprovider'ın client'ı) aracılığıyla
çağırır. ContentResolver, ContentProvider'ın client'ıdır. )
This object has methods
that call identically-named methods in the provider object, an instance of one
of the concrete subclasses of ContentProvider. (
ContentResolverve ContentProvider class'larında aynı isimli method'lar vardır.
Örneğin hem ContentResolver hem de ContentProvider class'ında query() method'u
vardır. ContentResolver object'in bir method'unu örneğin query() method'unu
çağırdığımızda, kendi tanımladığımız ContentProvider object'in aynı isimli
method'u, ki bu örnekte query() method'u otomatik olarak çağırılmış olur. )
The ContentResolver
methods provide the basic "CRUD" (create, retrieve, update, and
delete) functions of persistent storage. (ContentResolver
class'ının method'larını çağırıp, content provider'daki aynı isimli method'u
otomatik olarak invoke etmiş oluruz, invoke edilen content provider method'u aracılığıyla
da veritabanına satır ekleyebilir, silebilir, güncelleyebilir, table
yaratabilir, varolan table'lara select sorgusu atabiliriz. )
The ContentResolver object
in the client application's process and the ContentProvider object in the
application that owns the provider automatically handle inter-process
communication.( Client uygulamanın process'inde
çalışan Content Resolver object ve provider'a sahip olan uygulamanın process'inde
çalışan ContentProvider object birbirleriyle haberleşirler, iletişim
halindedirler. Yani telefonumuzda 2 uygulama var diyelim, whatsapp ve rehber.
whatsapp, rehber uygulamasının verilerine erişebiliyor olsun. dolayısıyla
telefonumuzdaki whatsapp uygulamasında contentresolver object'in query()
method'unu çağırırsak, rehber uygulamamızdaki contentprovider class'ının
query() method'u invoke edilir otomatik olarak. 2 uygulama birbirleriyle
haberleşirler. )
ContentProvider also acts
as an abstraction layer between its repository of data and the external
appearance of data as tables.( Yukarıdaki örneği
düşünürsek, rehber uygulamamızın veritabanına diğer uygulamalar doğrudan
erişemezler. Diğer uygulamalar önce kendi içlerinde bir contentresolver object
yaratırlar. Sonra bu object aracılığıyla rehber uygulamasındaki contentprovider
class'ının method'ları invoke edilmiş olur. Yani diğer uygulamalar contentresolver
object'i kullanarak, bir uygulamanın contentprovider'ını tetikler, content
provider da bu uygulamanın veritabanına erişir. Yani content provider bir
abstraction layer yani soyut bir kavram gibi düşünülebilir, uygulamanın
veritabanına doğrudan erişilemez, ancak contentprovider'a istek gönderilir,
contentprovider uygulamanın veritabına erişip gelen isteğe cevap verir.
contentprovider kendisine iletilen talebi veritabanına gidip söyleyen bir
ulaktır. )
Note: To access a
provider, your application usually has to request specific permissions in its
manifest file. ( Bir provider'a erişmek isteyen
uygulamanın manifest dosyasında gerekli izinler belirtilmelidir. )
For example, to get a list
of the words and their locales from the UserDictionary Provider, you call
ContentResolver.query(). (Örneğin UserDictionary
isimli bir provider'a erişip ilgili uygulamanın veritabanına bir select sorgusu
atmak istiyoruz diyelim. Bunun için contentresolver object'in query method'unu
çağırmalıyız. contentResolver object'i elde etmek için getContentResolver()
method'u çağırılır. )
The query() method calls
the ContentProvider.query() method defined by the User Dictionary Provider. The
following lines of code show a ContentResolver.query() call: (contentresolver object'inin query() method'unu
çağırdığımızda, query() method'una verdiğimiz argument'de belirttiğimiz
contentProvider'ın yani UserDictionary content provider'ın query() method'u
çağırılır. Aşağıda content resolver object elde edilip bu object'in query()
method'u çağırılmıştır, bu method cursor object return eder. )
Explanation 2 :
http://androidexample.com/Content_Provider_Basic/index.php?view=article_discription&aid=120
ContentProvider
used to get data from central repository. Android application contains content
provider to provide data to other applications.( Bir Android
uygulaması content provider içeriyorsa, uygulamanın veritabanındaki verileri bu
content provider'ı kullanarak diğer uygulamalara sağlar, erişilebilir kılar. Yani
uygulamanın verilerini tutan veritabanına, dosyaya veya uzaktaki veritabanına
erişimi sağlar content provider.)
You can also create your custom content provider to get data from database / sdcard / media etc. ( Custom bir content provider tanımlayabiliriz, diğer bir deyişle kendi content provider'ımızı tanımlayabiliriz. Bu content provider, uygulamamızın verilerini diğer uygulamalara sağlayacaktır.)
Content providers create an abstraction layer between its repository of data and external application that are using data.( Uygulamamızın verilerinin tutulduğu repository olan veritabanı veya dosya veya uzaktaki veritabanı ile uygulamıza erişmek isteyen diğer uygulamalar arasında bir layer'dır content provider. )
External Application can call Content Provider methods with the use of ContentResolver.( External uygulamalar, yani diğer uygulamalar contentResolver class'ını kullanarak, bir uygulamanın content provider method'larını çağırırlar. contentResolver ve contentProvider class'larındaki method'ların isimlerinin aynı olduğunu hatırlayalım, yani contentResolver class'ının query method'unu çağırırsak, ilgili uygulamanın contentProvider class'ının query method'u çağırılmış olur. )
ContentResolver work as ContentProvider client object, with the use of Content Resolver object we can get data from Content Provider. (ContentResolver class'ını, ContentProvider object'in client'ı gibi düşünebiliriz. contentresolver object contentprovider object'i çağırır yani. )
ContentProvider and ContentResolver (provider clients) used together to create a interface for data to handles inter-process communication and access data in secure way.( Bir uygulamanın verilerine doğrudan erişmek güvenli olmadığı için contentProvider kullanarak bir uygulamanın verilerine erişmek yöntemi geliştirilmiştir bu güvenli bir yöntemdir. ContentProvider ve ContentProvider client kullanılarak iki uygulama birbiriyle haberleşebilir, diğer bir deyişle processler arasında iletişim kurulur. )
You can also create your custom content provider to get data from database / sdcard / media etc. ( Custom bir content provider tanımlayabiliriz, diğer bir deyişle kendi content provider'ımızı tanımlayabiliriz. Bu content provider, uygulamamızın verilerini diğer uygulamalara sağlayacaktır.)
Content providers create an abstraction layer between its repository of data and external application that are using data.( Uygulamamızın verilerinin tutulduğu repository olan veritabanı veya dosya veya uzaktaki veritabanı ile uygulamıza erişmek isteyen diğer uygulamalar arasında bir layer'dır content provider. )
External Application can call Content Provider methods with the use of ContentResolver.( External uygulamalar, yani diğer uygulamalar contentResolver class'ını kullanarak, bir uygulamanın content provider method'larını çağırırlar. contentResolver ve contentProvider class'larındaki method'ların isimlerinin aynı olduğunu hatırlayalım, yani contentResolver class'ının query method'unu çağırırsak, ilgili uygulamanın contentProvider class'ının query method'u çağırılmış olur. )
ContentResolver work as ContentProvider client object, with the use of Content Resolver object we can get data from Content Provider. (ContentResolver class'ını, ContentProvider object'in client'ı gibi düşünebiliriz. contentresolver object contentprovider object'i çağırır yani. )
ContentProvider and ContentResolver (provider clients) used together to create a interface for data to handles inter-process communication and access data in secure way.( Bir uygulamanın verilerine doğrudan erişmek güvenli olmadığı için contentProvider kullanarak bir uygulamanın verilerine erişmek yöntemi geliştirilmiştir bu güvenli bir yöntemdir. ContentProvider ve ContentProvider client kullanılarak iki uygulama birbiriyle haberleşebilir, diğer bir deyişle processler arasında iletişim kurulur. )
Content provider
show data to content resolver as one or many tables that will show same as
relational database.(?)
Android provide number of content providers that store common data such as contact informations, calendar information, and media files etc.(?)
Content Provider is the mechanism which is used to expose many of a device's data resources for retrieval and update: Contacts, media store, bookmarks, phone-call log, and so on. It’s hard to find an interesting Android app that doesn’t either use or implement (or both) a Content Provider.( ContentProvider mekanizması sayesinde, cihazımızın uygulamalarındaki birçok veriyi kullanabiliriz, örneğin rehber ve galeri uygulamasındaki verilere erişebilir bu verileri değiştirebiliriz. Bookmark'lara, geçmiş arama kayıtlarına erişebilir bu verileri değiştirebiliriz. Android'deki tüm kaliteli uygulamalar content provider kullanırlar veya kendi content provider'larını implement ederler, veya her ikisini de yaparlar. )
You
address Content Providers by Url, query them with SQL, and iterate them with a
Cursor. But there’s a common anti-pattern, a way to misuse them that can
potentially get your app into trouble, and maybe we’ve made it a little too
easy. ( ContentProvider'a bir url ile erişiriz, SQL ile
sorgularız, elde ettiğimiz cursor object'in üzerinde iterate ederek veriler
üzerinde gezeriz. )
The
Content Providers that the Android framework gives you are described in the
SDK’s android.provider package summary. For many of them, the framework
provides helper classes of one sort or another, to help automate common chores
and provide symbolic names for useful constants. ( Android
framework'ün bize default olarak sağladığı content provider'lar nelerdir?
android.provider isimli package altındadır bunlar, listesi şuradadır : https://developer.android.com/reference/android/provider/package-summary.html
. Bize sağlanan bu content provider'lar
için, android framework bize helper class'lar sağlamıştır. )
The
problem is, there are more Content Providers in the system than are documented
in that package, and while you can use them, you probably shouldn’t. They’re
there because some of the Google-provided apps use them internally to access their
own data resources. Because Android is an open-source project, it’s easy enough
to find them just by running shell commands like find and grep over the source
tree.( Problem şudur, android.provider package'ından dökümante
edilenden daha fazla content provider vardır aslında. Dökümante edilmeyen
content provider'ları da kullanabiliriz ancak kullanmasak daha iyi olur çünkü
bunlar default olarak gelen bazı uygulamalar tarafından kullanılırlar. Android
open-source bir project olduğu için, find ve grep gibi shell komutlarını source
tree üzerinde çalıştırarak dökümanda olan veya olmayan tüm content
provider'ları görebiliriz. )
(By
the way, searching the source tree like this is an excellent idea, something
that probably every serious developer does regularly. Not 100% sure of the best
way to write code to display a record from the Contacts database? Go ahead,
have a look at how the built-in app does it; even better, steal some code; it’s
perfectly legal.) ( Search tree üzerinde bu şekilde
arama yapmak müthiş bir fikirdir ve tüm tecrübeli android yazılım
geliştiriciler bunu sıklıkla yaparlar. Örneğin, Contacts veritabanındaki
verilere erişmek için Contacts contentProvider'ı kullanmak istiyoruz, bu sayede
rehber uygulamasındaki bir kişinin kaydına erişip bunu göstermek istiyoruz
diyelim. Ancak bunu nasıl yapacağımızdan emin olmadığımızı varsayalım. Bu
durumda built-in uygulamaların bunu nasıl implement ettiklerine göz atabiliriz
ve bunu kendi kodumuzda kullanabiliriz bu son derece yasaldır. )
Back
to Content Providers. For example, there’s one inside the built-in Messaging
(A.K.A. texting or SMS) app that it uses to display and search your history.
Just because it’s there doesn’t mean you should use it. The Android team isn’t
promising that it’ll be the same in the next release or even that it’ll be
there in the next release.( Örneğin built-in SMS uygulamasından
rehber uygulamasının verilerine erişebiliriz. Built-in sms uygulamasının
kodlarını source tree'den bulup uygun kodu alıp kendi kodumuzda kullanabiliriz.
)
It’s
worse than that; someone could ship an Android device based on the current SDK
that follows all the rules but has its own enhanced messaging application that
doesn’t happen to have such a Content Provider. Your app will break on such a
device and it’ll be your fault.( Ancak şöyle bir kötü senaryo da
mümkündür. Son çıkan SDK'de rehber uygulamasının content provider'ı değişti
diyelim, bu durumda bizim uygulamamız da patlar çünkü uygulamamızda
kullandığımız content provider güncel SDK'e sahip bir telefonda var olmadığı
için bu telefonda bizim uygulamamız patlar.
)
So, go ahead and
look at the undocumented Content Providers; the code is full of good ideas to
learn from. But don’t use them. And if you do, when bad things happen you’re
pretty well on your own. ( Hadi dökümante edilmeyen
contentProvider'lara gidip bir göz atın. Bu kodlardan çok iyi fikirler
öğrenebiliriz, ancak bu fikirlerin hepsini uygulamamalıyız bunun sonuçları kötü
olursa bu bizim sorumluluğumuz olur. )
Diğer
uygulamalar contentResolver'ın aşağıda gösterilen method'larından birini
çağırırlarsa, ilgili uygulamanın aynı isimli method'u otomatik olarak
invoke edilmiş olur. Content Provider, bir uygulamanın veri repository'si
ve diğer uygulamalar arasında bir layer'dır gibi düşünülebilir.
|
ContentResolver ( Client object to Access
ContentProvider )
An application gets
data from a content provider with a ContentResolver client object.( Bir uygulama content resolver object'i kullanarak bir content
provider'dan veri çeker.)
The ContentResolver provides the basic create, retrieve, update, and delete functions. ( ContentResolver object,CRUD işlemlerini gerçekleştiren fonksiyonlara sahiptir. )
When application calls ContentResolver method then ContentProvider's identically-named method is called in. ( Diğer uygulamalardan biri content resolver object'in bir method'Unu çağırdığında, bizim uygulamamızın content provider object'inin aynı isimli method'u çağırılır. )
Example :
If you call getContentResolver().query() method then ContentProvider's query() method called. ( Content resolver object'in query() method'unu çağırırsak, ilgili content provider object'in query() method'u çağırılır. )
If you call getContentResolver().insert() method then ContentProvider's insert() method called. ( Content resolver object'in insert() method'unu çağırırsak, ilgili content provider object'in insert () method'u çağırılır. )
The ContentResolver provides the basic create, retrieve, update, and delete functions. ( ContentResolver object,CRUD işlemlerini gerçekleştiren fonksiyonlara sahiptir. )
When application calls ContentResolver method then ContentProvider's identically-named method is called in. ( Diğer uygulamalardan biri content resolver object'in bir method'Unu çağırdığında, bizim uygulamamızın content provider object'inin aynı isimli method'u çağırılır. )
Example :
If you call getContentResolver().query() method then ContentProvider's query() method called. ( Content resolver object'in query() method'unu çağırırsak, ilgili content provider object'in query() method'u çağırılır. )
If you call getContentResolver().insert() method then ContentProvider's insert() method called. ( Content resolver object'in insert() method'unu çağırırsak, ilgili content provider object'in insert () method'u çağırılır. )
Aşağıdaki kodu inceleyelim. Bu kod activity subclass'ı içerisinde
çağırılır, tanımladığımız contentProvider subclass'ı içerisinde değil!!! Dikkat
et sakın karıştırma !!
getContentResolver().query(...) activity subclass'ında çağırılır, örneğin bir
butona basıldığında x method'u çağırılacaksa bu x method'u activity
subclass'ında tanımlanacaktır, bu x method'unun içerisinde
getContentResolver().query(...) diyerek,
contentResolver object'in query() method'u çağırılır, böylece db manipulation
işlemleri yapılır.
// Queries the user dictionary
and returns results mCursor =
getContentResolver().query( CONTENT_URI,
// The content URI to access table mProjection,
// The columns to return for each
row mSelectionClause
// Selection criteria mSelectionArgs,
// Selection criteria mSortOrder);
// The sort order for the returned rows |
SQL query yazmak konusunda usta olan Android yazılımcılar
genellikle contentresolver object'İn query() method'una argument olarak sql
query yazılır.
ContentResolver.query()
vs. Activity.managedQuery
:managedQuery()
will use ContentResolver's query(). The difference is that with managedQuery()
the activity will keep a reference to your Cursor and close it
whenever needed (in onDestroy()
for instance.) If you do query()
yourself, you will have to manage the Cursor as a sensitive resource. If you
forget, for instance, to close()
it in onDestroy()
, you will leak underlying resources (logcat will warn you about
it.) ( getContentResolver().query() method'Unu çağırmak yerine
managedQuery() method'unu da çağırabiliriz. Peki getContentResolver().query()
mi çağırmalıyız managedQuery() mi çağırmalıyız? Aslında managedQuery(),
ContentResolver object'in query() method'unu çağırır. Aralarındaki fark ise
şudur :
- getContentResolver.query() method'unu kullanırsak, elde
ettiğimiz Cursor object'i daha titiz ve hassas kullanmalıyız, Cursor object'ini
activity'mizin onDestroy() method'unda kapatmayı unutmamalıyız aksi takdirde
memory leak olur.
- managedQuery() method'unu kullanırsak, activity'miz Cursor'ı
yönetme işini kendisi üstlenecektir, bizim Cursor object'i manual olarak
kapatmamıza gerek kalmayacaktır, cursor object otomatik olarak kapatılacaktır
doğru zamanda. )
To query a content
provider, you can use either the
ContentResolver.query()
method or the Activity.managedQuery()
method. Both methods take the same set of arguments, and both
return a Cursor object. However, managedQuery()
causes the activity to manage the life cycle of the Cursor. A
managed Cursor handles all of the niceties, such as unloading itself when the
activity pauses, and requerying itself when the activity restarts. You can ask
an Activity to begin managing an unmanaged Cursor object for you by calling Activity. startManagingCursor()
. ( Content provider'ı sorgulamak için ContentResolver.query()
method'unu da kullanabiliriz, Activity.managedQuery()
method'unu da kullanabiliriz.
Her 2 method da aynı parametreleri alır, ve her ikisi de bir Cursor object
return eder.managedQuery() method'unu kullanırsak, cursor object'in açılması
kapatılması vs. gibi tüm yönetim işleri otomatik olarak yapılacaktır bizim
manuel olarak cursor object'i manipule etmemize gerek kalmayacaktır. )
Update:
managedQuery
is now deprecated (as of Android 3.0).
getContentResolver().insert(URI,
contentValues)
vs
getContentResolver().notifyChange(URI,
null)
In an Activity we can call getContentResolver().insert(URI,
contentValues); via a button click.
In our implementation of
ContentProvider at the end of the insert() method, we call getContentResolver().notifyChange(URI, null);
( Yani şunu
karıştırma, getContentResolver().insert() veya getContentResolver().query() kodunu activity class'ında çağırırız, örneğin bir butona
basıldığında çağırılmasını istediğimiz method'un
içerisinde çağırabiliriz getContentResolver().insert() ve getContentResolver().query() method'larını.
Ancak tanımladığımız
contentProvider class'ında ise getContentResolver().notifyChange(URI, null); method'unu çağırırız. Bu 2
method'un çağırıldıkları yerleri unutma, Activity class'ında getContentResolver().query() çağırılır. Custom ContentProvider class'ında ise getContentResolver().notifyChange(URI, null); çağırılır.)
Example
:
Custom bir
ContentProvider tanımlayıp bunu kodumuzda kullanmak istiyorsak çok fazla teknik
detay bilmemiz gerekir. Şimdi bunları adım adım inceleyelim.
Step 1: ContentProvider class skeleton
public
class BirthProvider extends ContentProvider {
private
SQLiteDatabase db;
DBHelper dbHelper;
@Override
public boolean onCreate() {
Context context = getContext();
dbHelper
= new DBHelper(context);
db = dbHelper.getWritableDatabase();
if(db == null)
return false;
else
return true;
}
// DBHelper class creates and
manages the provider's database.
// Provider'ın veritabanını yönetmek
için DBHelper class'ını tanımlarız.
private
static class DBHelper extends SQLiteOpenHelper
{
public DBHelper(Context
context) {
super(context,
DATABASE_NAME, null, DATABASE_VERSION);
}
...
}
}
ContentProvider'ı
extend eden BirthProvider isimli bir class tanımladık. ContentProvider'ın
subclass'ında, SQliteOpenHelper class'ını
extend eden DBHelper isimli bir
static inner class tanımladık. Ayrıca ContentProvider subclass'ının scope'unda,
SQLiteOpenHelper object'tine
refer eden dbHelper isimli bir
reference variable tanımladık.
ContentProvider
subclass'ının onCreate() method'unda, SQLiteOpenHelper
subclass'ından bir object yaratırız : dbHelper = new DBHelper(context); DBHelper contructor'ına argument olarak context object verdiğimize
dikkat et. DBHelper class'ının
context object alan constructor'ında super(context,
DATABASE_NAME, null, DATABASE_VERSION); diyerek superclass'ın yani SQLiteOpenHelper class'ın
constructor'ı çağırılır.
ContentProvider'ın
subclass'ının onCreate() method'unda, SQLiteOpenHelper subclass'ının object'inin,
yani dbHelper'ın refer ettiği object'in getWritableDatabase() method'u
çağırılır, getWritableDatabase() method'u SQLiteDatabase object
return eder. ContentProvider subclass'ının scope'unda, SQLiteDatabase object'tine refer eden db
isimli bir reference variable tanımladık. Veritabanına başarılı bir şekilde
erişim sağladıysak, onCreate() method'u true retuırn eder, aksi takdirde false
return eder. SQLiteOpenHelper class'ının getWritableDatabase() method'unu
burada çağırmak zorunda değiliz. İstersek bunu provider'ın diğer method'ları
içerisinde de çağırabiliriz, ancak onCreate() method'unda çağırmak daha
mantıklıdır bence.
Step 2: ContentProvider class and SQLiteOpenHelper
public
class BirthProvider extends ContentProvider
{
private SQLiteDatabase db;
DBHelper dbHelper;
static final
String DATABASE_NAME =
"BirthdayReminder";
static final
int DATABASE_VERSION = 1;
@Override
public boolean onCreate() {
Context context = getContext();
dbHelper
= new DBHelper(context);
db = dbHelper.getWritableDatabase();
if(db
== null) return false;
else return
true;
}
private
static class DBHelper extends SQLiteOpenHelper
{
public DBHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
}
ContentProvider
class'ımızın scope'unda, kullanacağımız veritabanı ismini ve versiyonunu da
tanımlarız. ContentProvider class'ının
içinde tanımlanan SQLiteOpenHelper inner class'ının constructor'ında,
superclass'ın constructor'ını invoke ederken ContentProvider scope'unda
tanımlanan veritabanı ismi ve veritabanı versiyonunu da contructor'a argument
olarak veririz.
Step 3 : SQLiteQueryBuilder
ContentProvider class'ımızın query()
method'unu inceleyelim. query() method'u Cursor object return eder. query()
method'unda önce bir SqliteQueryBuilder object yaratırız. SqliteQueryBuilder
class'ını kullanarak complex query'ler yazabiliriz. Özellikle birden fazla
table içeren bir query yazmak için(mesela 2 table'ı join etmek için)
SqliteQueryBuilder class'ı kullanmak işimizi kolaylaştırır.
Aşağıdaki örnekte, tbl_books ve tbl_authors table'larını join ederek
kitapların ve yazarların bir listesini oluşturmak için SqliteQueryBuilder
class'ını kullanacağız :
2 farklı table içeren bir
sql query'si yazmak istediğimizi düşünelim, örneğin select * from tbl_books,
tbl_authors yazmak istiyoruz diyelim. Bunun için SqliteQueryBuilder object'in setTables method'unu çağırırız, bu
method'a argument olarak table isimlerini veririz : queryBuilder.setTables(“tbl_books, tbl_authors”);
Birden fazla table
kullanılacağı zaman, bir sütuna tableismi.sütunismi diyerek refer ederiz.
Örneğin tbl_books isimli table'ın title isimli sütununa erişmek için
tbl_books.title deriz. Yazmak istediğimiz bir query'nin where clause'unu
belirlemek için, SqliteQueryBuilder object'in appendWhere method'unu çağırırız : queryBuilder.appendWhere(“tbl_books.authorid tbl_authors.id”);
Yukarıdaki setTables() ve appendWhere() method'larını
çağırınca, şöyle bir query'ye sahip oluruz : select * from tbl_books,
tbl_authors where tbl_books.authorid tbl_authors.id
Peki select ? from ... where ... sql
clause'undaki ? soru işaretli kısmı nasıl ayarlarız. Bunun için bir string
array tanımlarız. Hangi table'larıın hangi sütunlarını listede göstermek
istiyorsak, yani ? işaretli kısma hangi table'ın hangi sütununu yazmak
istiyorsak, tanımladığımız string array'e eleman olarak koyarız.
String asColumnsToReturn[]
{ “tbl_books.title”, “tbl_books.id”, “tbl_authors.firstname”, “tbl_authors.lastname”,
“tbl_books.authorid” }; SqliteQueryBuilder
object'in query() method'unun 2. parametresine bu array'i veririz :
Cursor
c = queryBuilder.query(mDatabase, asColumnsToReturn,
null,
null, null, null,strSortOrder);
Peki select .. from ... where ... order by ? sql
statement'daki ? soru işaretli kısmı nasıl belirleriz. SqliteQueryBuilder
object'in query() method'unun sonuncu parametresine vereceğimiz string ? yerine
koyulur :
String strSortOrder = "title ASC";
Cursor c queryBuilder.query(mDatabase,
asColumnsToReturn, null, null, null, null,strSortOrder);
TÜm
bunlardan sonra şöyle bir sql query elde etmiş oluruz :
SELECT tbl_books.title, tbl_books.id, tbl_authors.firstname,
tbl_authors.lastname, tbl_books.authorid
FROM tbl_books
INNER JOIN tbl_authors on tbl_books.authorid = tbl_authors.id
ORDER BY title ASC;
Peki select .. from tbl_books
AS A, tbl_authors AS B where ... order by .. sql statement'daki mavi kısmı nasıl
belirleriz. Yani istediğimiz table'ların istediğimiz sütunlarını nasıl
isimlendiririz? SqliteQueryBuilder object'in setProjectionMap()
method'unu çağırarak. setProjectionMap()
method'una argument olarak bir HashMap object verilir:
ContentProvider class'ı
içerisinde :
private final static HashMap<String,
String> BirthMap;
static {
BirthMap =
new HashMap<String, String>();
sDictProjectionMap.put(MAIN_COLUMN_ID, MAIN_COLUMN_ID_NEW);
sDictProjectionMap.put(MAIN_COLUMN_WORD1,
MAIN_COLUMN_WORD1_NEW);
sDictProjectionMap.put(MAIN_COLUMN_WORD2, MAIN_COLUMN_WORD2);
sDictProjectionMap.put(MAIN_COLUMN_LOCALE, MAIN_COLUMN_LOCALE);
}
queryBuilder.setProjectionMap(BirthMap);
BirthMap'e eleman ekleme işini static scope'da yaptığımıza dikkat et.
Burası önemlidir.
queryBuilder.query() method'unda yapmamız gereken bir şey yoktur.
Örneğin, books isimli table'daki pagenumber
sütununun ismini A yapmak istiyorum
diyelim. Bunu şu sql komutunu
çalıştırarak yapabiliriz : SELECT books.pagenumber AS A
Bunu SqliteQueryBuilder class'ını
kullanarak nasıl yapabiliriz peki? ContentProvider class'ı içerisinde static
bir HashMap<String, String> object tanımlarız. Sonra
HashMap object'in put() method'unu çağırarak HashMap collection'a eleman
koyarız. put(), method'unun aldığı ilk argument yeni sütun ismi olacaktır,
2.argument ise current sütun ismidir.
HashMap<String, String> gProjectionMap= new HashMap<String,
String>();
gProjectionMap.add("A","
books.pagenumber")
SqliteQueryBuilder qb = new SqliteQueryBuilder ();
qb.setTables("apple");
qb.setProjection(gProjectionMap);
// The result:
s == "SELECT apple.orange AS
brumble FROM apple WHERE color = ?";
For example you could map "name" to "people.name". Yani people
table'ındaki name sütununa refer
etmek için name diyebiliriz sadece.
Şu gibi : select name as people.name from some_table. Here, name maps to people.name .
If a projection map is set it must
contain all column names the user may request, even if the key and value are
the same. ( Eğer setProjectionMap() method'u
kullanılarak bir projection map set edilecekse, bu projection map kullanıcının
istek gönderebileceği tüm sütun isimlerini içermek zorundadır. Key ve value aynı olsa bile! Çoğu örnekte, çoğu
satır için key ve value'nün aynı olması işte bu yüzdendir. )
setProjectionMap() method'una çok sık ihtiyacım
olacağını sanmıyorum ancak neredeyse tüm contentprovider class'larında bu
method kullanılmış. Hatta sütunları yeniden isimlendirmeseler bile bunu
kullanmışlar. Yukarıda da aynı sütunlara aynı isimleri verdik, bunu yazmak da yazmamak
da aynı sonucu verir.
Alternatif olarak, SQLiteQqueryBuilder
object'in setProjectionMap() method'unu kullanmak yerine, select ? from .. sql statement'daki ? soru
işaretli yere koymak istediğimiz şeyi queryBuilder.query() method'unun
2.parametresine veririz. Örnek :
projection = new String[]
{
TABLE_NAME + "._id" AS "
+ TABLE_NAME + "_id",
TABLE_NAME + "." +
"name" + " AS " + TABLE_NAME + "_" +
"name"
};
Cursor cursor = queryBuilder.query( .. ,
projection, .. , .. , .. , .. , .. );
SQLiteQueryBuilder class'ını tanımlayı
bitirdik. artık 3.adımı incelemeye kaldığımız yerden devam edebiliriz :
switch block'u inceleyelim. switch
block'da, UriMatcher class'ının static bir method'u olan match() method'u
çağırılır, match method'unua argument olarak query method'unun aldığı argument
olan URI object verilir. Buna göre bir işlem yapılır. Ayrıntıları bir sonraki
adımda inceleyeceğiz.
Step 4 : UriMatcher
ContentProvider
class'ında bir URI
object yaratılır. Bu URI object'in type'ını yani list mi item mı type'ında
olduğunu bulmak için UriMatcher
class'ı kullanılır. URI object'in nasıl
yaratıldığına dikkat edelim. Önce content url'i string olarak yazarız, sonra bu
string Uri class'ının static method'u olan parse() method'una verilir, bu
method URI object return eder.
static
final String PROVIDER_NAME = "com.javacodegeeks.provider.BirthdayProv";
static
final String URL = "content://" + PROVIDER_NAME +
"/friends";
static
final Uri CONTENT_URI = Uri.parse(URL);
The
UriMatcher is used to determine the URI
type: list or item. ContentProvider class'ında
yarattığımız URI object'in type'ını yani liste mi item mı type'ında olduğunu
bulmak için UriMatcher
class'ı kullanılır. ContentProvider class'ımızda
bir UriMatcher object yaratırız.
UriMatcher object'e refer eden variable static olduğu için ya declare edilirken
set edilmek zorundadır, ya da declare edildikten sonra static bir scope'da
declare edilmek zorundadır.
uriMatcher.addURI(PROVIDER_NAME,
"friends",
FRIENDS) : Defines the
list type. Any URI that uses the
com.javacodegeeks.provider.BirthdayProv authority and has a path named "friends"
returns the value FRIENDS.
uriMatcher.addURI(PROVIDER_NAME,
"friends/#",
FRIENDS_ID) : Defines the item type.
Any URI that uses the com.javacodegeeks.provider.BirthdayProv authority and has
a path that looks like friends/# (where # is a number) returns the value FRIENDS_ID.
// integer values used in content URI
static final int FRIENDS = 1;
static final int FRIENDS_ID = 2;
// maps content URI "patterns" to the
integer values that were set above
static
final UriMatcher uriMatcher;
static
{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "friends", FRIENDS);
uriMatcher.addURI(PROVIDER_NAME, "friends/#",
FRIENDS_ID);
}
addURI
method'unun aldığı 1.parametre authority'dir,2. parametre path'dir. Bu
authority ve path'e sahip olan uri object FRIENDS_ID'ye yani 2'ye map eder.
Using
a content provider involves calling its data operations with REST-style URIs defined
by the UriMatcher
class.
UriMatcher
provides
a simple string matching utility for REST-based URLs, with support for
wild-carding strings. ( ContentProvider kullanmak,
REST-style URI object'ler ile veri işlemleri gerçekleştirmeyi içerir. UriMatcher class'ı, REST tabanlı URL'ler için bir string
matching(karşılaştırma) yapmayı sağlar. )
Content
provider URLs always take the following form:
content://authority/path/id where
authority is the Java package of the
content provider namespace (often the Java namespace of the content provider
implementation). Here are some example content provider URIs: (Content provider URL'leri, genellikle şu formdadır : content://authority/path/id . Buradaki authority kısmı genellikle content provider
class'ının full path'idir, bu zorunlu değildir ancak convention yani gelenek
böyledir. Bazı örnek content provider URI'lar şöyledir. )
// references a person
content://contacts/people/25
// this URI designates the
phone numbers of the person whose ID is "25"
content://contacts/people/25/phones
Content URIs can represent either of two forms:
content://content_provider'in_path'i/items -> This URI represents a
request for all values of that type (e.g., all items). (select
* from items gibi düşün. )
content://content_provider'in_path'i/items/5 -> Appending a trailing
/<rownumber>, as shown here, represents a request for a single record
(e.g., “the fifth item”). (select * from items
where id=5 gibi düşün.)
It’s good form to support
access to your provider using both these forms. (
Tanımladığınız ContentProvider class'ınızda iki farklı content provider
type'ını(list ve item) da düşünerek kod yazmanız tavsiye edilir. )
Örnek : ContentProvider
class tanımlayalım. Content Provider URI form'unu string olarak yazarız.
A URI ending in ‘items’ will correspond to a request for all items, and
‘items/[rowID] represents a single row. (Sonu
"items" ile biten bir URI tüm item'lar için bir request'dir. Sonu
"items/rowID" ile biten bir URI specific bir item için bir
request'dir. ) :
private
static final String myURI = “content://com.paad.provider.myapp/items”;
Uri.parse() method'Unu çağırıp Uri object'i elde ederiz:
public static final Uri CONTENT_URI = Uri.parse(myURI);
Tüm
satırlar için yapılacak request'e karşılık gelecek olan ve specific bir satıra
yapılacak bir request'e karşılık gelecek olan 2 tane int variable tanımlarız,
bu variable'lar private static final olmalıdır:
private static final int ALLROWS = 1;
private static
final int SINGLE_ROW = 2;
UriMatcher
type'ında bir reference variable tanımlarız. Sonra UriMatcher object yaratırız.
Sonra UriMatcher object'in addURI() method'unu çağırarak UriMatcher object'e
elemanlar ekleriz, böylece com.paad.provider.myApp/items uri'a request gelirse
bunun 1'e map etmesini, com.paad.provider.myApp/items/# uri'a request gelirse
2'ye map etmesini sağlarız.
public class MyProvider extends ContentProvider
{
private static final
String myURI = "content://com.paad.provider.myapp/items";
public static final
Uri CONTENT_URI = Uri.parse(myURI);
private static
final int ALLROWS = 1;
private static
final int SINGLE_ROW = 2;
private static
final UriMatcher uriMatcher;
// Populate the UriMatcher object, where
a URI ending in ‘items’ will
// correspond to a request for all
items, and ‘items/[rowID]’
// represents a single row.
static {
uriMatcher
= new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(“com.paad.provider.myApp”,
“items”, ALLROWS);
uriMatcher.addURI("com.paad.provider.myApp",
"items/#", SINGLE_ROW);
}
}
You can use the same technique to expose
alternative URIs for different subsets of data, or different tables within your
database from within the same Content Provider. (
Aynı ContentProvider'ın içerisindeki diğer table'ları da uriMatcher object'e
yukarıdaki şekilde ekleyebiliriz. Farklı URI'lar kullanarak farklı veri altkümelerini
(mesela id'si 25 olan kişinin/kişilerini phone sütunlarını) de yukarıda şekilde
urimatcher object'e ekleyebiliriz. )
- Bu URIMatcher object'i, ContentProvider class'ının query()
method'unda şu şekilde kullanabiliriz:
@Override
public
Cursor query(Uri uri, String[] projection, String selection, String[]
selectionArgs, String sortOrder)
{
...
switch (uriMatcher.match(uri)) {
// maps all database column names
case ALLROWS:
...
case SINGLE_ROW:
...
default:
throw new
IllegalArgumentException("Unknown URI " + uri);
}
....
}
It’s also good practice to expose the
names and indexes of the columns available in your provider to simplify
extracting information from Cursor results.
Example
:
public static final String AUTHORITY
= "com.androidbook.TrackPointProvider"
private static final int TRACKPOINTS = 1;
private static final int TRACKPOINT_ID = 10;
private static final UriMatcher
sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY,
“points”, TRACKPOINTS);
sURIMatcher.addURI(AUTHORITY,
“points/#”, TRACKPOINT_ID);
}
- First,
arbitrary numeric values are defined to identify each different pattern:
private
static final int TRACKPOINTS = 1;
private
static final int TRACKPOINT_ID = 10;
- Next, a static UriMatcher instance is created
for use:
private static final UriMatcher
sURIMatcher = new
UriMatcher(UriMatcher.NO_MATCH);
- Next,
the URI values are added to the matcher with their corresponding identifiers:
static {
sURIMatcher.addURI(AUTHORITY,
“points”, TRACKPOINTS);
sURIMatcher.addURI(AUTHORITY,
“points/#”, TRACKPOINT_ID);
}
-
Bu URIMatcher object'i, ContentProvider class'ının query() method'unda şu
şekilde kullanabiliriz:
@Override
public
Cursor query(Uri uri, String[] projection, String selection, String[]
selectionArgs, String sortOrder)
{
...
switch (uriMatcher.match(uri)) {
// maps all database column names
case TRACKPOINTS:
...
case TRACKPOINTS_ID:
...
default:
throw new
IllegalArgumentException("Unknown URI " + uri);
}
....
}
Step 5 :
Cursor.setNotificationUri(),Cursor.getNotificationUri()
İncelediğimiz
BirthProvider.java class'ının query() method'undaki her şeyi inceledik
öğrendik, sadece cursor.setNotificationUri() method'u kaldı öğrenmediğimiz.
Şimdi de bu method'u öğrenelim hadi!!!!!
Cursor class'ı, şu 2 method'u içerir:
setNotificationUri (ContentResolver cr, Uri uri)
Register
to watch a content URI for changes. This can be the URI of a specific data row
(for example, "content://my_provider_type/23"), or a a generic URI
for a content type.
Uri
getNotificationUri()
Return the URI at which notifications of changes in
this Cursor's data will be delivered, as previously set by
setNotificationUri(ContentResolver, Uri)
.
----
@Override
public Cursor query(Uri uri, String[] projection, String
selection, String[] selectionArgs, String sortOrder)
{
SQLiteQueryBuilder
queryBuilder = new SQLiteQueryBuilder();
...
Cursor cursor =
queryBuilder.query(database, projection, selection, selectionArgs, null, null,
sortOrder);
// register to watch a content
URI for changes
cursor.setNotificationUri(getContext().getContentResolver(),
uri);
return cursor;
}
Inside
the
contentProvider
's insert
, update
, delete
methods, you need to call getContext().getContentResolver().notifyChange(uri,
null);
to notify change to the uri
observers. ( contentProvider
class'ının insert(), update() delete() method'larında şu contentResolver
object'in notifyChange method'u çağırılır :getContext().getContentResolver().notifyChange(uri,
null);
insert(),update(),delete()
method'ları çağırıldığında veritabanında bir değişiklik olur, ve
getContext().getContentResolver.notifyChange(uri,null) method'u şu amaçla
çağırılır: notifyChange method'unun aldığı 1. argument olan uri object'i
observe eden kişilere bir değişiklik olduğu haberi verilir. )
So if you don't call
cursor#setNotificationUri()
, your CursorLoader
will not receive notification if data underlying that uri
changes. ( contentProvider
class'ının query() method'unda SQLiteQueryBuilder object'in query() method'u
çağırılarak bir Cursor object elde edilir. Bu cursor object'in setNotificationUri() method'u çağırılarak, uri object'de bir
değişikilk olursa bu değişikliğin Cursor object'e de yansıması sağlanır.
Diyelim
ki uygulamayı çalıştırdık. Sonra insert(), update() ve delete() method'larını
çağırarak veritabanında değişiklikler yaptık diyelim. insert(), update() ve
update() method'larının sonunda çağırdığımız
getContext().getContentResolver().notifyChange(uri,
null);
kodu
query() method'unun sonunda
çağırdığımız cursor.setNotificationUri(getContext().getContentResolver(),
uri); satır sayesinde bu cursor'ı
tetikler. Yani query method'unda elde ettiğimiz Cursor object'in veritabanında
yapılan değişikiklerden haberi olur. Sonra query method'unda bu cursor object'i
return ederiz.
Peki
query() method'unu sonunda bu kodu çağırmazsak ne olur? cursor.setNotificationUri(getContext().getContentResolver(),
uri); veritabanında yapılan
değişikiklerden cursor object'in haberi olmaz, dolayısıyla contentprovider
class'ının query() method'unu çağırdığımızda veritabanının güncel halini
sorgulayamayız. )
public class BirthProvider extends
ContentProvider {
@Override
public Cursor query(){
Cursor cursor = queryBuilder.query(...);
//
register to watch a content URI for changes
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
}
CursorLoader
registers observer for the cursor, not to
the URI.
So once that
ContentResolver
knows that URI's content has been changed [
this happens when you call getContext().getContentResolver().notifyChange(uri,
contentObserver);
inside ContentProvider
's insert()
, update()
and delete()
methods ] it notifies all the observers
including CursorLoader's ForceLoadContentObserver
.
( ContentProvider'ın
insert()
, update()
ve delete()
method'larından
biri çağırıldığında, bu method'ların içerisindeki şu kod çalışır : getContext().getContentResolver().notifyChange(uri,
contentObserver);
bu kodun çalışmasıyla ContentResolver object, URI'ın içeriğinin
değiştiğinden haberdar olur. Bu, URI'ı observe eden herkese bu değişiklik haber
verilir. Örneğin query method'unda şu kod olduğu için : cursor.setNotificationUri(getContext().getContentResolver(), uri); cursor
object bu değişiklikten haberdar olur ve güncel veritabanına refer eder. )
ContentProvider class'ındaki query() method'u şöyle bir satır içerir:
// Tell the cursor what uri to watch, so it knows when
its source data changes (Cursor object'e hangi uri object'i
izleyeceğini söyleriz burada. Bu sayede bu uri'daki içerik değişince cursor
object'in bundan haberi olur. )
cursor.setNotificationUri(getContext().getContentResolver(), uri);
CursorLoader get
cursor back and registers an observer.(
CursorLoader, cursor'ı bir observer'a register eder, yani cursor bu observer'ı
dinler. Yani bu observer'da bir değişiklik olursa cursor'ın bundan haberi
olacaktır. ) When someone modifies data, ContentProvider notifies ContentResolver about changes: ( Uri içeriğinde(yani veritabanında)bir değişiklik olursa
şu method çağırılarak contentProvider'ın contentResolver'a haber vermesi
sağlanır: )
getContext().getContentResolver().notifyChange(uri,
null);
ContentResolver in
its turn notifies all registered observers. Observer, registered by CursorLoader, forces it to load new data.( ContentResolver da bu uri observe eden(dinleyen, bu
uri'a register edilmiş olan) observer'lara değişikliği haber verir. Observer
güncel veritabanını görür. )
Step 6 : Uri.getLastPathSegment()
uri.getLastPathSegment()
nedir?
import
android.net.Uri;
Uri
uri = Uri.parse("http://example.com/foo/bar/42");
String
token = uri.getLastPathSegment();
content uri, şu formdadır :
prefix>://<authority>/<data_type>/<id>
bu form'daki son kısmı yani id kısmını nasıl elde edebiliriz? uri object'in getLastPathSegment() method'unu çağırarak. Yukarıdaki uri object için bu
method, "42" return eder.
String
getLastPathSegment
()
Returns
the decoded last segment or null if the path is empty. (
content uri path'inin son segment'ini string olarak return eder. )
String
getPath()
Returns
the decoded path, or null if this is not a hierarchical URI (like
"mailto:nobody@google.com") or the URI is invalid. (content uri path'ini string olarak return eder. Yukarıdaki
uri object için bu method, "http://example.com/foo/bar/42" return
eder. )
List<String>
getPathSegments ()
Gets
the decoded path segments.
Returns
decoded path segments, each without a leading or trailing '/' ( Bu method, elemanları string olan bir List object
return eder. content uri path'ini "/" karakterlerine göre parse eder,
elde ettiği parçaları listeye ekle, bu listeyi return eder. Yukarıdaki uri
object için bu method, "example.com", "foo",
"bar","42" string'lerini içeren bir List object return
eder. )
Örnek : Aşağıda,
content provider class'ının query() method'u gösterilmiştir. Bu method'da, uri
object'in getLastPathSegment'i çağırılarak content url path'inin son kısmı elde
edilmiştir, bu kısım id'dir bu örnek için. Bu id, dbHelper.getReadableDatabase().query()
method'unun selection argument'ine verilmiştir bu örnekte.
@Override
public Cursor query(Uri uri, String[] projection, String
selection, String[] selectionArgs, String sortOrder) {
if
(uri.getLastPathSegment() != null) {
String id =
uri.getLastPathSegment();
selection =
"_id = ?";
selectionArgs
= new String[] {id};
}
return
dbHelper.getReadableDatabase().query(
TABLE_NAME
,
null // columns
,
selection
, selectionArgs
,
null // groupBy
,
null // having
,
null // orderBy
);
}
SQLiteDatabase class'ının query() method'unun aldığı son 3 argument
sırasıyla SQL'deki groupBy, having ve orderBy 'a benzer.
Step 7 : TextUtils.isEmpty(str)
Question
:
TextUtils.isEmpty(string)
ve string.isEmpty()
arasındaki
fark nedir? Her ikisi de kullanılarak bir string'in boş olup olmadığı yani şuna
"" eşit olup olmadığı check edilir. )
Answer :
You can use android.text.TextUtils.isEmpty() instead. This method also checks to see if the
String is null and 0-length since API level 1.
TextUtils.isEmpty(string)
is always
preferred over string.isEmpty()
because the
later will throw a NullPointerException for any null string
whereas the
first will always return a boolean value. (string.isEmpty()
method'u değil de TextUtils.isEmpty(string)
method'unu
tercih etmelisiniz. Çünkü bir null string için string.isEmpty()
method'unu
çağırırsak NullPointerException throw eder. TextUtils.isEmpty(string)
method'u ise
exception throw etmez, her zaman için boolean return eder. )
In code, the first
simply calls the equivalent of the other, plus a null
check : return string == null || string.length() == 0;
(
TextUtils.isEmpty(string)
method'unun
implementation'ı aşağıda gösterilmiştir. Aşağıda da gördüğümüz üzere, bu
method'un implementation'ı, string.isEmpty() method'unun
implementation'ını(string.length==0) da içerir, buna ek olarak string'in null
olup olmadığını da check eder. )
public class TextUtils
{
...
public static
boolean isEmpty(CharSequence str) {
if (str ==
null || str.length() == 0)
return
true;
else
return
false;
}
}
Örnek : Aşağıdaki örnekte ContentProvider
class'ının update() method'unun implementation'ı gösterilmiştir :
Step 8 : getType(Uri) method of ContentProvider
ContentProvider
class'ının getType() method'unu
inceleyelim. Bu method ne işe yarar ne için kullanılır?
getType(Uri)
returns the MIME type of data in the content provider. (ContentProvider'daki
verilerin MIME type'ını return eder. )
getType() method uses the UriMatcher on
line 19 to determine which MIME type to return. If the URI is a list URI, it
returns TASKS_MIME_TYPE. If it’s an item URI, it returns TASK_MIME_TYPE. ( getType() method'unu inceleyelim. Switch block'da,
UriMatcher object'in match() method'una argument olarak URI object verilmiştir.
Eğer URI bir list
URI ise, yani FRIENDS case'i için , tüm satırları getirmek için kullanılır.
List type'ında bir URI için getType() method'unun return edeceği string "vnd.android.cursor.dir" ile başlamak
zorundadır.
Eğer URI bir item
URI ise, yani FRIENDS_ID case'i için, belirli bir satırı getirmek için
kullanılır. Item type'ında bir URI için getType() method'unun return edeceği
string "vnd.android.cursor.item" ile başlamak zorundadır. )
@Override
public String getType(Uri uri)
{
switch
(uriMatcher.match(uri)){
case FRIENDS: // Get all friend-birthday
records
return
"vnd.android.cursor.dir/vnd.example.friends";
case
FRIENDS_ID: // Get a particular friend
return "vnd.android.cursor.item/vnd.example.friends";
default:
throw
new IllegalArgumentException("Unsupported URI: " + uri);
}
}
getType(Uri uri) will usually only be
called after a call to ContentResolver#getType(Uri uri). It is used by
applications to retrieve the MIME type of the given content URL. If your app
isn't concerned with the data's MIME type, it's perfectly fine to simply have
the method return null.( ContentResolver class'ının
getType() method'U çağırıldıktan sonra, ContentProvider class'ının getType()
method'u da çağırılır. Uygulamalar, getType() method'unu, bir URL'in MIME
type'ını elde etmek için kullanırlar. Eğer uygulamanız, verilerinizin MIME
type'ı ile ilişkili değilse, getType() method'unun implementation'da null
return etmeniz yeterli olacaktır. )
----
https://developer.android.com/reference/android/content/ContentProvider.html
Implement this to handle requests for
the MIME type of the data at the given URI. The returned MIME type should start
with
vnd.android.cursor.item
for
a single record, or vnd.android.cursor.dir/
for multiple items.
This method can be called from multiple threads, as described in Processes
and Threads.
( Diyelim ki ekrandaki bir butona bastık bu butona
basınca, bir URL'e istek yapıldı diyelim, bir URI object yaratıldığını
düşünelim. contentresolver object'in getType() method'unu çağırdık kodda
diyelim. contentresolver object'in getType() method'u çağırıldıktan sonra
otomatik olarak contentprovider object'in getType() method'u çağırılır.
getType() method'u, istek yapılan URI
object'e göre bir string return eder. uygulamamızın herhangi bir yerinde,
contentresolver object'in getType() method'unu çağırarak, URI object'in type'ını
string olarak elde edebiliriz. Örneğin bir .jpeg dosyasının url'inden
oluşturulmuş bir URI object olsun. Bunun için contentResolver'ın getType()
method'unu çağırdığımızda, images/.jpeg gibi bir şey elde ederiz. Buna
gerçekten ihtiyacın olduğunda kod içinde deneyerek gör. )
Note that there are no permissions
needed for an application to access this information; if your content provider
requires read and/or write permissions, or is not exported, all applications
can still call this method regardless of their access permissions. This allows
them to retrieve the MIME type for a URI when dispatching intents. ( Tüm uygulamalar, tüm contentProvider'ların getType()
method'larına erişebilirler. ContentProvider'ınızın read/write erişimleri
olmasa bile, hatta contentProvider'ınızı export etmeseniz bile, erişim
yetkileri ne olursa olsun, tüm uygulamalar
getType() method'unu çağırabilirler, böylece URI object'in MIME type'ını
öğrenebilirler. )
---------
http://stackoverflow.com/questions/5351669/why-use-contentprovider-gettype-to-get-mime-type
For example,
you're writing content provider for picture gallery. You should mention in your
getType() method that you provide pictures - jpg or png. So, when one will
launch image gallery, it will be able to show built-in pictures and pictures
provided by your content provider.( Diyelim ki bir resim galerisi uygulaması
için bir contentProvider yazıyoruz. Bu durumda, contentProvider'ımızın,
getType() method'unda uygulamada kullanılması muhtemel tüm resim dosyalarının
uzantılarını belirtmeliyiz. Bu sayede, uygulamamızı çalıştırdığımızda, bu
getType() method'unda tanımladığımız uzantıdaki resim dosyaları gösterilebilir
olur. Burayı açıkladım ancak ben de tam anlamadım. )
( Aşağıdaki
pseudocode kodda, contentProvider'ın client'ı yani contentProvider'a
erişen,kullanan bir kodda, yani contentProvider class'ı haricinde bir yerde getProviders(); method'unu çağırarak tüm
system'de tanımlı tüm provider'ları içeren bir List object elde ederiz. jpg
uzantılı dosyalar için bir Type object yaratırız. Sonra tüm provider'lar
üzerinde tek tek gezip provider'ın getType() method'unu çağırarak provider'ın MIME
type'ını elde ederiz ve bu type'ın type object ile aynı olup olmadığına
bakarız. Aynı ise bu provider'ı provider'lar listemize ekleriz. Böylece .jpg
uzantılı dosyalara refer edebilecek URI'ları destekleyen tüm
contentProvider'ların bir listesini elde etmiş oluruz. )
List contentProviders = getProviders();
List resultProviders;
final Type type = Type.JPG;
for (ContentProvider provider : contentProviders) {
if (type == provider.getType()) {
resultProviders.add(provider);
}
}
So in this case you are getting a list
of all providers in the system and filter on the type of data they return, for
example to display an image browser with all available JPGs on the device? :)
Yes, that's right. I'm not
sure this is the code you're going to write whenever, but it is good to show
the concept.
-----------
Implement the getType() method
Every content provider must return the
content type for its supported URIs. The signature of the method takes a URI
and returns a
String
. The next code sample shows the getType()
method of a sample content provider. ( ContentProvider, desteklediği
URI'ların content type'larını return etmek zorundadır. getType() method'u
argument olarak URI object alır, string return eder. )@Override
publicString
getType(Uri uri
){
switch
(
URI_MATCHER
.match(uri
)){
case
ITEM_LIST
:
return
Items
.CONTENT_TYPE
;
case
ITEM_ID
:
return
Items
.CONTENT_ITEM_TYPE
;
case
PHOTO_ID
:
return
Photos
.CONTENT_PHOTO_TYPE
;
case
PHOTO_LIST
:
return
Photos
.CONTENT_TYPE
;
case
ENTITY_ID
:
return
ItemEntities
.CONTENT_ENTITY_TYPE
;
case
ENTITY_LIST
:
return
ItemEntities
.CONTENT_TYPE
;
default:
return
null
;
}
}
As you can see this method is pretty
simple. You just have to return the appropriate content type – defined within
your contract class – for the URI passed into this method. ( getType() method'unu implement etmek çok
kolaydır. Tek yapmamız gereken hangi URL için hangi string'i return edeceğimizi
implement etmektir. Muhtemel URL'ler genellikle contract class'da static final olarak
tanımlanırlar.
Daha önce de
söylediğimiz gibi, contentProvider'ın client'ı yani contentProvider'a erişen,
kullanan bir kodda, yani contentProvider class'ı haricinde bir yerde
contentResolver
object'in getType() methoD'unu çağırırız, böylece otomatik olarak ilgili
contentProvider'ın getType() method'u çağırılır. URI object'in MIME type'ı elde
edilir bu şekilde.
Elde ettiğimiz URI
object'in MIME type'ını, contentProvider'ın contract class'ında tanımlı
type'lar ile kıyaslayıp buna uygun aksiyon alabiliriz. )
Step 9 :
insert() method of ContentProvider
Bu örnekte, butona tıklayınca, activity
class'ında tanımladığımız addBirthday() method'u
çağırılır.
addBirthday() method'u çağırıldığında, EditText'lere
yazdığımız string'leri okuruz. Bu string değerlerini sırasıyla "name" ve "birtday" sütunlarına
vereceğiz.
addBirthday() method'unda, getContentResolver().insert(BirthProvider.CONTENT_URI,value)
method'unu
çağırarak birthTable isimli
table'a value isimli ContentValues object'e
eklediğimiz değerleri insert ederiz.
import android.content.ContentUris;
long row = database.insert(TABLE_NAME, "", values);
// If record is added successfully
if(row > 0) {
Uri newUri =
ContentUris.withAppendedId(CONTENT_URI, row);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
ContentProvider class'ının insert() method'unda, SQLiteDatabase object'in
insert() method'unu çağırırız, bu method aldığı 1.argument'deki table'a satır
insert eder. Eğer insertion başarılı bir şekilde gerçekleşirse, insert()
method'u 0'dan büyük bir değer return eder. URI object'imize elde ettiğimiz bu
değeri append ederiz ContentUris isimli class'ın withAppendedId() method'unu
çağırarak.ContentUris.withAppendedId(CONTENT_URI, row) bir Uri
object return eder. Sonra getContext().getContentResolver().notifyChange(newUri,
null)
diyerek, bu uri'da gerçekleşen değişikliği haber veririz. ContentResolver class'ının notifyChange() method'una
yeni elde ettiğimiz uri object'i verdiğimize dikkat et. insert() method'u yeni
uri object'i return eder. )
Step 10 :
ContentProvider , URI object, which table
ContentProvider class'ında
tanımlanan URI object'in
authority'si "com.javacodegeeks.provider.BirthdayProv"dir. Bu
contentprovider için hangi table'ın kullanılacağı nerede belirlenmiştir peki?
Bu sorunun tam cevabını bilmiyorum.
Contentprovider object
yaratılırken, contentProvider class'ının onCreate() method'u çağırıldığında, contentProvider class'ının
içerdiği SQLiteOpenHelper class'ından
da bir object yaratılır, dolayısıyla birthtable isimli bir
table yaratılır.
MainActivity'de
tanımlanan 3 method'a bakalım. 3 butona basınca bu 3 method çağırılır. Her 3
method'da da contentResolver object'in delete,insert,update
method'larının 1. argument'leri URI object alır. Hangi table'a
insert atacağımızı, hangi table'a sorgu atacağımızı, hangi table'ı güncelleyeceğimizi, contentProvider'ın insert(), query() ve update() method'larında belirtiriz.
Peki mademki hangi
table'a insert atılacağını insert(),
query() ve update() method'larında
belirledik. O halde URI object'e neden ihtiyacımız var? Çünkü bu örnekte sadece
bir tane table söz konusu. Eğer birden fazla table söz konusuysa, URIMatcher object'in match() method'una URI object'i vererek URI 'a göre uygun table'ı seçebiliriz contentprovider class'ının insert(), query() ve update() method'larında.
Step 11 :
Declare provider in AndroidManifest.xml
<provider android:name=".BirthProvider"
android:authorities="com.javacodegeeks.provider.BirthdayProv">
</provider>
android:authorities : provider'ın uri'ının authorities'ini
beliritiriz.
android:name : provider class'ının path'İni beliritiriz.
---------
SQL dersimizde
contract class'ını anlatmıştık. ContentProvider kullanırken contract class
kullanmak istiyorsak SQL derslerimizi incelemeliyiz.
Contract class
kullanılarak content provider implementation yapılması örneği aşağıdaki
kodlarda mevcuttur:
http://www.grokkingandroid.com/android-tutorial-writing-your-own-content-provider/
https://bitbucket.org/grokkingandroid/cpsample/downloads Bu proje bayağı
karmaşık sen sadece provider klasörü altındaki dosyalara bak.
2 -
https://github.com/udacity/android-content-provider
3 -
https://gist.github.com/dustin-graham/9146366 RainEmployeeProvider.java birden fazla authority table vs var bu
örnekte.
Step 12 :
ContentUris.withAppendedId(CONTENT_URI, row)
ContentProvider class'ının
sadece insert() method'unda Uri newUri =
ContentUris.withAppendedId(CONTENT_URI, row); kullanılıp yeni uri elde edilir. Bu method, yeni uri
object'i return eder.
ContentProvider class'ının update() method'unda uri object'de
değişiklik olmaz. SQLiteDatabase object'in update() method'unun return ettiği değeri return
eder.
ContentProvider class'ının delete() method'unda uri object'de
değişiklik olmaz. SQLiteDatabase object'in delete() method'unun return ettiği değeri return
eder.
Hiç yorum yok:
Yorum Gönder