vault backup: 2022-09-30 21:53:03

Affected files:
.obsidian/workspace
03. 專注Study/Android/ADB 取得 APK 的 icon.md
03. 專注Study/Android/ADB.md
03. 專注Study/Android/AOSP.md
03. 專注Study/Android/Android programming.md
03. 專注Study/Android/AudioTrack.md
03. 專注Study/Android/Ktor.md
03. 專注Study/Android/Service.md
03. 專注Study/Android/Tools.md
03. 專注Study/Android/UI.md
03. 專注Study/C++/C++17.md
03. 專注Study/C++/Class template.md
03. 專注Study/C++/GCC.md
03. 專注Study/C++/Structured binding declaration.md
03. 專注Study/C++/for_each.md
03. 專注Study/C++/lambda.md
03. 專注Study/C++/lvalue.md
03. 專注Study/C++/move operator.md
03. 專注Study/C++/rvalue.md
03. 專注Study/C++/智慧指標.md
03. 專注Study/RxKotlin/20200207 - Study RxKotlin.md
04. Programming/COM/20210726 - COM Interface.md
04. Programming/DB/MySQL.md
04. Programming/DB/sqlite.md
04. Programming/Design Pattern.md
04. Programming/FFMPEG/00. Introduction.md
04. Programming/FFMPEG/01. Setup.md
04. Programming/FFMPEG/FFMpeg.md
04. Programming/Flask.md
04. Programming/Kotlin/class.md
04. Programming/Kotlin/run, let, with, also 和 apply.md
04. Programming/Media Foundation/20210604 - Windows media foundation.md
04. Programming/OpenCV.md
04. Programming/OpenGL.md
04. Programming/Python/argparse.ArgumentParser.md
04. Programming/Python/decorator.md
04. Programming/Python/logging.md
04. Programming/Python/opencv.md
04. Programming/Python/subprocess.md
04. Programming/Python/threading.md
04. Programming/Python/tkinter.md
04. Programming/Python/檢測工具.md
04. Programming/QT/Dropdown button.md
04. Programming/QT/QVariant.md
04. Programming/QT/Qt.md
04. Programming/UML.md
04. Programming/演算法.md
05. 資料收集/99. templates/blogHeader.md
05. 資料收集/99. templates/date.md
05. 資料收集/99. templates/front matter.md
05. 資料收集/99. templates/note.md
05. 資料收集/99. templates/table.md
05. 資料收集/99. templates/thisWeek.md
05. 資料收集/99. templates/日記.md
05. 資料收集/99. templates/讀書筆記.md
05. 資料收集/Linux/CLI/cut.md
05. 資料收集/Linux/CLI/scp.md
05. 資料收集/Linux/CLI/timedatectl.md
05. 資料收集/Linux/Programming.md
05. 資料收集/Linux/Ubuntu.md
05. 資料收集/Tool Setup/Hardware/RaspberryPi.md
05. 資料收集/Tool Setup/Software/Chrome.md
05. 資料收集/Tool Setup/Software/Obisidian.md
05. 資料收集/Tool Setup/Software/SublimeText.md
05. 資料收集/Tool Setup/Software/VirtualBox.md
05. 資料收集/Tool Setup/Software/Visual Studio Code.md
05. 資料收集/Tool Setup/Software/Windows Setup.md
05. 資料收集/Tool Setup/Software/Windows Terminal.md
05. 資料收集/Tool Setup/Software/freefilesync.md
05. 資料收集/Tool Setup/Software/vim.md
05. 資料收集/名言佳句.md
05. 資料收集/架站/Gitea.md
05. 資料收集/架站/HTTP Server/Apache.md
05. 資料收集/架站/HTTP Server/Nginx/Reverse Proxy(Layer4).md
05. 資料收集/架站/Pelican blog.md
05. 資料收集/架站/Proxmox VE.md
05. 資料收集/架站/SWAG Reverse proxy.md
05. 資料收集/架站/Storj.md
05. 資料收集/架站/Trojan.md
05. 資料收集/每週外食.md
05. 資料收集/科技/802.11.md
05. 資料收集/科技/HDR Sensor.md
05. 資料收集/科技/量子電腦.md
05. 資料收集/科技/鋰電池.md
05. 資料收集/興趣嗜好/RC/Traxxas Sledge.md
05. 資料收集/興趣嗜好/RC/好盈電變調整中立點.md
05. 資料收集/興趣嗜好/RC/差速器調教教學.md
05. 資料收集/興趣嗜好/模型/舊化作例.md
05. 資料收集/興趣嗜好/軍武/虎式.md
05. 資料收集/讀書筆記/20201201 - 學習如何學習.md
05. 資料收集/讀書筆記/20201218 - Kotlin權威2.0.md
05. 資料收集/讀書筆記/20201224 - 寫作是最好的自我投資.md
05. 資料收集/讀書筆記/20210119 - 中產悲歌.md
05. 資料收集/讀書筆記/20210220 - 最高學習法.md
05. 資料收集/讀書筆記/20210320 - 最高學以致用法.md
05. 資料收集/讀書筆記/20210406 - 精準購買.md
05. 資料收集/讀書筆記/20210723 - 高手學習.md
05. 資料收集/讀書筆記/20220526 - 深入淺出設計模式.md
05. 資料收集/讀書筆記/20220619 - 精確的力量.md
05. 資料收集/軟體工具/IPFS.md
05. 資料收集/軟體工具/MkDocs.md
05. 資料收集/軟體工具/Obsidian.md
05. 資料收集/軟體工具/docker.md
05. 資料收集/軟體工具/git/apply.md
05. 資料收集/軟體工具/git/submodule.md
05. 資料收集/軟體工具/youtube-dl.md
05. 資料收集/面試準備/技术面试最后反问面试官的话.md
This commit is contained in:
2022-09-30 21:53:04 +08:00
parent 3fe30e50b8
commit 5e4265b783
107 changed files with 36 additions and 14 deletions

