1.運行時權限
Android運行時的危險權限
在運行時申請權限代碼:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
makeCall.setOnClickListener {
//獲得運行時權限
if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,arrayOf(Manifest.permission.CALL_PHONE),1)
}else{
call()
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when(requestCode){
1 ->{
if(grantResults.isNotEmpty()&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
call()
}else{
Toast.makeText(this,"沒有打電話的權限",Toast.LENGTH_SHORT).show()
}
}
}
}
private fun call(){
try {
val intent = Intent(Intent.ACTION_CALL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}catch (e : Exception){
e.printStackTrace()
}
}
}
在按鈕點擊事件中,我們構建了一個隱式 Intent,Intent 的 action 指定爲Intent.ACTION_CALL,這是系統內置的打電話的動作,然後在data部分制定了協議是tel,號碼是10086。
第一步先判斷用戶是不是已經給過我們授權了,藉助的是ContextCompat.checkSelfPermission()方法。checkSelfPermission()方法接收兩個參數:第一個是Context,第二個參數是具體的權限名。然後我們使用返回值和PackageManager.PERMISSION_GRANTED做比較。相等就說明用戶已經授權
我們將打電話的邏輯封裝到了call()方法當中。如果沒有授權的話,則需調用ActivityCompat.request.Permissions()方法向用戶申請授權,requestPermissions()方法接收3個參數:第一個要求是Activity的實例,第二個參數是一個String數組,把申請的權限名放到數組裏即可。第三個參數是請求碼,只要是唯一值就可以,這裏傳入了1.
調用完requestPermissions()方法後,會彈出申請權限對話框。不論選擇哪種結果,都會回調到onRequestPermissionsResult()方法中,授權結果會封裝在grantResults參數中。
2.訪問其他程序中的數據
ContentResolver 中提供了一系列方法對數據進行增刪改查操作,但不同於SQLiteDatabase,ContentProvider中不接受表名,而是使用一個Uri參數代替。
內容URI最標準的格式如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
它主要由兩部分組成:authority 和 path。authority用於對不同的應用程序做區分,會採用應用包名的方式進行命名。path則是對同一應用程序不同表做區分
1.將內容URI解析成Uri對象纔可以作爲參數傳入。
val uri = Uri.parse(“content://com.example.app.provider/table1”)
2.使用這個Uri對象查詢table1表中的數據
val cursor = contentResolver.query{
uri,
projection,
selection,
selectionArgs,
sortOrder
}
query()方法參數 | 對應SQL部分 | 描述
uri | from table_name | 指定查詢某個應用程序下的某一張表
projection | selet column1 , colum2 | 指定查詢的列名
selection | where column =value | 指定where的約束條件
selectionArgs | | 爲where中的佔位符提供具體的值
sortOrder | order by column1,column2 | 指定查詢結果的排序方式
查詢完成後返回仍然是一個Cursor對象
while(cursor.moveToNext()){
val column1 = cursor.getString(Cursoe.getColumnIndex("colum1"))
val column2 = cursor.getString(Cursoe.getColumnIndex("colum2"))
}
curosr.close()
向table1表添加一條數據
val values = contentValuesOf( "column1" to "text", "column2" to 1 )
contentResolver.insert(uri , values)
更新數據,將colum1的值清空
val values = contentValuesOf( "column1" to "" )
contentResolver.update(uri , values,"column2 = ?", arrayOf("1"))
調用ContentResolver的delete()方法將這條數據刪除掉
contentResolver.deleter(uri , "colummn2 = ?", arrayOf("1"))
2.2實戰讀取系統聯繫人
修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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=".MainActivity"
android:orientation="vertical">
<ListView
android:id="@+id/contactsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
></ListView>
</androidx.appcompat.widget.LinearLayoutCompat>
修改MainActivity
class MainActivity : AppCompatActivity() {
private val contactsList = ArrayList<String>()
private lateinit var adapter: ArrayAdapter<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
adapter= ArrayAdapter(this,android.R.layout.simple_list_item_1,contactsList)
contactsView.adapter=adapter
//動態獲取權限
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, arrayOf( Manifest.permission.READ_CONTACTS),1)
}else{
readContacts()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when(requestCode){
1 -> {
if (grantResults.isNotEmpty()&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
readContacts()
}else{
Toast.makeText(this,"沒有權限的話,就不能這樣做啦!",Toast.LENGTH_SHORT).show()
}
}
}
}
//讀取聯繫人操作
private fun readContacts(){
//查詢聯繫人數據
contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null)?.apply {
while (moveToNext()){
//獲取聯繫人姓名
val displayName = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
//獲取聯繫人手機號
val number = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
contactsList.add("$displayName\n電話號碼:$number")
}
adapter.notifyDataSetChanged()
close()
}
}
}
3.創建自己的ContentProvider
3.1創建 ContentProvider的步驟
ContentProvider類種有6個抽象方法
1)onCreate()初始化ContentProvider時調用。通常會在這裏完成對數據庫的創建和升級等操作,返回true表示ContentProvider初始化成功,返回false則表示失敗
2)query()。從ContentProvider中查詢數據。uri確定查詢哪張表,projection確定查詢那些列,selection和selectionArgs約束那些行,sortOrder對結果排序,查詢結果存在Cursor對象中返回。
3)insert()。uri確定查詢哪張表,待添加的數據保存在values參數中。完成添加後,返回一個表示這條新記錄的URI
4)update()。uri確定查詢哪張表,待添加的數據保存在values參數中,selection和selectionArgs約束那些行,受影響的行數作爲返回值返回
5)delete()。uri確定查詢哪張表,selection和selectionArgs約束那些行,被刪除的行數作爲返回值返回
6)getType()。根據傳入的內容URI返回相應的MIME類型
標準URI格式寫法content://com.example.app.provider/table1
還可以在URI後面加idcontent://com.example.app.provider/table1/1
一個能匹配任意表的內容URI可以寫成content://com.example.app.provider/*
一個能匹配table1表中任意一行數據內容URI格式content://com.example.app.provider/#
getType()方法中,用於獲取Uri對象所對應的MIMe類型。一個內容URI對應的MIME字符串主要有3部分組成,android對這3部分做了如下格式規定
1).必須以vnd開頭。
2).如果內容URI以路徑結尾,則後接android.cursor.dir/;如果內容URI以id結尾,則後接android.cursor.item/
3).最後接上vnd.<authority>.<path>
3.2實現跨程序數據共享
首先在DatabaseTest項目的基礎上繼續開發。右擊 com.example.databasetest包->New->Other->Content Provider.
將 ContentProvider 命名 DatabaseProvider , 將authority 指定爲com.example.databasetest.provider。
class DatabasesProvider : ContentProvider() {
private val bookDir = 0
private val bookItem = 1
private val categoryDir = 2
private val categoryItem =3
private val authority = "com.example.databasetest.provider"
private var dbHelper : MyDatabaseHelper? = null
private val uriMatcher by lazy{
val matcher = UriMatcher(UriMatcher.NO_MATCH)
matcher.addURI(authority,"book",bookDir)
matcher.addURI(authority,"book/#",bookItem)
matcher.addURI(authority,"category",categoryDir)
matcher.addURI(authority,"category/#",categoryItem)
matcher
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = dbHelper?.let{
//刪除數據
val db = it.writableDatabase
val deletedRows = when (uriMatcher.match(uri)){
bookDir -> db.delete("Book",selection,selectionArgs)
bookItem -> {
val bookId = uri.pathSegments[1]
db.delete("Book","id = ?", arrayOf(bookId))
}
categoryDir -> db.delete("Category",selection,selectionArgs)
categoryItem ->{
val categoryId = uri.pathSegments[1]
db.delete("Category","id = ?", arrayOf(categoryId))
}
else -> 0
}
deletedRows
} ?:0
override fun getType(uri: Uri) = when (uriMatcher.match(uri)) {
bookDir -> "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book"
bookItem -> "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book"
categoryDir ->"vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category"
categoryItem -> "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category"
else -> null
}
override fun insert(uri: Uri, values: ContentValues?) = dbHelper?.let{
//添加數據
val db = it.writableDatabase
val uriReturn = when(uriMatcher.match(uri)){
bookDir,bookItem -> {
val newBookId = db.insert("Book",null,values)
Uri.parse("content://$authority/book/$newBookId")
}
categoryDir, categoryItem -> {
val newCategoryId = db.insert("Category",null,values)
Uri.parse("content://$authority/category/$newCategoryId")
}
else -> null
}
uriReturn
}
override fun onCreate() = context?.let{
dbHelper =MyDatabaseHelper(it,"BookStore.db",2)
true
} ?:false
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
) = dbHelper?.let {
//查數據
val db = it.readableDatabase
val cursor =when(uriMatcher.match(uri)) {
bookDir -> db.query("Book",projection,selection,selectionArgs,null,null,sortOrder)
bookItem -> {
val bookId = uri.pathSegments[1]
db.query("Book",projection,"id = ?", arrayOf(bookId),null,null,sortOrder)
}
categoryDir ->db.query("Caregory",projection,selection,selectionArgs,null,null,sortOrder)
categoryItem ->{
val categoryId = uri.pathSegments[1]
db.query("Category",projection,"id = ?", arrayOf(categoryId),null,null,sortOrder)
}
else -> null
}
cursor
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
) = dbHelper?.let {
//更新數據
val db = it.writableDatabase
val updatedRows =when(uriMatcher.match(uri)){
bookDir ->db.update("Book",values,selection,selectionArgs)
bookItem ->{
val bookId = uri.pathSegments[1]
db.update("Book",values,"id = ?", arrayOf(bookId))
}
categoryDir ->db.update("Category",values,selection,selectionArgs)
categoryItem -> {
val categoryId = uri.pathSegments[1]
db.update("Category",values,"id = ?", arrayOf(categoryId))
}
else -> 0
}
updatedRows
} ?:0
}
創建新項目ProviderTest
class MainActivity : AppCompatActivity() {
var bookId : String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addData.setOnClickListener {
//添加數據
val uri = Uri.parse("content://com.example.databasetest.provider/book")
val values =contentValuesOf("name" to "A Clash of Kings","author" to "George Martin","pages" to 1040 ,"price" to 33.85)
val newUri =contentResolver.insert(uri,values)
bookId = newUri?.pathSegments?.get(1)
}
queryData.setOnClickListener {
//查詢數據
val uri =Uri.parse("content://com.example.databasetest.provider/book")
contentResolver.query(uri,null,null,null,null)?.apply {
while(moveToNext()){
val name = getString(getColumnIndex("name"))
val author = getString(getColumnIndex("author"))
val pages = getInt(getColumnIndex("pages"))
Log.d("MainActivity","書名是 $name")
Log.d("MainActivity","作者是 $author")
Log.d("MainActivity","有 $pages 頁")
}
close()
}
}
updateData.setOnClickListener {
//更新數據
bookId?.let {
val uri = Uri.parse("content://com.example.databasetest.provider/book/$it")
val values = contentValuesOf("name" to "A Storm of Swords","pages" to 1216)
contentResolver.update(uri,values,null,null)
}
}
deleteData.setOnClickListener {
//刪除數據
bookId?.let {
val uri = Uri.parse("content://com.example.databasetest.provider/book/$it")
contentResolver.delete(uri,null,null)
}
}
}
}
4.Kotlin
4.1泛型基本用法
4.1.1定義一個泛型類
class MyClass<T> {
fun method(param : T) : T{
return param
}
}
調用方式
val myClass = MyClass<Int>()
val result = myClass.method(123)
4.1.2定義一個泛型方法
class MyClass {
fun <T> method (param : T) : T{
return param
}
}
調用方式
val myClass = MyClass()
val result = myClass.method<Int>(123)
由於kotlin有出色的類推導機制,這裏可以簡化成
val myClass = MyClass()
val result = myClass.method(123)
4.1.3Kotlin還允許對泛型的類型進行限制,可以將method方法的泛型指定成任意類型。
class MyClass {
fun <T : Number> method (param : T) : T{
return param
}
}
在默認情況下,所有泛型可指定可空類型。泛型上界默認是Any?。如果想讓泛型的類型不可爲空,只需要將泛型的上界手動指定爲Any就可以了
4.1.4泛型實例
新建一個build.kt文件,並編寫如下代碼:
fun <T> T.build(block : T.() -> Unit) : T{
block()
return this
}
完成後就可以像使用apply函數一樣去使用build
4.2類委託和委託屬性
委託使用的關鍵字:by
4.2.1類委託
class MySet<T>(val helperSet : HashSet<T>) : Set<T> by helperSet{
}
藉助委託的功能之後,只需要單獨重寫那一方法就可以了。如:
class MySet<T> (val helperSet : HashSet<T>) : Set<T> by helperSet {
fun helloWorld() = println("Hello World")
override fun isEmpty() = false
} ```
4.2.2屬性委託
語法結構
class MyClass{
var p by Delegate()
}
這裏使用by連接左邊的p屬性和右邊Delegate實例
當調用p屬性的時候會自動調用Delegate類的getValue()方法,當給p屬性賦值的時候會自動調用Delegate類的setValue()的方法
同時我們還得對Delegate類進行具體操作
class Delegate {
var propValue : Any? = null
operator fun getValue(myClass : MyClass.prop : KProperty<*>) : Any?{
return propValue
}
operator fun settValue(myClass : MyClass.prop : KProperty<*>,vlalue : Any?) {
propValue = value
}
}
getValue()方法需要接收兩個參數:第一個用於聲明該Delegate類的委託可能在什麼類中使用,這裏寫成MyClass表示僅可在MyClass類中使用;第二個參數KProperty<>是Kotlin中的一個屬性操作類,可用於獲取各種屬性相關的值。<>這種泛型的寫法表示你不知道或者不關心的泛型的具體類型。類似於java中<?>的寫法。
setValue()方法啊相似。
4.2.3實現一個自己的lazy函數
工作原理解密
val p by lazy{ ...}
新建一個Later.kt文件
class Later<T>(val block : () -> T){
var value:Any? = null
operator fun getValue(any : Any?,prop : KProperty<*>) : T{
if ( value == null) {
value = block()
}
return value as T
}
}
爲了是它的用法更加類似於lazy函數,最好再定義一個頂層函數
fun <T> later(block : () -> T) =Later(T)