1. 程式人生 > >Android 8.0 升級筆記(適配圖片、通知欄、ContentProvider、Receiver)

Android 8.0 升級筆記(適配圖片、通知欄、ContentProvider、Receiver)

Android 8.0 升級筆記

前言

Google 在2017年就釋出了Android 8.0,並且強制AppStore上得應用都要升級,國內得不曉得。為了防止出現之前升級6.0 得時候許可權問題導致Crash這種情況得發生…這次很小心得去看了Google得升級意見,小夥伴們可以自行去看(https://developer.android.com/index.html)。
我大致記錄以下有以下幾點需要注意的:

  • 升級API版本號到26
  • 移除隱式意圖接收器
  • 更新應用圖示

正題

Android studio 3.0.1 下載

提前說下Google 建議使用Android studio 3.0 以上得版本,給一個地址需要得自己去下載吧。

官方(需要翻牆):https://developer.android.com/studio/index.html
國內:http://www.android-studio.org/

編譯版本&相關庫升級

找到你專案的build.gradle,主要關注以下幾個,升級到26

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.2"
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 26
    }
}

升級相關的支援庫


如果你不這麼做studio會提示警告,但是也不會影響執行,如果要用庫裡最新的控制元件還是更新下吧。程式碼潔癖的不會想看到大段的黃色警告和紅色波浪線…

 compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:cardview-v7:26.1.0'
    compile 'com.android.support:design:26.1.0'

我暫時就用到這些,有些專案裡有用到Google Service的服務也要升級到 11.8.0

注意一點是studio 3.0以下的版本會提示找不到 “26.1.0”的庫,而且你在Sdk Manager也找不到,需要再project的build.gradle加google的庫,jCneter 裡沒有。

allprojects {
    repositories {
        jcenter()
        google()
    }
}

或者這樣寫

maven {
            url 'https://maven.google.com/'
            name 'Google'
        }

當然如果是Studio3.0 以上的版本IDE 會自動幫你加上!

應用隱式意圖檢查

Google為了優化(填以前的坑)系統性能,讓應用無法再Manifast註冊隱式意圖了,因為Google認為這樣做太消耗資源,特麼的開發者寫的APP消耗資源,使用者覺得效能差,出了事要Google的Android系統背鍋!他們不幹了,要開始約束開發者行為。

舉個例子:

一款社交圖片APP,開發者希望再每次連結資料線的時候備份照片並清理快取。

那麼他需要寫一個廣播接收器,並再manifest中註冊這個接收器,接收ACTION_POWER_CONNECTED 的廣播。

<receiver android:name="com.xxx.PowerConnectReceiver">
            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
            </intent-filter>
        </receiver>

再Android 8.0 上Google說你不能這麼幹!這會在每次連結電源的時候去通知所有註冊了這個廣播的應用浪費系統資源!所以移除吧。

