init
This commit is contained in:
59
02. PARA/02. Area(領域)/20150803 - Android/ADB.md
Normal file
59
02. PARA/02. Area(領域)/20150803 - Android/ADB.md
Normal file
@@ -0,0 +1,59 @@
|
||||
## 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
|
||||
- `adb shell pm list packages`:可以列出所有安裝的apk
|
||||
|
||||
## 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`
|
||||
2
02. PARA/02. Area(領域)/20150803 - Android/AOSP.md
Normal file
2
02. PARA/02. Area(領域)/20150803 - Android/AOSP.md
Normal 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)
|
||||
55
02. PARA/02. Area(領域)/20150803 - Android/Ktor.md
Normal file
55
02. PARA/02. Area(領域)/20150803 - Android/Ktor.md
Normal 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()
|
||||
```
|
||||
205
02. PARA/02. Area(領域)/20150803 - Android/Service.md
Normal file
205
02. PARA/02. Area(領域)/20150803 - Android/Service.md
Normal 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 表示啟動請求時是否有額外數據,可選值有 0,START_FLAG_REDELIVERY,START_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)
|
||||
17
02. PARA/02. Area(領域)/20150803 - Android/Tools.md
Normal file
17
02. PARA/02. Area(領域)/20150803 - Android/Tools.md
Normal 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`來中止錄影。
|
||||
8
02. PARA/02. Area(領域)/20150803 - Android/UI.md
Normal file
8
02. PARA/02. Area(領域)/20150803 - Android/UI.md
Normal 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"]
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user