SlideShare a Scribd company logo
1 of 56
Download to read offline
新しくなった画面間の
データ連携を知ろう
About Me
早川 勉(sokume)
stmn.inc/GDG Nagoya organizer
1. Activity データ連携、これまでとこれから
2. Fragment データ連携、これまでとこれから
3. まとめ
新しくなった画面間のデータ連携を知ろう
“このセッションで取り上げている内容には
一部 alpha版、beta版の機能が含まれてお
り、今後変更されていく可能性がありま
す。”
【調査環境】
Android Studio 4.0.2
【追加ライブラリ】
"androidx.activity:activity-ktx:1.2.0-beta01"
"androidx.fragment:fragment-ktx:1.3.0-beta01"
Activity データ連携、
これまでとこれから
● UIや画面を持つコンポーネントの一つ
● ライフサイクルを持っている
● 他のActivity と連携する事が可能
など
Activity とは?
● 暗黙的Intent 連携
● 明示的Intent 連携
連携の種類
● リクエストを元に、対応できるActivityを呼び出す
○ 電話をする、地図を表示する、画像を取得するなど
● 呼び出したActivityがデータを送ってくれたり処理
したりしてくれる
● 主に別のプロジェクトに含まれるActivityとの連携
に使われる
暗黙的Intent 連携
● リクエストを送る先を明確にしていしActivityを呼
び出す
● 呼び出したActivityがデータを送ってくれたり処理
したりしてくれる
● 主に、自分のプロジェクトに含まれるActivityとの
連携に使われる
明示的Intent 連携
1. リクエストを作る
2. 起動する
3. 結果を取得する
これまでの暗黙的Intentの連携処理
リクエストをつくる
// 画像を取得
val nextIntent =
Intent(Intent.ACTION_GET_CONTENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("image/*")
起動する
// 起動処理
startActivityForResult(nextIntent, REQUEST_CODE)
結果を取得する
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}else if (resultCode == RESULT_CANCELED){
// キャンセルしたとき
}
}
}
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton.setOnClickListener {
val nextIntent = Intent(Intent.ACTION_GET_CONTENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("image/*")
startActivityForResult(nextIntent, REQUEST_CODE)
}
}
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}else if (resultCode == RESULT_CANCELED){
// キャンセルしたとき
}
}
}
・・・//省略
}
ボタン押下
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton.setOnClickListener {
val nextIntent = Intent(Intent.ACTION_GET_CONTENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("image/*")
startActivityForResult(nextIntent, REQUEST_CODE)
}
}
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}else if (resultCode == RESULT_CANCELED){
// キャンセルしたとき
}
}
}
・・・//省略
}
ボタン押下
1. リクエストを作る
2. 起動する
3. 結果を取得する
これまでの明示的Intent処理
リクエストをつくる
// MainActivity2を呼び出すリクエスト
val nextIntent =
Intent(this, MainActivity2::class.java)
起動する
// 起動処理
startActivityForResult(nextIntent, REQUEST_CODE)
結果を取得する
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}else if (resultCode == RESULT_CANCELED){
// キャンセルしたとき
}
}
}
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton.setOnClickListener {
val nextIntent = Intent(this, MainActivity2::class.java)
startActivityForResult(nextIntent, REQUEST_CODE)
}
}
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}else if (resultCode == RESULT_CANCELED){
// キャンセルしたとき
}
}
}
・・・//省略
}
ボタン押下
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton.setOnClickListener {
val nextIntent = Intent(applicationContext, MainActivity2::class.java)
startActivityForResult(nextIntent, REQUEST_CODE)
}
}
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}else if (resultCode == RESULT_CANCELED){
// キャンセルしたとき
}
}
}
・・・//省略
}
ボタン押下
● アクションが増えると結果を受ける処理が肥大化しや
すい😨
● Int型の数値で呼び出し元を判定するので重複しそう
なところが嫌だったり😟
これまでの実装で悩ましいポイント
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
     ・・・