當然也不是所有的意圖都被過濾掉了,官網也說了有些意圖不會受這條約束(https://developer.android.com/guide/components/broadcast-exceptions.html),我從官網摘錄了下來,訪問不了外網的可以搜以下你的應用有下面這些隱式意圖的話就不用做8.0的適配,相反你就需要做適配

ACTION_LOCKED_BOOT_COMPLETED, ACTION_BOOT_COMPLETED
由於這些廣播只能在第一次啟動時只發送一次,因此許多應用程式需要接收此廣播來安排作業,警報等等。

ACTION_USER_INITIALIZE, "android.intent.action.USER_ADDED", "android.intent.action.USER_REMOVED"
這些廣播受到特權的保護,因此大多數普通應用程式無法收到它們。

"android.intent.action.TIME_SET", ACTION_TIMEZONE_CHANGED, ACTION_NEXT_ALARM_CLOCK_CHANGED
時鐘應用程式可能需要接收這些廣播,以在時間,時區或警報發生更改時更新警報。

ACTION_LOCALE_CHANGED
只有在語言環境發生變化時才會傳送,這種情況並不常見。應用程式可能需要在區域設定更改時更新其資料。

ACTION_USB_ACCESSORY_ATTACHED, ACTION_USB_ACCESSORY_DETACHED, ACTION_USB_DEVICE_ATTACHED, ACTION_USB_DEVICE_DETACHED
如果一個應用程式需要了解這些USB相關事件,目前還沒有一個好的選擇來註冊廣播。

ACTION_CONNECTION_STATE_CHANGED, ACTION_CONNECTION_STATE_CHANGED, ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED
如果應用程式接收到這些藍芽事件的廣播,使用者體驗不太可能受到影響。

ACTION_CARRIER_CONFIG_CHANGED, TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED, "TelephonyIntents.SECRET_CODE_ACTION"
OEM電話應用程式可能需要接收這些廣播。

LOGIN_ACCOUNTS_CHANGED_ACTION
某些應用程式需要知道登入帳戶的更改,以便他們可以為新帳戶和更改的帳戶設定預定的操作。

ACTION_PACKAGE_DATA_CLEARED
只有在使用者從“設定”中明確清除其資料時才會傳送,因此廣播接收器不太可能會顯著影響使用者體驗。

ACTION_PACKAGE_FULLY_REMOVED
有些應用程式可能需要更新其儲存的資料時,另一個包被刪除; 對於這些應用程式,沒有好的選擇註冊這個廣播。

注意:其他與包相關的廣播(例如ACTION_PACKAGE_REPLACED)不受新限制的限制。這些廣播已經足夠普遍,對於免除這些廣播有潛在的效能影響。

ACTION_NEW_OUTGOING_CALL
針對撥打電話的使用者採取行動的應用程式需要接收此廣播。

ACTION_DEVICE_OWNER_CHANGED
這個廣播不經常傳送; 一些應用程式需要接收它,以便他們知道裝置的安全狀態已經改變。

ACTION_EVENT_REMINDER
由日曆提供程式傳送,以便將事件提醒傳送到日曆應用程式。由於日曆提供程式不知道日曆應用程式是什麼,這個廣播必須是隱含的。

ACTION_MEDIA_MOUNTED, ACTION_MEDIA_CHECKING, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_EJECT, ACTION_MEDIA_UNMOUNTABLE, ACTION_MEDIA_REMOVED, ACTION_MEDIA_BAD_REMOVAL
這些廣播是由於使用者與裝置的物理互動(安裝或移除儲存卷)或作為引導初始化的一部分(因為可用卷被掛載)而傳送的,因此它們不是常見的並且通常在使用者的控制之下。

SMS_RECEIVED_ACTION, WAP_PUSH_RECEIVED_ACTION
這些廣播是由SMS收件人應用程式依靠。

注:需要簽名許可權的廣播不受此限制所限

那麼想實現上面那個功能該怎麼辦呢?我就是想在應用中接收連線電源的廣播要怎麼做呢?

  • 讓使用者再Setting 中自己開啟(基本不可取)
  • 在應用啟動的時候動態的註冊廣播

    Context.registerReceiver()
  • 使用JobScheduler

簡單一句介紹,5.0 之後系統會幹掉輪詢等保活機制,JobScheduler系統不會殺。

有興趣的哥們自己去研究下,不難理解。

顯示意圖 & 隱式意圖

舉個例子,如果忘記了顯示\隱式意圖的可以看下。

顯式意圖

顯式意圖:呼叫Intent.setComponent()或Intent.setClass()方法明確指定了元件名的Intent為顯式意圖,顯式意圖明確指定了Intent應該傳遞給哪個元件。

Intent intent = new Intent(context,AnotherActivity.class);//第二個引數是待啟動Activity的類的反射  
intent.putExtra("name",name);//可通過意圖向另一個Activity傳遞資料  
startActivity(intent);//跳轉到另一個Activity  
隱式意圖

隱式意圖:沒有明確指定元件名的Intent為隱式意圖。 Android系統會根據隱式意圖中設定的動作(action)、類別(category)、資料(URI和資料型別)找到最合適的元件來處理這個意圖。

<intent-filter>  
   <action android:name="android.intent.yinsiyitu.action"/>  
   <category android:name="android.intent.category.DEFAULT"/>  
   <data android:mimeType="application/person"/>  
   <data android:scheme="jianren" android:host="www.ggl.com"/>  
</intent-filter>  

或者在Activity中寫

Intent intent = new Intent();  
intent.setAction("android:intent.yinsiyitu.action");  
intent.addCategory(Intent.CATEGORY_DEFAULT);  
//intent.setData(Uri.parse(jianren://www.ggl.com));//會清除前面所有set的type  
//intent.setType("application/person");//會清除前面所有的set的data  
//這是setData和setType兩全的方法,另外如果上面的Activity定義了host,則這裡一定也要指定  
intent.setDataAndType(Uri.parse("jianren://www.ggl.com"),"application/person");  
//如果上面的Activity沒有定義host,則Uri.parse("jianren:");至少要寫到冒號,不可以只寫Uri.parse("jianren")  
startActivity(intent);  

adative圖片適配

這裡寫圖片描述
官網上的圖,我就放這裝個逼,看起來比較屌 哈哈!

https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive.html

上面是官網的文件,去看下吧,我覺得我講的不會有這個細了,我大致說下流程。

1.準備background、foreground icon

用人話說就是讓UI給圖,把logi拆成ICON和背景色 兩張圖

2.建立/res/mipmap-anydpi/ic_launcher.xml

<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Logo背景色 -->
    <background android:drawable="@color/ic_background"/>
    <!--Logo 圖示-->
    <foreground android:drawable="@mipmap/ic_foreground_layer"/>
</adaptive-icon>

注:如果不做這個適配的話8.0 的裝置上你的App 圖示可能是很醜的一個被圓形包住方塊
這裡寫圖片描述

問UI小姐姐要了圖,改完之後的。

這裡寫圖片描述

其他一些API變動

官網上摘錄下來的。

變化 摘要 其他參考資料
隱私性 Android 8.0 不支援使用 net.dns1、net.dns2、net.dns3 或 net.dns4 系統屬性。 行為變更:隱私性
實行了可寫且可執行的程式碼段 對於原生庫,Android 8.0 實行的規則是:資料不應可執行,程式碼不應可寫。 行為變更:原生庫
ELF 標頭和節驗證 動態連結器對 ELF 標頭和節頭中的更多值進行檢查,如果值無效則失敗。 行為變更:原生庫
通知 以 SDK 的 Android 8.0 版本為目標平臺的應用必須實現一個或多個通知渠道,以便向用戶釋出通知。 API 概覽:通知
List.sort() 方法 該方法的實現不得再呼叫 Collections.sort(),否則應用將因堆疊溢位而引發異常。 行為變更:集合的處理
Collections.sort() 方法 在列表實現中,Collections.sort() 現在會引發 ConcurrentModificationException 行為變更:集合的處理

適配後發現的一些問題

上次適配了圖片和部分許可權後,又發現了一些需要適配的地方.

通知許可權

主要就是多了一個通道的概念,Channel ,簡單的就是下面這樣用 先建立一個channel,在向這個channel中傳送通知,相同通道的通知可以歸類,可以做到按照時間、按照使用者群、按照年齡等建立通道,分類接收資料。不這麼做的話收不到通知。

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if(notificationManager != null){
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                //建立channel
                NotificationChannel notificationChannel = new NotificationChannel("channel01","channel01_name"
                        ,NotificationManager.IMPORTANCE_DEFAULT);
                notificationChannel.enableLights(true);//是否允許桌面小紅點, 8.0 上無效?
                notificationChannel.setLightColor(Color.RED);//桌面紅點顏色
                notificationChannel.setSound(null,null);
                Toast.makeText(this,"建立",Toast.LENGTH_SHORT).show();
                notificationManager.createNotificationChannel(notificationChannel);
            }

            int notifyId = 1;
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"channel01");
            builder.setContentTitle("This is Content Title~");//設定內容資訊
            builder.setContentInfo("This is Content Info~");//8.0 上無效

            builder.setOngoing(true);//禁止滑動刪除
            builder.setSmallIcon(R.mipmap.ic_launcher);//設定左上角小圖示
            builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_16_18));//右側圖片
            builder.setDefaults(Notification.DEFAULT_SOUND);//設定預設聲音
            builder.setVibrate(new long[]{100,200,100,500,500});//設定震動 {間隔,時長,間隔,時長...}
            builder.setLights(Color.BLUE,1000,5000);//設定呼吸燈顏色 亮時間 暗時間 有些機型不支援
            builder.setChannelId("channel01");//設定通道,8.0 以上有用
            notificationManager.notify(notifyId,builder.build());

參考:Android O(8.0)通知欄適配

ContentProvider

Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在針對 Android 8.0 的應用中的行為方式。現在,這些 API 需要在所有 URI 中為頒發機構定義一個有效的 ContentProvider。使用相關許可權定義一個有效的 ContentProvider 可幫助您的應用防範來自惡意應用的內容變更,並防止將可能的私密資料洩露給惡意應用。

官方的話,現象就是你registerContentProvider 或者 notifyChange 的時候如果沒有註冊明確的authorities的話那麼他就會拋一個SecurityException 的異常Failed to find provider null for user 0; expected to find a valid ContentProvider for this authority。主要也是防止其他APP監聽本APP的資料。

解決辦法就是在AndroidManifest裡去註冊一下Providor,並且記住在NotifyChange的時候URI一定要傳 content://authority/ authority別傳null

這裡寫圖片描述

借鑑 Android 8.0 java.lang.SecurityException: Failed to find provider for user 0 產生原因以及解決方法。

總結

比較重要的幾個就是移除隱式意圖,更換app logo圖,升級相關庫;如果有用到API 在上面有說明改變的也要注意以下,目前來看還沒有遇到別的坑。發現來再來補上!