- 投稿日:2019-07-30T21:34:53+09:00
AndroidのライブラリでminSdkVersionとtargetSdkVersionをライブラリで指定していない場合にアプリに勝手にパーミッションが追加される
概要
いろんな理由で社内などでライブラリを作ってmavenサーバーで公開して、それをアプリで参照したりとかってよくありますよね?
最初に結論ですが、そのときにライブラリ側でtargetSdkVersionが指定されていないと勝手にREAD_PHONE_STATE
とREAD_EXTERNAL_STORAGE
とWRITE_EXTERNAL_STORAGE
が追加されてしまいます
リリース前にGoogle Playで気づいたりすると再アップロードして確認などでリリースが翌日になったり、面倒なことになりがちです。(パーミッション変更がプルリクで検知できるともっといいですね)どう調べたか
通常、mafniestに追加されているPermissionはAndroidManifest.xmlにある
Merged Manfiest
という項目をみて、そこで右クリックしてGo to Declaration
することで、ライブラリを特定できます。しかし、今回はメインのManfiest.xmlに飛ばされるのみで、原因がわかりませんでした。
ドキュメントより、ビルド中に生成される
anifest-merger-[バリアント名]-report.txt
という項目を見れば良いことが分かったので、それを見てみました。
find . -name "manifest-merger*txt"
./app/build/outputs/logs/manifest-merger-[バリアント名]-report.txtそこで以下のように吐き出されていました。
reason: library has a targetSdkVersion < 4
IMPLIED from .../src/main/AndroidManifest.xml:2:1-550:12 reason: library has a targetSdkVersion < 4 uses-permission#android.permission.READ_PHONE_STATE IMPLIED from .../src/main/AndroidManifest.xml:2:1-550:12 reason: library has a targetSdkVersion < 4 uses-permission#android.permission.READ_EXTERNAL_STORAGE IMPLIED from .../src/main/AndroidManifest.xml:2:1-550:12 reason: library requested WRITE_EXTERNAL_STORAGEで以下のスタックオーバーフローにたどり着きました。
原因はなにか
ここからはちょっと興味の話なのですが、なぜtargetSdkVersion < 4だとパーミッションが追加されるのでしょうか?
ここにドキュメントがあります
https://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE
コードをたどっていくと、以下にたどり着きます。
https://android.googlesource.com/platform/tools/base/+/5521274ec220aea3f2b38d77dd34aeb463666da2When importing libraries targeting past versions of the SDK with some implied permissions (and possible other element types) into an application targeting a newer SDK, we must explicitely declare those implied permissions for the application to run correctly.正しくアプリが動くために、新しいOSでは明示的に指定する必要があるパーミッションを全部追加しちゃうっぽいです。
そのパーミッションを追加するmergerのコードはここにあるようです
https://android.googlesource.com/platform/tools/base/+/2343d80f0fed536331249d0190294de0a9660358/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
- 投稿日:2019-07-30T16:53:17+09:00
Androidアプリ開発の主要ライブラリ集
はじめに
サクッとアプリを書きたい時によく使うライブラリを使えるようになるまでをTips的にまとめときます。
いちいち検索するの面倒だからまとめた!ってだけなので、詳しく知りたい人はググって他の記事を見たほうがいいと思います。
Navigation
準備
appの
build.gradle
build.gradleimplementation "androidx.navigation:navigation-fragment-ktx:2.1.0-alpha06" implementation "androidx.navigation:navigation-ui-ktx:2.1.0-alpha06"
navigation
resourceファイルの作成
res/navigation/nav_graph.xml
つくるnav_graph.xml<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nav_graph" app:startDestination="@id/main_fragment"> <fragment android:id="@+id/main_fragment" android:name="<package>.MainFragment" android:label="fragment_main" tools:layout="@layout/fragment_main"/> </navigation>ActivityにFragment表示
activity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MainActivity"> <fragment android:id="@+id/nav_host_fragment" android:layout_height="match_parent" android:layout_width="match_parent" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/nav_graph" app:defaultNavHost="true" /> </androidx.constraintlayout.widget.ConstraintLayout>Tips
遷移するときはこう
Fragment.ktfindNavController().navigate(R.id.action_main_fragment_to_second_fragment)databinding
準備
appの
build.gradle
build.gradleandroid{ ... dataBinding { enabled = true } }レイアウト
<layout></layout>
で囲むfragment.xml<layout> // かっこいいレイアウト </layout>フラグメント
Fragment.ktval binding = inflate( inflater, R.layout.fragment_main, container, false ) as FragmentMainBindingRoom
準備
appの
build.gradle
build.gradleimplementation "androidx.room:room-runtime:2.2.0-alpha01" implementation "androidx.room:room-ktx:2.2.0-alpha01" annotationProcessor "androidx.room:room-compiler:2.2.0-alpha01" kapt "androidx.room:room-compiler:2.2.0-alpha01"保存するアイテムの
Entity
クラスの作成
data class
に@Entity
アノテーション付いただけです
@PrimaryKey
で主キーパラメータを指定できて、autoGenerate = true
オプションで主キーの値がデータベース上に重複してた場合は自動で値を付与してくれます。
ちなみに主キーは整数型限定っぽいです。ItemEntity.kt@Entity data class ItemEntity( @PrimaryKey(autoGenerate = true) val id: Int, val title: String, val content: String )
Data Access Objects
(DAO)クラスの作成
@Dao
アノテーション付けます。
名前の通りこのクラスを使ってデータにアクセスしていくので、適宜機能を増やしていけばいいと思います。ItemDao.kt@Dao interface ItemDao { @Insert suspend fun insert(entity: ItemEntity) @get:Query("SELECT * FROM ItemEntity") val all: List<ItemEntity> }
Database
クラスの作成
@Database
アノテーション付けます
このクラス内のitemDao()
から操作しますItemDatabase.kt@Database(entities = [ItemEntity::class], version = 1) abstract class ItemDatabase : RoomDatabase() { abstract fun itemDao(): ItemDao companion object { private var INSTANCE: ItemDatabase? = null fun getInstance(context: Context): ItemDatabase? { if (INSTANCE == null) { synchronized(ItemDatabase::class) { INSTANCE = Room.databaseBuilder( context.applicationContext, ItemDatabase::class.java, "item.db" ).build() } } return INSTANCE } fun destroyInstance() { INSTANCE = null } } }Tips
インスタンス化
Activity.ktval dao = ItemDatabase.getInstance(this)?.itemDao()挿入
autoGenerate = true
なので勝手にidを振ってくれますActivity.ktdao?.insert(ItemEntity(0, "title", "content"))読み込み
細かく読み込みたい場合は
ItemDao
に追記Activity.ktdao?.all?.let { it.forEach { item -> } }Koin
準備
appの
build.gradle
build.gradleimplementation 'org.koin:koin-android:1.0.2'
Application
クラスの作成例として
Hoge.kt
があるとしますApp.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.module ) ) } private val module = module { factory { //Injectionしたいクラスのインスタンス化(今回はHogeクラス) Hoge() } } }
Manifest
ファイルでapplicationを指定
application
タグのname
に先程作成したApplication
クラスを指定しますAndroidManifest.xml... <application android:name=".App" ...実際につかう
例えば上記
App.kt
でHoge
クラスのインスタンスをモジュール化していた場合は以下のように書くとインスタンスhoge
にinjectされますActivity.ktval hoge: Hoge by inject()Tips
複数のモジュールを使いたい
例えば
Huga.kt
もモジュール化したい時App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.hogeModule, this.hugaModule ) ) } private val hogeModule = module { factory { Hoge() } } // Hugaクラスをインスタンス化するモジュールを追加 private val hugaModule = module { factory { Huga() } } }又はこう
App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.module ) ) } private val module = module { factory { Hoge() } // このモジュール内でHugaクラスもインスタンス化する factory { Huga() } } }コンストラクタでinjectさせたい
例えば
Huga.kt
のコンストラクタでHoge.kt
のインスタンスを使いたい場合Huga.ktclass Huga(private val hoge:Hoge){ }App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.hogeModule, this.hugaModule ) ) } private val hogeModule = module { factory { Hoge() } } private val hugaModule = module { factory { // `get()`で自動injectされます Huga(get()) } } }
interface
をインスタンス化したいもし
Hoge.kt
がinterface
だった場合Hoge.ktinterface Hoge{ }HogeImpl.ktclass HogeImpl():Hoge{ }App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.hogeModule, this.hugaModule ) ) } private val hogeModule = module { factory { // `as ~~`で明記する HogeImpl() as Hoge } } }and more ... !
- 投稿日:2019-07-30T16:53:17+09:00
Androidことはじめ集
はじめに
サクッとアプリを書きたい時によく使うライブラリを使えるようになるまでをTips的にまとめときます。
いちいち検索するの面倒だからまとめた!ってだけなので、詳しく知りたい人はググって他の記事を見たほうがいいと思います。
Navigation
準備
appの
build.gradle
build.gradleimplementation "androidx.navigation:navigation-fragment-ktx:2.1.0-alpha06" implementation "androidx.navigation:navigation-ui-ktx:2.1.0-alpha06"
navigation
resourceファイルの作成
res/navigation/nav_graph.xml
つくるnav_graph.xml<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nav_graph" app:startDestination="@id/main_fragment"> <fragment android:id="@+id/main_fragment" android:name="<package>.MainFragment" android:label="fragment_main" tools:layout="@layout/fragment_main"/> </navigation>ActivityにFragment表示
activity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MainActivity"> <fragment android:id="@+id/nav_host_fragment" android:layout_height="match_parent" android:layout_width="match_parent" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/nav_graph" app:defaultNavHost="true" /> </androidx.constraintlayout.widget.ConstraintLayout>Tips
遷移したい
Fragment.ktfindNavController().navigate(R.id.action_main_fragment_to_second_fragment)親Activityから操作したい
Activity.kt// layoutのfragmentを指定 findNavController(R.id.nav_host_fragment).navigate(R.id.action_main_fragment_to_second_fragment)databinding
準備
appの
build.gradle
build.gradleandroid{ ... dataBinding { enabled = true } }レイアウト
<layout></layout>
で囲むfragment.xml<layout> // かっこいいレイアウト </layout>インスタンス化
Fragment
Fragment.ktval binding = inflate( inflater, R.layout.fragment_main, container, false ) as FragmentMainBindingActivity
Activity.ktval binding = DataBindingUtil.setContentView(this, R.layout.activity_main) as ActivityMainBindingRoom
準備
appの
build.gradle
build.gradleimplementation "androidx.room:room-runtime:2.2.0-alpha01" implementation "androidx.room:room-ktx:2.2.0-alpha01" annotationProcessor "androidx.room:room-compiler:2.2.0-alpha01" kapt "androidx.room:room-compiler:2.2.0-alpha01"保存するアイテムの
Entity
クラスの作成
data class
に@Entity
アノテーション付いただけです
@PrimaryKey
で主キーパラメータを指定できて、autoGenerate = true
オプションで主キーの値がデータベース上に重複してた場合は自動で値を付与してくれます。
ちなみに主キーは整数型限定っぽいです。ItemEntity.kt@Entity data class ItemEntity( @PrimaryKey(autoGenerate = true) val id: Int, val title: String, val content: String )
Data Access Objects
(DAO)クラスの作成
@Dao
アノテーション付けます。
名前の通りこのクラスを使ってデータにアクセスしていくので、適宜機能を増やしていけばいいと思います。ItemDao.kt@Dao interface ItemDao { @Insert suspend fun insert(entity: ItemEntity) @get:Query("SELECT * FROM ItemEntity") val all: List<ItemEntity> }
Database
クラスの作成
@Database
アノテーション付けます
このクラス内のitemDao()
から操作しますItemDatabase.kt@Database(entities = [ItemEntity::class], version = 1) abstract class ItemDatabase : RoomDatabase() { abstract fun itemDao(): ItemDao companion object { private var INSTANCE: ItemDatabase? = null fun getInstance(context: Context): ItemDatabase? { if (INSTANCE == null) { synchronized(ItemDatabase::class) { INSTANCE = Room.databaseBuilder( context.applicationContext, ItemDatabase::class.java, "item.db" ).build() } } return INSTANCE } fun destroyInstance() { INSTANCE = null } } }Tips
インスタンス化
Activity.ktval dao = ItemDatabase.getInstance(this)?.itemDao()挿入
autoGenerate = true
なので勝手にidを振ってくれますActivity.ktdao?.insert(ItemEntity(0, "title", "content"))読み込み
細かく読み込みたい場合は
ItemDao
に追記Activity.ktdao?.all?.let { it.forEach { item -> } }Koin
準備
appの
build.gradle
build.gradleimplementation 'org.koin:koin-android:1.0.2'
Application
クラスの作成例として
Hoge.kt
があるとしますApp.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.module ) ) } private val module = module { factory { //Injectionしたいクラスのインスタンス化(今回はHogeクラス) Hoge() } } }
Manifest
ファイルでapplicationを指定
application
タグのname
に先程作成したApplication
クラスを指定しますAndroidManifest.xml... <application android:name=".App" ...実際につかう
例えば上記
App.kt
でHoge
クラスのインスタンスをモジュール化していた場合は以下のように書くとインスタンスhoge
にinjectされますActivity.ktval hoge: Hoge by inject()Tips
複数のモジュールを使いたい
例えば
Huga.kt
もモジュール化したい時App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.hogeModule, this.hugaModule ) ) } private val hogeModule = module { factory { Hoge() } } // Hugaクラスをインスタンス化するモジュールを追加 private val hugaModule = module { factory { Huga() } } }又はこう
App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.module ) ) } private val module = module { factory { Hoge() } // このモジュール内でHugaクラスもインスタンス化する factory { Huga() } } }コンストラクタでinjectさせたい
例えば
Huga.kt
のコンストラクタでHoge.kt
のインスタンスを使いたい場合Huga.ktclass Huga(private val hoge:Hoge){ }App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.hogeModule, this.hugaModule ) ) } private val hogeModule = module { factory { Hoge() } } private val hugaModule = module { factory { // `get()`で自動injectされます Huga(get()) } } }
interface
をインスタンス化したいもし
Hoge.kt
がinterface
だった場合Hoge.ktinterface Hoge{ }HogeImpl.ktclass HogeImpl():Hoge{ }App.ktclass App : Application() { override fun onCreate() { super.onCreate() startKoin( this, listOf( this.hogeModule, this.hugaModule ) ) } private val hogeModule = module { factory { // `as ~~`で明記する HogeImpl() as Hoge } } }and more ... !
- 投稿日:2019-07-30T08:58:55+09:00
【Programming News】Qiitaまとめ記事 July 29, 2019 Vol.15
筆者が2019/7/29(月)に気になったQiitaの記事をまとめました。昨日のまとめ記事はこちら。
2019/7/21(日)~2019/7/27(土)のWeeklyのまとめのまとめ記事もこちらで公開しております。
Java
Python
- Tips
- Apps
Rails
- Tips
Vue.js
Android
- Tips
Swift
- Beginner
- Tips
Kotlin
- Beginner
- Tips
Flutter
JavaScript
- Beginner
- Tips
Node.js
React
Laravel
Keras
- Beginner
PowerShel
Spark
R言語
MySQL
Azure
AWS
- EC2
- AWS Lambda
- Tips
- AWS CodeStar
Docker
- Tips
- DockerコンテナのロギングにGrafana+Lokiを試してみた
- 最短2文でLibraのテストネットを立ち上げるDockerイメージ作りました
- もっと簡単にDockerでNuxt.jsを始めてみる(続Dockerでローカル環境を汚さずにNuxt.jsを始めてみる)
- Hugo を Docker 上で動作させる
- Nginx版:mkcertを使ってローカル環境でもDockerでも楽々SSL
- Vagrant環境のDockerでJenkinsサーバを構築し、スレーブのWindowsマシンを接続する(前半)
- Vagrant環境のDockerでJenkinsサーバを構築し、スレーブのWindowsマシンを接続する(後半)
- DockerでNuxt.js on TypeScriptを始めてみよう
- DockerでRailsの環境構築
TypeScript
Google Apps Script
機械学習
- Beginner
Raspberry
- Tips
Develop
- Tips
- Meetup
Intellij IDEA
更新情報
Kotlin
- Kotlin入門
Android
Java
IDE
- 投稿日:2019-07-30T01:41:23+09:00
APP 基本架構
佈局
Android Studio 使用
XML Tutorial範例
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" android:gravity="center" xmlns:android="http://schemas.android.com/apk/res/android" > 將要放的佈局物件放在LinearLayout裡 . . .
- layout_height 與 android:layout_width 是基本設定,如果不宣告便會出錯。
- orientation 用來設定排列方
- gravity 用來放置的位置(置中、置左...)
佈局物件範例
<EditText android:id="@+id/et_name" android:layout_width="200dp" android:layout_height="wrap_content" /> <Button android:id="@+id/btn_enter" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="push it" android:textSize="20dp"/> <TextView android:id="@+id/tv_s1" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="heheXD" android:textSize="20dp"/>
- EditText : 可編輯文字方塊
- Button : 按鈕
- TextView : 文字
- id 是用來宣告這個物件的名稱,可在主程式內呼叫與操作。
MainActivity
從畫面得到物件,並轉換成該物件型態
public class MainActivity extends AppCompatActivity { private EditText et_name; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);//該畫面中的布局 et_name = (EditText)findViewById(R.id.et_name);//物件型態轉換 Toast.makeText(MainActivity.this,"successful",Toast.LENGTH_LONG).show();//產生成功提示 } }TAG 用法
- 可以用來顯示於debug視窗,方便偵錯。
private final String TAG = MainActivity.class.getSimpleName(); Log.i(TAG, put some to debug );建立物件
ListView 使用
參考資料
透過setContentView轉換layout
Android Context完全解析
Android入門基礎:從這裡開始
Android 學習筆記 - 建立簡單的 ListView