nextButton1.setOnClickListener {
val nextIntent = Intent(applicationContext, MainActivity2::class.java)
startActivityForResult(nextIntent1, REQUEST_CODE1)
}
nextButton2.setOnClickListener {
val nextIntent = Intent(applicationContext, MainActivity3::class.java)
startActivityForResult(nextIntent2, REQUEST_CODE2)
}
}
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE1){
// 連携結果を元に処理を実施する部分
・・・//省略
}
     if (requestCode == REQUEST_CODE2){
・・・//省略
}
}
companion object{
private const val REQUEST_CODE1 = 100
private const val REQUEST_CODE2 = 101
}
}
ボタン押下1
ボタン押下2
1. 結果を取得する為の登録を行う
2. 起動する
これからの暗黙的Intentの処理
結果を取得する為の登録を行う
// 結果取得の登録
val registerResult =
registerForActivityResult
(ActivityResultContracts.GetContent()) { uri: Uri? →
//結果を取得した処理
}
起動する
// 起動処理
registerResult.launch("image/*")
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val registerResult =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? →
Log.d("result", "activity: $uri")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton.setOnClickListener {
registerResult.launch("image/*")
}
}
・・・//省略
}
ボタン押下
1. データ連携クラスを作る(呼び出すクラス)
2. 結果を取得する為の登録を行う
3. 起動する
これからの明示的Intentの処理
データ連携クラスを作る
// MainActivity2のデータ連携クラス
class Main2ActivityResultContract : ActivityResultContract<Int,Main2Result?>()
{
override fun createIntent(context: Context, input: Int): Intent {
return Intent(context, MainActivity2::class.java).apply {
putExtra(ID_KEY, input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Main2Result? {
if (resultCode == RESULT_OK){
return intent?.run {
Main2Result(
getIntExtra(ID_KEY,0),
getStringExtra(NAME_KEY) ?: "")
}
}
return null
}
・・・省略
}
データ連携クラスを作る
// MainActivity2のデータ連携クラス
class Main2ActivityResultContract : ActivityResultContract<Int,Main2Result?>()
{
override fun createIntent(context: Context, input: Int): Intent {
return Intent(context, MainActivity2::class.java).apply {
putExtra(ID_KEY, input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Main2Result? {
if (resultCode == RESULT_OK){
return intent?.run {
Main2Result(
getIntExtra(ID_KEY,0),
getStringExtra(NAME_KEY) ?: "")
}
}
return null
}
・・・省略
}
データ連携クラスを作る
// MainActivity2のデータ連携クラス
class Main2ActivityResultContract : ActivityResultContract<Int,Main2Result?>()
{
override fun createIntent(context: Context, input: Int): Intent {
return Intent(context, MainActivity2::class.java).apply {
putExtra(ID_KEY, input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Main2Result? {
if (resultCode == RESULT_OK){
return intent?.run {
Main2Result(
getIntExtra(ID_KEY,0),
getStringExtra(NAME_KEY) ?: "")
}
}
return null
}
・・・省略
}
結果を取得する為の登録を行う
// 結果取得の登録
val registerResult =
registerForActivityResult(Main2ActivityResultContract())) {
result: Main2Result? →
// 結果を取得した処理
}
起動する
// 起動処理
registerResult.launch(12345)
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val registerResult =
registerForActivityResult(Main2ActivityResultContract()) { result: Main2Result? →
Log.d("result", "activity: $result")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton.setOnClickListener {
registerResult.launch(12345)
}
}
・・・//省略
}
ボタン押下
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val registerResultM2 =
registerForActivityResult(Main2ActivityResultContract()) { result: Main2Result? →
Log.d("result", "activity: $result")
}
private val registerResultM3 =
registerForActivityResult(Main3ActivityResultContract()) { result: Main3Result? →
Log.d("result", "activity: $result")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nextButton1.setOnClickListener {
registerResultM2.launch(12345)
}
nextButton2.setOnClickListener {
registerResultM3.launch(12345)
}
}
・・・//省略
}
ボタン押下
ボタン押下
● Activityを使われるクラスで、
ActivityResultContractを利用し
インプット(入力値)、アウトプット(結果)を
明確に指定できる。
● 呼び出す側の結果取得処理の肥大化を防げる
● 呼び出す側はデータの不一致などを気にする必要が減
る。
point
Fragmentのデータ連携
のこれまでとこれから
● UIを持つコンポーネントの一つ
● ライフサイクルを持っている
● 同じFragmentManagerに属するFragment と連携
する事が可能
など
Fragment とは?
1. 次のFragmentを作る
2. FragmentManagerに登録する
3. 結果を取得する
これまでのFragmentの連携処理
次のFragmentを作る
val nextFragment =
NextFragment.newInstance("param_n_1","param_n_2")
nextFragment.setTargetFragment(this,REQUEST_CODE)
FragmentManagerに登録する
requireActivity()
.supportFragmentManager
.beginTransaction()
.replace(R.id.content,nextFragment)
.addToBackStack(null)
.commit()
結果を取得する
override fun onActivityResult
(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == AppCompatActivity.RESULT_OK){
// 処理がOKのとき
}
}
}
class TopFragment : Fragment() {
・・・//省略
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
nextButton.setOnClickListener {
val nextFragment = NextFragment.newInstance("param_n_1","param_n_2")
nextFragment.setTargetFragment(this,REQUEST_CODE)
requireActivity()
.supportFragmentManager
.beginTransaction()
.replace(R.id.content,nextFragment)
.addToBackStack(null)
.commit()
}
}
// 結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE){
// 連携結果を元に処理を実施する部分
if (resultCode == RESULT_OK){
// 処理がOKのとき
}
}
}
・・・//省略
}
ボタン押下
結果を返却する(呼び出し先アプリ)
targetFragment?.apply {
val intent = Intent()
intent.putExtra(Intent.EXTRA_TEXT, "fragment")
onActivityResult(targetRequestCode, RESULT_OK, intent)
}
requireActivity()
.supportFragmentManager.run {
popBackStack()
}
1. 次のFragmentを作る
2. 結果の取得先を登録する
3. FragmentManagerに登録する
これからのFragmentの連携処理
次のFragmentを作る
val nextFragment =
NextFragment.newInstance("param_n_1","param_n_2")
結果の取得先を登録する
setFragmentResultListener(REQUEST_KEY) { key, data →
// 結果の受信処理
if (key == REQUEST_KEY){
}
}
FragmentManagerに登録する
requireActivity()
.supportFragmentManager
.beginTransaction()
.replace(R.id.content,nextFragment)
.addToBackStack(null)
.commit()
class TopFragment : Fragment() {
・・・//省略
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
nextButton.setOnClickListener {
val nextFragment = NextFragment.newInstance("param_n_1","param_n_2")
setFragmentResultListener(REQUEST_KEY) { key, data →
// 結果の受信処理
if (key == REQUEST_KEY){
}
}
requireActivity()
.supportFragmentManager
.beginTransaction()
.replace(R.id.content,nextFragment)
.addToBackStack(null)
.commit()
}
}
・・・//省略
}
ボタン押下
結果を返却する(呼び出し先アプリ)
val bundle = Bundle().apply {
putString(Intent.EXTRA_TEXT, "fragment")
}
requireActivity()
.supportFragmentManager.run {
setFragmentResult(REQUEST_KEY,bundle)
popBackStack()
}
● FragmentManagerが結果の返却を管理する
● ライフサイクルに合わせてデータの返却をしてくれる
● FragmentなのにonActivityResult()を利用する
必要がなくなった
point
まとめ
● ActivityとFragmentのデータ連携は新しい方法
がきっとスタンダートになるだろうから、積極的に
使っていくと良さそう。
● まだ検索するとこれまでの実装方法がたくさんでて
くるから、古い方法も一応知っておこうと良いと思
う。
まとめ
● https://developer.android.com/training
/basics/intents/result
↑日本語ページは内容が古いのでご注意を😅
● https://developer.android.com/training/basics/fragme
nts/pass-data-between
参考URL
“引き続き GDG DevFest 2020をお
楽しみください🎉”

