ユーザ用ツール

サイト用ツール


サイドバー

android:androidstudio2で始めるアプリ開発入門

以前のリビジョンの文書です


androidstudio2で始めるアプリ開発入門

第7回.Androidアプリの画面遷移 ~ アクティビティの起動をつかさどるIntentクラス

SimpleAdapter

        var list:MutableList<Map<String,String>> = mutableListOf()
        var map:Map<String,String> = mapOf("name" to "お味噌汁" ,"price" to "100")
 
        list.add(map)
        map= mapOf("name" to "お寿司","price" to "2000")
        list.add(map)
        map= mapOf("name" to "カレー","price" to "600")
        list.add(map)
 
        val from:Array<String> = arrayOf("name","price")
        val to:IntArray = intArrayOf(android.R.id.text1,android.R.id.text2 )
        val adpter:SimpleAdapter= SimpleAdapter(this  ,list,android.R.layout.simple_expandable_list_item_2,from,to)
        list1.adapter=adpter

アクティビティの起動とIntent

        list1.setOnItemClickListener { adapterView, view, i, l ->
            val item:Map<String,String> = adapterView.getItemAtPosition(i) as Map<String, String>
            var intent:Intent=Intent(this,ThanksActivity::class.java) //javaのクラスはこのようにかく
            intent.putExtra("menuName",item["name"])
            intent.putExtra("menuPrice",item["price"])
            startActivity(intent)
        }

        val intent = getIntent()
        menu.text="あなたの注文は ${intent.getStringExtra("menuName")} で ${intent.getStringExtra("menuPrice")} 円です!"

intentの中身の確認

        val intent = getIntent()
        val ban:Bundle?=intent.extras
        for (k in ban?.keySet()!!){
            Log.d("TagName", "key=${k} val=${ban[k] as String}")
        }

第8回.Androidアプリのメニュー ~ オプションメニューとコンテキストメニュー

「戻る」メニュー

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //この1行追加
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

    }
    
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId
        if(id == android.R.id.home) finish()
        return super.onOptionsItemSelected(item)
    }

第11回.Androidアプリでの非同期処理とWeb API連携

UIスレッド(mainスレッド)へのアクセス

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.nekotype.ips.api, PID: 22804
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    
 ↓
・・・
        GlobalScope.launch {

            val apidata = URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010").readText()
            async(Dispatchers.Main) { //UIスレッドでコルーチンを実行する
                text.text=apidata
            }
・・・

HTTPアクセスのパーミッション

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.nekotype.ips.api, PID: 22981
    java.io.IOException: Cleartext HTTP traffic to weather.livedoor.com not permitted
   

step1:xmlフォルダを作り、network_security_config.xmlを作成する。

res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">livedoor.com</domain>
    </domain-config>
</network-security-config>

AndroidManifest.xmlに作成したnetwork_security_configへの参照を追加する。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.nekotype.ips.api">
 
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:networkSecurityConfig="@xml/network_security_config"> //←ここ
 
        <activity android:name=".ResultActivity"></activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

HTTPSの証明書エラー

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.nekotype.ips.api, PID: 23831
    javax.net.ssl.SSLPeerUnverifiedException: Hostname weather.livedoor.com not verified:

証明書エラーを無視するようにしてみたが、なぜか301が帰ってきてしまう。

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>

一応やってみた2パターンの対応

証明証を無視するパターン1

    fun myTask(id:String){

        GlobalScope.launch {

            disableSSLCertificateChecking() //証明書の無効処理呼び出し

            val urlStr = "https://weather.livedoor.com/forecast/webservice/json/v1?city=${id}"
            //val urlStr = "https://chargen-api.herokuapp.com/"
            //val urlStr = "https://www.google.co.jp/"
            val url= URL(urlStr)
            val con=url.openConnection() as HttpsURLConnection

            con.requestMethod="GET"
            con.connect()

            val stream=con.getInputStream()
            val response=streamToString(stream)
            async(Dispatchers.Main) {
                text.text=response
            }
        }
    }


    private fun streamToString(stream: InputStream?): String {
        val reader=BufferedReader(InputStreamReader(stream,"UTF-8"))
        val sb=StringBuilder()
        val b = CharArray(10240)
        var line:Int
        val result = reader.read(b).let { sb.append(b,0,it) }
        return result.toString()
    }


    // 証明書の無効処理
    @Throws(Exception::class)
    fun disableSSLCertificateChecking() {
        println("[WARN] *** SSLCertificate Checking DISABLED ***")

        // ホスト名の検証を行わない
        val hv = HostnameVerifier { s, ses ->
            println("[WARN] *** HostnameVerifier DISABLED *** ")
            true
        }
        HttpsURLConnection.setDefaultHostnameVerifier(hv)
        // 証明書の検証を行わない
        val km: Array<KeyManager>? = null
        val tm = arrayOf<TrustManager>(object : X509TrustManager {
            @Throws(CertificateException::class)
            override fun checkClientTrusted(arg0: Array<X509Certificate>, arg1: String) {
            }

            @Throws(CertificateException::class)
            override fun checkServerTrusted(arg0: Array<X509Certificate>, arg1: String) {
            }

            override fun getAcceptedIssuers(): Array<X509Certificate>? {
                return null
            }
        })
        val sslcontext = SSLContext.getInstance("SSL")
        sslcontext.init(km, tm, SecureRandom())
        HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.socketFactory)
    }
          