View File

@@ -0,0 +1,12 @@
所有 apk 在安裝之後必須要向 launcher 註冊,並將 icon 存在 launcher 的 `app_icons.db` 裡面。
launcher 的 package 是 `com.android.launcher3`,所以 `app_icons.db` 的位置在 `/data/data/com.android.launcher3/databases/app_icons.db`
`SQLiteDatabaseBrowserPortable.exe` 之類的工具可以打開 `app_icons.db`,其內容如下:
![[Pasted image 20220712100904.png|800]]
icon 欄位的blob就是icon的圖檔看來是PNG檔。
## 參考
- [Can i get the icon image of an app through adb](https://stackoverflow.com/questions/39170162/can-i-get-the-icon-image-of-an-app-through-adb)
- [只是简单看下Launcher_Jason_Lee155的博客-CSDN博客](https://blog.csdn.net/Jason_Lee155/article/details/125096966)
- [Android Launcher3中微信联系人快捷方式无法卸载的解决方案 - 简书](https://www.jianshu.com/p/8ba912ad537e)

View File

@@ -0,0 +1,67 @@
## am
### start
`am start`來打開一個activity
```
adb shell am start -S com.logitech.sentineliq/.MainActivity --es cameraId 0
```
其中`-S`是指先停止app再打開app。還有其他的命令如下
```
-D: enable debugging
-W: wait for launch to complete
--start-profiler <FILE>: start profiler and send results to <FILE>
-P <FILE>: like above, but profiling stops when app goes idle
-R: repeat the activity launch <COUNT> times. Prior to each repeat,
the top activity will be finished.
-S: force stop the target app before starting the activity
--opengl-trace: enable tracing of OpenGL functions
```
### 參考
- [Android activity manager "am" command help](https://gist.github.com/tsohr/5711945)
## pm
### list packages
列出所有安裝的apk
```bash
adb shell pm list packages
```
只列出 user 自己安裝的 apk:
```bash
adb shell "pm list packages -3"
```
## Forward
ADB forward用來把PC端收到的TCP轉到Android去這樣就可以透過USB ADB達到網路的功能。
例:
```
adb forward tcp:6100 tcp:7100
```
上述等於:
```
PC Android
http://127.0.0.1:6100 APP(port: 8080)
| |
| |
V----------> ADB(USB)----------->
```
Android端所回應的HTTP封包也會經由原路回來但是如果Android端要發一個request的話PC端就收不到了必須經由[[ADB#Reverse]]才行。
### 參考
* [Android Debug Bridge (adb)  |  Android Developers](https://developer.android.com/studio/command-line/adb#forwardports)
## Reverse
ADB reverse用來監聽Android端收到的request假設今天在Android APP上寫一個`http://127.0.0.1:8080/say/message`並希望PC端可以收到的話就可以用reverse來操作
```
adb reverse tcp:8081 tcp:8080
```
也就是說Android那邊發過來的封包會送交給PC這邊的port 8081。因此PC端還需要寫一個Server來監聽8081才行。
### 參考
* [adb命令-adb reverse的研究-有解無憂](https://www.uj5u.com/qita/277742.html)
## Logcat
- Enlarge logcat buffer to 16M: `adb logcat -G 16M`

View File

@@ -0,0 +1,2 @@
## AOSP framework jar
To use AOSP jar file in Android Studio: [Import AOSP framework jar file](https://medium.com/@chauyan/import-aosp-framework-jar-file-f0c2ac979a8a)

View File

@@ -0,0 +1,164 @@
## Build AOSP
### Build compile environment
1. Install Ubuntu 18.04
2. Install packages: `sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig`
- https://source.android.com/setup/build/initializing
3. Install Repo
```
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo ;\
chmod a+x ~/bin/repo ;\
gpg --recv-key 8BB9AD793E8E6153AF0F9A4416530D5E920F5C65 ;\
curl https://storage.googleapis.com/git-repo-downloads/repo.asc | gpg --verify - ~/bin/repo
```
- https://source.android.com/setup/develop#installing-repo
4. Download AOSP source
1. Create folder for AOSP
```
mkdir -p ~/codes/aosp ; cd ~/codes/aosp
```
2. Setup git
```
git config --global user.name AwinHuang ;\
git config --global user.email awinhuang@gmail.com
```
3. Download source code
```
repo init -u https://android.googlesource.com/platform/manifest ;\
repo sync -j8
```
- 如果要切換某一個特定版本可以使用`-b`,例如:`repo init -u https://android.googlesource.com/platform/manifest -b android-10.0.0_r47`。
- 要知道版本tag可以查看https://source.android.com/setup/start/build-numbers#source-code-tags-and-builds
5. Build code
```
source build/envsetup.sh ;\
lunch aosp_arm-eng ;\
make clobber ;\
make -j16
```
- `make clobber`用來刪除build資料夾
### Reference
- [GitHub - henrymorgen/android-knowledge-system: Android应用开发最强原创知识体系](https://github.com/henrymorgen/android-knowledge-system)
- [Android AOSP基础AOSP源码和内核源码下载 | BATcoder - 刘望舒](http://liuwangshu.cn/framework/aosp/2-download-aosp.html)
- [Android AOSP基础Android系统源码的整编和单编 | BATcoder - 刘望舒](http://liuwangshu.cn/framework/aosp/3-compiling-aosp.html)
## Build kernel
1. Download the code
```
mkdir -p ~/codes/kernel ;\
cd ~/codes/kernel ;\
repo init -u https://android.googlesource.com/kernel/manifest ;\
repo sync -j16
```
2. Compile
```
build/build.sh
```
- 如果遇到`Command 'java' not found, but can be installed with:`
- 依序安裝
- `sudo apt install default-jre`
- `sudo apt install openjdk-11-jre-headless`
- `sudo apt install openjdk-8-jre-headless`
- 執行 `sudo update-alternatives --config java`
- 選擇 `/usr/lib/jvm/java-11-openjdk-amd64/bin/java`
- 再次compile
- `source build/envsetup.sh`
- `mm idegen`
3. 產生android.iml和android.ipr
在source code跟目錄下執行`development/tools/idegen/idegen.sh`
### Reference
- [Android kernel源码下载与编译](https://blog.csdn.net/u010164190/article/details/106561022)
## Android App programming
- R的全名`<PACKAGE_NAME> + .R`例如package name是`com.awin.testapp`,那全名是`com.awin.testapp.R`。
- AndroidX = Android eXtension
- Layout
- layout_margin: 物件與其他物件的距離
- layout_gravity: 物件在容器內的位置(靠左、靠右、置中...
- textApperance: 字型大小
- Extensions
- Android 4.1 沒有自動加入的extension
- 打開build.gradle在`plugins`區塊中加入:
```
id 'kotlin-kapt'
id 'kotlin-android-extensions'
```
- 使用ViewModel & LiveData
- 確認有 `kotlin-kapt` 這個plugin。
![[Pasted image 20210330102148.png]]
- [Android jetpack所有library](https://developer.android.com/jetpack/androidx/explorer)
- [Android jetpack - Lifecycle](https://developer.android.com/jetpack/androidx/releases/lifecycle)
- 從`Declaring dependencies`這區塊複製必要的module
![[Pasted image 20210330110411.png]]
```
def lifecycle_version = "2.3.1"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Annotation processor
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
```
- Create a ViewModel
```kotlin
viewModel = ViewModelProvider(this).get(GuessViewModel::class.java)
```
- Observe a live data in ViewModel
```kotlin
viewModel.counter.observe(this, Observer {
counter.setText(it.toString())
})
```
counter這個變數是包含在ViewModel裡面的live data我們的資料則是放在counter裡面的「value」。
所以如果要取用我們的data則是`viewModel.counter.value`。
- 使用LiveData
- `val counter = MutableLiveData<Int>()`
### Use ViewBinding
ViewBinding is used to replace Kotlin Synthetics.
1. 在`app/build.gradle`中加入:
```
plugins {
...
id 'kotlin-parcelize' <-- Add this
}
android {
...
buildFeatures { <-- Add this
viewBinding true
}
...
}
```
2. 在你的activity裡面
1. `import <APP_DOMAIN_NAME>.databinding.<ACTIVITY_NAME>Binding`
假如APP_DOMAIN_NAME是`com.example.testmultisectioncyclerview`ACTIVITY_NAME是`ActivityMain`,那就是:
`import com.example.testmultisectioncyclerview.databinding.ActivityMainBinding`
2. 用`lateinit`宣告一個變數變數名稱是activity的名字加上binding例如`ActivityMain`就是:
`private lateinit var activityBinding: ActivityMainBinding`
3. 在`onCreate()`中,就可以用`activityBinding`來取得view與其他元件了
```
activityBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityBinding.root) <-- root就是view
```
## ADB usage
- [如何透過 adb command line 指令啟動 Android App](https://kkboxsqa.wordpress.com/2014/08/20/%E5%A6%82%E4%BD%95%E9%80%8F%E9%81%8E-adb-command-line-%E6%8C%87%E4%BB%A4%E5%95%9F%E5%8B%95-android-app/)
# MISC
## 教學文
- [Android Template 小技巧 及 寫程式常見的問題](https://www.eeaseries.com/2021/01/android-template.html?m=1)
- [Jetpack Compose 基础知识](https://developers.google.com/codelabs/jetpack-compose-basics?hl=zh-cn#0)
- [一文带你了解适配Android 11分区存储](https://zhuanlan.zhihu.com/p/354632087)

View File

@@ -0,0 +1,22 @@
AudioTrack 和 MediaPlayer 都可以播放聲音,主要差別是 AudioTrack 沒有 decode 的能力,只能播放 PCM。
## 底層原理
每一個 audio stream 對應著一個 AudioTrack 類的一個實例,每個 AudioTrack 會在建立時會註冊到 AudioFlinger 中,由 AudioFlinger 把所有的 AudioTrack 進行混合Mixer然後輸送到 AudioHardware中 進行播放,目前 Android 同時最多可以創建32個音頻流也就是說Mixer 最多會同時處理32個 AudioTrack 的資料。
## 建立 AudioTrack 物件
```kotlin
const val SAMPLE_RATE = 44100
const val CHANNEL_OUT_FORMAT = AudioFormat.CHANNEL_OUT_STEREO
const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
var audioBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_OUT_FORMAT, AUDIO_FORMAT)
audioTrack = AudioTrack(
AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
CHANNEL_OUT_FORMAT,
AUDIO_FORMAT,
audioBufferSize,
AudioTrack.MODE_STREAM)
audioTrack?.play()
```

View File

@@ -0,0 +1,55 @@
Ktor是由Kotlin提供的一個framwork。
要在Android使用Ktor需要在build.gradle加入以下的dependency:
```
implementation "io.ktor:ktor:1.2.5"
implementation "io.ktor:ktor-server-netty:1.2.5"
implementation "io.ktor:ktor-gson:1.2.5"
```
`packagingOptions`裡,也需要加入以下的設定來必面編譯問題:
```
packagingOptions {
exclude 'META-INF/*'
}
```
`AndroidManifest.xml`記得加入internet的權限
```
<uses-permission android:name="android.permission.INTERNET"/>
```
然後就是HTTP Server的code了
```
embeddedServer(Netty, 8080) {
install(ContentNegotiation) {
gson {}
}
routing {
get("/") {
call.respond(mapOf("message" to "Hello world"))
}
}
}.start(wait=true)
```
但是這段code會block所以需要一個thread把它包起來
```kotlin
Thread {
httpEngine = embeddedServer(Netty, 8080) {
install(ContentNegotiation) {
gson {}
}
routing {
get("/") {
call.respond(mapOf("message" to "Hello world"))
}
get("/say/{something}") {
call.respond(mapOf("message" to "You say: " + call.parameters["something"]))
activity?.findViewById<TextView>(R.id.textView)?.text = "You say: " + call.parameters["something"]
}
}
}.start(wait = false)
}.start()
```

View File

@@ -0,0 +1,205 @@
## Service的生命週期
![[Pasted image 20220307103552.png]]
## Service的啟動方式
Service由`startService()`啟動之後便獨立動作啟動者例如某個activity無法取得Service的intance也無法呼叫Service的API。Service可以被多次呼叫`startService()`,但是只要一旦`stopService()`被呼叫了Service就會結束。所以需要確保Service的管理者是誰統一管理者來呼叫`startService()``stopService()`才不會造成混亂。
`bindService()`則是像是典型的Server-Client架構第一次`bindService()`的時候會建立Service的instance然後可以多次`bindService()``unbindService()`當Service沒也任何人跟它"Bind"的時候Service才會結束。
### startService
要建立自己的Service需要繼承`Service()`類別然後複寫4個成員函式
```kotlin
class MyService : Service() {
override fun onCreate() {
Log.i("Awin","onCreate - Thread ID = " + Thread.currentThread().id)
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("Awin", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().id)
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(p0: Intent?): IBinder? {
Log.i("Awin", "onBind - Thread ID = " + Thread.currentThread().id)
return null // 因為目前沒有要支援bindService()所以這裡直接return null
}
override fun onDestroy() {
Log.i("Awin", "onDestroy - Thread ID = " + Thread.currentThread().id)
super.onDestroy()
}
}
```
假設現在由MainActivity呼叫`startService()`因為是第一次呼叫所以MyService的callback被呼叫的順序是`onCreate()` -> `onStartCommand()`
第二次之後的呼叫就只會執行`onStartCommand()`
當MainActivity呼叫`stopService()`則會執行MyService的`onDestroy()`
所以,必須將「長期任務」開個 Thread 並且執行在 `onStartCommand()` 方法內。
Client端的code:
```kotlin
val intent = Intent(this, ServiceDemo.class)
startService(intent)
```
### bindService
使用`bindService()`來建立Service的一個好處就是可以取得Service的instance然後藉由這個instance來使用Service的API。
要使用`bindService()`的話Service類別必須實做`onBind()``onUnbind()`至兩個override function。
也必須提供一個Binder class繼承自`Binder()`來讓client呼叫。例
```kotlin
class MyService : Service() {
//client 可以通过Binder获取Service实例
inner class MyBinder : Binder() {
val service: MyService
get() = this@MyService
}
//通过binder实现调用者client与Service之间的通信
private val binder = MyBinder()
private val generator: Random = Random()
override fun onCreate() {
Log.i("xiao", "MyService - onCreate - Thread = " + Thread.currentThread().name)
super.onCreate()
}
/**
* @param intent 啟動時啟動組件傳遞過來的Intent如Activity可利用Intent封裝所需要的參數並傳遞給Service
* @param flags 表示啟動請求時是否有額外數據,可選值有 0START_FLAG_REDELIVERYSTART_FLAG_RETRY
* 0: 在正常創建Service的情況下onStartCommand傳入的flags為0。
*
* START_FLAG_REDELIVERY:
* 這個值代表了onStartCommand()方法的返回值為 START_REDELIVER_INTENT
* 而且在上一次服務被殺死前會去調用stopSelf()方法停止服務。
* 其中START_REDELIVER_INTENT意味著當Service因記憶體不足而被系統kill後
* 則會重建服務,並透過傳遞給服務的最後一個 Intent調用 onStartCommand()此時Intent時有值的。
*
* START_FLAG_RETRY
* 該flag代表當onStartCommand()調用後一直沒有返回值時會嘗試重新去調用onStartCommand()。
*
* @param startId 指明當前服務的唯一ID與stopSelfResult(int startId)配合使用stopSelfResult()可以更安全地根據ID停止服務。
*
* @return
* START_STICKY:
* 當Service因記憶體不足而被系統kill後一段時間後記憶體再次空閒時
* 系統將會嘗試重新創建此Service一旦創建成功後將回調onStartCommand方法
* 但其中的Intent將是null除非有掛起的Intent如pendingintent
* 這個狀態下比較適用於不執行命令、但無限期運行並等待作業的媒體播放器或類似服務
*
*
* START_NOT_STICKY:
* 當Service因記憶體不足而被系統kill後即使系統記憶體再次空閒時
* 系統也不會嘗試重新創建此Service。除非程序中再次調用startService啟動此Service
* 這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啟所有未完成的作業時運行服務。
*
* START_REDELIVER_INTENT:
* 當Service因記憶體不足而被系統kill後則會重建服務
* 並透過傳遞給服務的最後一個 Intent 調用 onStartCommand(),任何掛起 Intent均依次傳遞。
* 與START_STICKY不同的是其中的傳遞的Intent將是非空是最後一次調用startService中的intent。
* 這個值適用於主動執行應該立即恢復的作業(例如下載文件)的服務。
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("xiao", "MyService - onStartCommand - startId = $startId, Thread = " + Thread.currentThread().name)
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder{
Log.i("xiao", "MyService - onBind - Thread = " + Thread.currentThread().name)
return binder
}
override fun onUnbind(intent: Intent): Boolean {
Log.i("xiao", "MyService - onUnbind - from = " + intent.getStringExtra("from"))
return super.onUnbind(intent)
}
override fun onDestroy() {
Log.i("xiao", "MyService - onDestroy - Thread = " + Thread.currentThread().name)
super.onDestroy()
}
//getRandomNumber是Service暴露出去供client调用的公共方法
fun getRandomNumber(): Int {
return generator.nextInt()
}
}
```
Client端要做的事
1. 建立ServiceConnection類型實例並覆載`onServiceConnected()`方法和`onServiceDisconnected()`方法。
2. 當執行到onServiceConnected callback時可通過IBinder instance得到Service的instance這樣可實現client與Service的連接。
3. onServiceDisconnected callback被執行時表示client與Service已經斷開在此可以寫一些斷開後需要做的處理。
```kotlin
class AActivity : AppCompatActivity() {
private var service: MyService? = null
private var isBind = false
private var conn = object : ServiceConnection{
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
isBind = true
val myBinder = p1 as MyService.MyBinder
service = myBinder.service
Log.i("xiao", "ActivityA - onServiceConnected")
val num = service!!.getRandomNumber()
Log.i("xiao", "ActivityA - getRandomNumber = $num");
}
override fun onServiceDisconnected(p0: ComponentName?) {
isBind = false
Log.i("xiao", "ActivityA - onServiceDisconnected")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_a)
Log.i("xiao", "ActivityA - onCreate - Thread = " + Thread.currentThread().name)
btn_bind_service_a.setOnClickListener {
val intent = Intent(this,MyService::class.java)
intent.putExtra("from","ActivityA")
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 执行 bindService");
bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
btn_unbind_service_a.setOnClickListener {
if(isBind){
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 执行 unbindService");
unbindService(conn)
}
}
btn_a_start_b.setOnClickListener {
val intent = Intent(this,BActivity::class.java)
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 启动 ActivityB");
startActivity(intent)
}
btn_finish_a.setOnClickListener {
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 执行 finish");
finish()
}
}
override fun onDestroy() {
super.onDestroy()
Log.i("xiao", "ActivityA - onDestroy")
}
}
```
---
## 參考
1. [如何使用Service(kotlin)](https://givemepass.blogspot.com/2015/10/service.html)
2. [Android kotlin service使用简析 - 簡書](https://www.jianshu.com/p/f9712b470b42)
3. [《Android》『Service』- 背景執行服務的基本用法 - 賽肥膩膩の娛樂生活誌](https://xnfood.com.tw/android-service/#skill_02)

View File

@@ -0,0 +1,17 @@
# screenrecord
## 螢幕錄影
`screenrecord `可以把目前的螢幕錄製下來,一個簡單的例子:
```
adb shell screenrecord --size 1200x1920 /storage/sdcard0/demo.mp4
```
### 指定錄製時間
```
adb shell screenrecord --time-limit 10 /storage/sdcard0/demo.mp4
```
### 指定bit rate
```
adb shell screenrecord --bit-rate 6000000 /storage/sdcard0/demo.mp4
```
沒有指定時間的話,就必須手動`Ctrl+c`來中止錄影。

View File

@@ -0,0 +1,8 @@
## 在thread更新UI
Android framework只能在main thread裡更新UI若需要在其他的thread更新UI的話需要呼叫activity的`runOnUiThread()`
例:
```kotlin
activity?.runOnUiThread {
activity?.findViewById<TextView>(R.id.textView)?.text = "You say: " + call.parameters["something"]
}
```