More Related Content

Similar to GDG DevFest 2020 Android data linkage info

Tokyo GTUG Bootcamp2010
Tokyo GTUG Bootcamp2010Tokyo GTUG Bootcamp2010
Tokyo GTUG Bootcamp2010Takashi EGAWA
 
iTamabi 13 第7回:ARTSAT API 実践 2 衛星の情報で表現する
iTamabi 13  第7回:ARTSAT API 実践 2 衛星の情報で表現するiTamabi 13  第7回:ARTSAT API 実践 2 衛星の情報で表現する
iTamabi 13 第7回:ARTSAT API 実践 2 衛星の情報で表現するAtsushi Tadokoro
 
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
Next2Dで始めるゲーム開発  - Game Development Starting with Next2DNext2Dで始めるゲーム開発  - Game Development Starting with Next2D
Next2Dで始めるゲーム開発 - Game Development Starting with Next2DToshiyuki Ienaga
 
Node.js - JavaScript Thread Programming
Node.js - JavaScript Thread ProgrammingNode.js - JavaScript Thread Programming
Node.js - JavaScript Thread Programmingtakesako
 
Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Yuji Takayama
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Yoshifumi Kawai
 
Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Yuki Higuchi
 
Mashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSource
Mashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSourceMashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSource
Mashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSourcecmutoh
 
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...Shotaro Suzuki
 
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)Fujio Kojima
 
Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)Akihiro Iwaya
 