証明書を無視するパターン2

色々試しているとそもそも動かなくなった…

            // ↓
            val tm = arrayOf<TrustManager>(object : X509TrustManager {
                override fun getAcceptedIssuers(): Array<X509Certificate>? {
                    return null
                }
                override fun checkClientTrusted(xc: Array<X509Certificate>, type: String) {}
                override fun checkServerTrusted(xc: Array<X509Certificate>, type: String) {}
            })
            val ctx = SSLContext.getInstance("SSL")
            ctx.init(null, tm, SecureRandom())
            // ↑

            val urlStr = "https://weather.livedoor.com/forecast/webservice/json/v1?city=${id}"
            
            val url= URL(urlStr)
            val con=url.openConnection() as HttpsURLConnection

            // ↓
            con.doOutput = true
            con.sslSocketFactory=ctx.socketFactory
            // ↑
            
            con.requestMethod="GET"
            con.connect()

BufferedReaderのread

        val reader=BufferedReader(InputStreamReader(stream,"UTF-8"))
        val sb=StringBuilder()
        val b = CharArray(10240)
        var line:Int

// javaの場合は下。これはそのまま流用できな。
//        while(0 <= {line = reader.read(b)}) {
//            sb.append(b, 0, line);
//        }

        val result = reader.read(b).let { sb.append(b,0,it) }
        return result.toString()

BufferedReaderが長すぎて読み込めない

read(b)をreadline()に修正

    private fun streamToString(stream: InputStream?): String {
        val reader=BufferedReader(InputStreamReader(stream,"UTF-8"))
        val sb=StringBuilder()
           var line:Int
        val result = reader.readLine().let { sb.append(it) }
        return result.toString()
    }

詳細が画面に収まらないためスクロールさせる

   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:gravity="center"
            android:text="@string/tv_winfo_title"
            android:textSize="25sp"/>
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:orientation="horizontal">
 
            <TextView
                android:id="@+id/tvCityName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp"/>
 
            <TextView
                android:id="@+id/tvWeatherTelop"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:layout_weight="1"/>
 
        </LinearLayout>
 
<!--TextViewをScrollViewで囲む-->
 
        <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
 
        <TextView
            android:id="@+id/tvWeatherDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="30dp"
            android:textSize="15sp"/>
        </ScrollView>
    </LinearLayout>
android/androidstudio2で始めるアプリ開発入門.1570920480.txt.gz · 最終更新: 2019/10/13 07:48 by ips