20190730のAndroidに関する記事は5件です。

AndroidのライブラリでminSdkVersionとtargetSdkVersionをライブラリで指定していない場合にアプリに勝手にパーミッションが追加される

概要

いろんな理由で社内などでライブラリを作ってmavenサーバーで公開して、それをアプリで参照したりとかってよくありますよね?
最初に結論ですが、そのときにライブラリ側でtargetSdkVersionが指定されていないと勝手にREAD_PHONE_STATEREAD_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGEが追加されてしまいます
リリース前にGoogle Playで気づいたりすると再アップロードして確認などでリリースが翌日になったり、面倒なことになりがちです。(パーミッション変更がプルリクで検知できるともっといいですね)

どう調べたか

通常、mafniestに追加されているPermissionはAndroidManifest.xmlにあるMerged Manfiestという項目をみて、そこで右クリックして Go to Declarationすることで、ライブラリを特定できます。

image.png

しかし、今回はメインの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

で以下のスタックオーバーフローにたどり着きました。

https://stackoverflow.com/questions/27792660/google-play-detecting-new-permissions-after-using-android-studio

原因はなにか

ここからはちょっと興味の話なのですが、なぜtargetSdkVersion < 4だとパーミッションが追加されるのでしょうか?
ここにドキュメントがあります
https://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE
image.png

コードをたどっていくと、以下にたどり着きます。
https://android.googlesource.com/platform/tools/base/+/5521274ec220aea3f2b38d77dd34aeb463666da2

When 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidアプリ開発の主要ライブラリ集

はじめに

サクッとアプリを書きたい時によく使うライブラリを使えるようになるまでをTips的にまとめときます。

いちいち検索するの面倒だからまとめた!ってだけなので、詳しく知りたい人はググって他の記事を見たほうがいいと思います。

Navigation

準備

appのbuild.gradle

build.gradle
implementation "androidx.navigation:navigation-fragment-ktx:2.1.0-alpha06"
implementation "androidx.navigation:navigation-ui-ktx:2.1.0-alpha06"

navigationresourceファイルの作成

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.kt
findNavController().navigate(R.id.action_main_fragment_to_second_fragment)

databinding

準備

appのbuild.gradle

build.gradle
android{
  ...
  dataBinding {
    enabled = true
  }
}

レイアウト

<layout></layout>で囲む

fragment.xml
<layout>

// かっこいいレイアウト

</layout>

フラグメント

Fragment.kt
val binding = inflate(
            inflater,
            R.layout.fragment_main,
            container,
            false
        ) as FragmentMainBinding

Room

準備

appのbuild.gradle

build.gradle
implementation "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.kt
val dao = ItemDatabase.getInstance(this)?.itemDao()

挿入

autoGenerate = trueなので勝手にidを振ってくれます

Activity.kt
dao?.insert(ItemEntity(0, "title", "content"))

読み込み

細かく読み込みたい場合はItemDaoに追記

Activity.kt
dao?.all?.let {
    it.forEach { item ->

    }
}

Koin

準備

appのbuild.gradle

build.gradle
implementation 'org.koin:koin-android:1.0.2'

Applicationクラスの作成

例としてHoge.ktがあるとします

App.kt
class 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.ktHogeクラスのインスタンスをモジュール化していた場合は以下のように書くとインスタンスhogeにinjectされます

Activity.kt
val hoge: Hoge by inject()

Tips

複数のモジュールを使いたい

例えばHuga.ktもモジュール化したい時

App.kt
class 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.kt
class 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.kt
class Huga(private val hoge:Hoge){

}
App.kt
class 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.ktinterfaceだった場合

Hoge.kt
interface Hoge{

}
HogeImpl.kt
class HogeImpl():Hoge{

}
App.kt
class 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 ... !

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidことはじめ集

はじめに

サクッとアプリを書きたい時によく使うライブラリを使えるようになるまでをTips的にまとめときます。

いちいち検索するの面倒だからまとめた!ってだけなので、詳しく知りたい人はググって他の記事を見たほうがいいと思います。

Navigation

準備

appのbuild.gradle

build.gradle
implementation "androidx.navigation:navigation-fragment-ktx:2.1.0-alpha06"
implementation "androidx.navigation:navigation-ui-ktx:2.1.0-alpha06"

navigationresourceファイルの作成

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.kt
findNavController().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.gradle
android{
  ...
  dataBinding {
    enabled = true
  }
}

レイアウト

<layout></layout>で囲む

fragment.xml
<layout>

// かっこいいレイアウト

</layout>

インスタンス化

Fragment

Fragment.kt
val binding = inflate(
            inflater,
            R.layout.fragment_main,
            container,
            false
        ) as FragmentMainBinding

Activity

Activity.kt
val binding = DataBindingUtil.setContentView(this, R.layout.activity_main) as ActivityMainBinding

Room

準備

appのbuild.gradle

build.gradle
implementation "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.kt
val dao = ItemDatabase.getInstance(this)?.itemDao()

挿入

autoGenerate = trueなので勝手にidを振ってくれます

Activity.kt
dao?.insert(ItemEntity(0, "title", "content"))

読み込み

細かく読み込みたい場合はItemDaoに追記

Activity.kt
dao?.all?.let {
    it.forEach { item ->

    }
}

Koin

準備

appのbuild.gradle

build.gradle
implementation 'org.koin:koin-android:1.0.2'

Applicationクラスの作成

例としてHoge.ktがあるとします

App.kt
class 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.ktHogeクラスのインスタンスをモジュール化していた場合は以下のように書くとインスタンスhogeにinjectされます

Activity.kt
val hoge: Hoge by inject()

Tips

複数のモジュールを使いたい

例えばHuga.ktもモジュール化したい時

App.kt
class 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.kt
class 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.kt
class Huga(private val hoge:Hoge){

}
App.kt
class 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.ktinterfaceだった場合

Hoge.kt
interface Hoge{

}
HogeImpl.kt
class HogeImpl():Hoge{

}
App.kt
class 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 ... !

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Programming News】Qiitaまとめ記事 July 29, 2019 Vol.15

筆者が2019/7/29(月)に気になったQiitaの記事をまとめました。昨日のまとめ記事はこちら

2019/7/21(日)~2019/7/27(土)のWeeklyのまとめのまとめ記事もこちらで公開しております。

Java

Python

Rails

Vue.js

Android

Swift

Kotlin

Flutter

JavaScript

Node.js

React

Laravel

Keras

PowerShel

Spark

R言語

MySQL

Azure

AWS

Docker

TypeScript

Google Apps Script

機械学習

Raspberry

Develop

Intellij IDEA

更新情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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_heightandroid: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 );

建立物件

  • 點選右鍵 app->java->new->java class

  • 編輯畫面中右鍵使用generate 幫助跨快速建構function 如: 建構子 set & get

ListView 使用

Android 學習筆記 - 建立簡單的 ListView

參考資料

透過setContentView轉換layout
Android Context完全解析
Android入門基礎:從這裡開始
Android 學習筆記 - 建立簡單的 ListView

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む