初めての Data api
初めての Data api初めての Data api
初めての Data apiYuji Takayama
 
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Windows azure mobile services による mobile + cloud アプリケーション超高速開発Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Windows azure mobile services による mobile + cloud アプリケーション超高速開発Shotaro Suzuki
 
仕事で使うちょっとしたコードをOSSとして開発メンテしていく - Django Redshift Backend の開発 - PyCon JP 2016
仕事で使うちょっとしたコードをOSSとして開発メンテしていく- Django Redshift Backend の開発 - PyCon JP 2016仕事で使うちょっとしたコードをOSSとして開発メンテしていく- Django Redshift Backend の開発 - PyCon JP 2016
仕事で使うちょっとしたコードをOSSとして開発メンテしていく - Django Redshift Backend の開発 - PyCon JP 2016Takayuki Shimizukawa
 
【ネイティブアドを支えるPhantomJS】
【ネイティブアドを支えるPhantomJS】【ネイティブアドを支えるPhantomJS】
【ネイティブアドを支えるPhantomJS】Kengo Shimada
 
Google I/O 2016 報告会
Google I/O 2016 報告会Google I/O 2016 報告会
Google I/O 2016 報告会shingo suzuki
 
Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Yuki Higuchi
 

Similar to GDG DevFest 2020 Android data linkage info (20)

Tokyo GTUG Bootcamp2010
Tokyo GTUG Bootcamp2010Tokyo GTUG Bootcamp2010
Tokyo GTUG Bootcamp2010
 
Jetpack Workshop
Jetpack WorkshopJetpack Workshop
Jetpack Workshop
 
iTamabi 13 第7回:ARTSAT API 実践 2 衛星の情報で表現する
iTamabi 13  第7回:ARTSAT API 実践 2 衛星の情報で表現するiTamabi 13  第7回:ARTSAT API 実践 2 衛星の情報で表現する
iTamabi 13 第7回:ARTSAT API 実践 2 衛星の情報で表現する
 
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
Next2Dで始めるゲーム開発  - Game Development Starting with Next2DNext2Dで始めるゲーム開発  - Game Development Starting with Next2D
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
 
Node.js - JavaScript Thread Programming
Node.js - JavaScript Thread ProgrammingNode.js - JavaScript Thread Programming
Node.js - JavaScript Thread Programming
 
Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
 
Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.
 
Mashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSource
Mashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSourceMashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSource
Mashup Caravan in オープンソースカンファレンス2011 Hiroshima: infoScoop OpenSource
 
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
 
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
 
Ajax 応用
Ajax 応用Ajax 応用
Ajax 応用
 
Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)Lighting componentワークブック(経費精算アプリ)
Lighting componentワークブック(経費精算アプリ)
 
初めての Data api
初めての Data api初めての Data api
初めての Data api
 
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Windows azure mobile services による mobile + cloud アプリケーション超高速開発Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
 
仕事で使うちょっとしたコードをOSSとして開発メンテしていく - Django Redshift Backend の開発 - PyCon JP 2016
仕事で使うちょっとしたコードをOSSとして開発メンテしていく- Django Redshift Backend の開発 - PyCon JP 2016仕事で使うちょっとしたコードをOSSとして開発メンテしていく- Django Redshift Backend の開発 - PyCon JP 2016
仕事で使うちょっとしたコードをOSSとして開発メンテしていく - Django Redshift Backend の開発 - PyCon JP 2016
 
【ネイティブアドを支えるPhantomJS】
【ネイティブアドを支えるPhantomJS】【ネイティブアドを支えるPhantomJS】
【ネイティブアドを支えるPhantomJS】
 
Google I/O 2016 報告会
Google I/O 2016 報告会Google I/O 2016 報告会
Google I/O 2016 報告会
 
RGtk2入門
RGtk2入門RGtk2入門
RGtk2入門
 
Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.
 

GDG DevFest 2020 Android data linkage info