標籤雲

搜尋此網誌

2018/08/07

AOSP - 系統內建 app 的 Android.mk 範例

有玩過 aosp 的人應該都有接觸過 mk 檔的撰寫
今天就來整理下如果要將 app 放進 aosp 裡面
Android.mk 檔要怎麼寫

最簡單的方式是先將 Module 編譯成 apk 檔
這樣子可以使用 gradle 編譯
dependency 也不用寫在 Android.mk 裡
非常簡單
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

# Module name should match apk name to be installed
LOCAL_MODULE := XXXXXXX

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(LOCAL_MODULE).apk

LOCAL_MODULE_CLASS := APPS

LOCAL_CERTIFICATE := platform

include $(BUILD_PREBUILT)

如果因為原始碼管理或是其他原因
你必須要把 Module 的專案原始碼放進 aosp 裡面的話
那麼可以參考一下下面的複雜版本

LOCAL_PATH:= $(call my-dir)

# compile time only jar names (for later use)
#COMPILE_LIBRARIES := compiler-a compiler-b

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

# Name of the module to build
LOCAL_PACKAGE_NAME := XXXXXXX

# module src file path
LOCAL_SRC_FILES := \
        $(call all-java-files-under, java)

# path(s) of resources
#LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)

# List of static libraries to include in the package
#LOCAL_STATIC_JAVA_LIBRARIES := lib-jar-a lib-jar-b lib-jar-c

#LOCAL_STATIC_JAVA_AAR_LIBRARIES := lib-aar-a lib-aar-b lib-aar-c

# path of compile time libs
#LOCAL_CLASSPATH := $(addprefix $(LOCAL_PATH)/libs/,$(addsuffix .jar, $(COMPILE_LIBRARIES)))

# set as privileged application
#LOCAL_PRIVILEGED_MODULE := true

# use platform key to sign the module
LOCAL_CERTIFICATE := platform

# proguard enabled setting
LOCAL_PROGUARD_ENABLED := disabled

include $(BUILD_PACKAGE)

############## JAR/AAR Files ####################################
#include $(CLEAR_VARS)
#
## pre built all your jar and aar lib for your module 
## so you can use the lib name in LOCAL_STATIC_JAVA_LIBRARIES and LOCAL_STATIC_JAVA_AAR_LIBRARIES
#LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := lib-jar-a:libs/lib-jar-a.1.0.0.jar \
#    lib-jar-b:libs/lib-jar-b.1.0.0.jar \
#    lib-jar-c:libs/lib-jar-c.1.0.0.jar \
#    lib-aar-a:libs/lib-aar-a.1.0.0.aar \
#    lib-aar-b:libs/lib-aar-b.1.0.0.aar \
#    lib-aar-c:libs/lib-aar-c.1.0.0.aar
#
#include $(BUILD_MULTI_PREBUILT)

以上是參考不同範例綜合而成
若有錯誤及建議也希望不吝指教

Room Persistence Library 簡單介紹

使用 Room 需要在 app 的 build.gradle 中增加 dependencies

dependencies {
    def room_version = "1.1.1"

    implementation "android.arch.persistence.room:runtime:$room_version"
    annotationProcessor "android.arch.persistence.room:compiler:$room_version" // use kapt for Kotlin

    // optional: RxJava support for Room
    implementation "android.arch.persistence.room:rxjava2:$room_version"
    // optional: Guava support for Room, including Optional and ListenableFuture
    implementation "android.arch.persistence.room:guava:$room_version"
    // Test helpers
    testImplementation "android.arch.persistence.room:testing:$room_version"
}

Room 有三種主要元件

- Entity: 對應到 database 的 table

- DAO: 擁有存取 database 資料的 method

- Database: 是主要存取點與 db 的持有者, 需要滿足以下要件
使用 @Database 這個 annotation 並且在裡面宣告 Entities 的 list 來定義包含的 table
繼承 RoomDatabase 的 abstract class
擁有無參數的 abstract method 可以 return 用 @Dao 標記的 Data Access Object

另外, 由於 Database 是比較耗資源的類別, 所以建議是使用 singleton 模式來獲得實體

所以一個 Database 大概會長這樣
@Database(entities = {Alarm.class}, version = 1, exportSchema = false)
public abstract class XXXXXDB extends RoomDatabase {
 public static final String DB_NAME = "xxxxx_db";

 private static XXXXXDB INSTANCE;

 public static XXXXXDB getInstance(final Context context){
  if(INSTANCE == null){
   synchronized (AlarmDB.class) {
    if(INSTANCE == null) {
     INSTANCE = Room.databaseBuilder(context, XXXXXDB.class, DB_NAME).build();
    }
   }
  }
  return INSTANCE;
 }

 public static void destroyInstance() {
  if(INSTANCE != null){
   INSTANCE.close();
  }
  INSTANCE = null;
 }


 public abstract XXXXXDao xxxxxDao();

}

而 Data Access Object 則會長得像是這樣
@Dao
public interface XXXXXDao {
 @Query("SELECT * FROM "+XXXXX.TABLE_NAME+" ORDER BY "+XXXXX.Col.TIME+" DESC" )
 List queryXXXXXs();

 @Query("SELECT * FROM "+XXXXX.TABLE_NAME+" WHERE "+XXXXX.Col.ID+" = :id")
 Alarm queryXXXXX(long id);

 @Query("SELECT * FROM "+XXXXX.TABLE_NAME+" WHERE "+XXXXX.Col.TIME+" BETWEEN :afterTheTime AND :beforeTheTime")
 List queryXXXXXs(long afterTheTime, long beforeTheTime);

 //return long (the rowId)
 @Insert(onConflict = OnConflictStrategy.REPLACE)
 long insertXXXXX(XXXXX x);

 @Delete
 void deleteXXXXX(XXXXX x);
}

最後是 Entity object 的簡單範例
@Entity(tableName = XXXXX.TABLE_NAME)
public class XXXXX {
 private static final String TAG = XXXXX.class.getSimpleName();

 public static final String TABLE_NAME = "XXXXXs";

 public class Col {
  public static final String ID = "id";
  public static final String TARGET_DEVICE = "target_device_id";
  public static final String ACTION = "action";
  public static final String TIME = "time";
  public static final String TYPE = "type";
  public static final String CONTENT = "content";
 }
 public class Value {
  public static final String ACTION_AAA = "aaa";
  public static final String ACTION_BBB = "bbb";
 }

 @PrimaryKey
 @ColumnInfo(name= Col.ID)
 public long id;

 @ColumnInfo(name= Col.TARGET_DEVICE)
 public String targetDeviceId;

 @ColumnInfo(name= Col.ACTION)
 public String action;

 @ColumnInfo(name= Col.TIME)
 public long time;

 @ColumnInfo(name= Col.TYPE)
 public int type;

 @ColumnInfo(name= Col.CONTENT)
 public String content;

 @Ignore
 private int[] myInts;

 public Alarm() { }

}

另外由於檔案存取不允許在主 thread 進行
所以建議在 AsyncTask 或是 IntentService 裡面操作


相關資料:
Android Developers: Adding Components to your Project#Room
Google Developers Codelabs: Android Room with a View
Android Developers: Save data in a local database using Room

2016/07/13

Mac 使用 FAQ 與 terminal 指令

*手動產生 SSH key

1. 產生 public/private rsa key pair
ssh-keygen -t rsa
你會需要設定一組 password (passphrase)
完成後檔案會放置在 /Users/yourusername/.ssh/id_rsa
而公開鑰 (public key) 則是放在 /Users/yourusername/.ssh/id_rsa.pub

2. 將 id_rsa.pub 拷貝到剪貼簿
pbcopy < ~/.ssh/id_rsa.pub

*顯示隱藏檔案與資料夾

將 com.apple.finder 的 AppleShowAllFiles 屬性設為 YES
然後關閉所有現有 Finder
defaults write com.apple.finder AppleShowAllFiles YES
killall Finder
執行完後再打開 Finder 就可以了

相關資料:
Manually Generating Your SSH key in MAC OS X

2016/07/07

Flex Box 與常用 Styles

Flex Box

Flex Box 的 style 屬性有四個 flex, flexDirection, justifyContent, alignItems

其中 flex 代表該 element 應該擴展的大小, 0 代表不展開
另外三個則互相相關:

flexDirection 指定子 element 的排列方向, 'vertical'(column) 或 'horizontal'(row)
而 flexDirection 的值會影響 justifyContent 與 alignItems 兩個屬性所造成的效果
因為 justifyContent 代表內容物在主要軸上的分布方式
alignItems 代表內容物在次要軸上的對齊方式

其值範圍:

flexDirection enum('row', 'row-reverse', 'column', 'column-reverse')
justifyContent enum('flex-start', 'flex-end', 'center', 'space-between', 'space-around')

alignItems: 'flex-start'(對齊前端 default) / 'center'(置中) / 'flex-end'(對齊尾端) / 'stretch'(填滿)

在 React Native 中
flexDirection 預設值為 column
這表示 justifyContent 的方向是 vertical , alignItems 的方向是 horizontal

所以在這樣的情況下
justifyContent: flex-start
alignItems: stretch
代表的是子 element 的排列方式是 向上對齊、左右填滿

同樣的值在 flexDirection: row 設定下
flexDirection: row
justifyContent: flex-start
alignItems: stretch
子 element 的排列方式變成 靠左對齊、上下填滿

其實只要記住 justifyContent 預設(column)下是直式
那 alignItems 自然就是橫的
row 模式下則是相反
這樣就比較簡單了

以下列出常用 style 屬性:

Layout Props
flex number
flexDirection enum('row', 'row-reverse', 'column', 'column-reverse')
flexWrap enum('wrap', 'nowrap')
justifyContent enum('flex-start', 'flex-end', 'center', 'space-between', 'space-around')
alignItems enum('flex-start', 'flex-end', 'center', 'stretch')
alignSelf enum('auto', 'flex-start', 'flex-end', 'center', 'stretch')

width number (以及 maxWidth, minWidth)
height number (以及 maxHeight, minHeight)
margin number (以及 marginHorizontal, marginVertical, marginTop, marginBottom, marginLeft, marginRight)
padding number (以及 paddingHorizontal, paddingVertical, paddingTop, paddingBottom, paddingLeft, paddingRight)

backgroundColor string
opacity number 0~1
borderRadius number (以及 borderBottomLeftRadius, borderBottomRightRadius, borderTopLeftRadius, borderTopRightRadius)
borderWidth number (以及 borderTopWidth, borderBottomWidth, borderLeftWidth, borderRightWidth)
borderColor string (以及 borderTopColor, borderBottomColor, borderLeftColor, borderRightColor)

position enum('absolute', 'relative')
top number (以及 bottom, left, right)
zIndex number

Transforms
transform:[
{perspective: number},
{rotate: string}, {rotateX: string}, {rotateY: string}, {rotateZ: string}, (ex:'45deg')
{scale: number}, {scaleX: number}, {scaleY: number},
{translateX: number}, {translateY: number},
{skewX: string}, {skewY: string},
]
decomposedMatrix DecomposedMatrixPropType
transformMatrix TransformMatrixPropType

Shadow Props (ios)
shadowColor color
shadowOffset {width: number, height: number}
shadowOpacity number
shadowRadius number


相關資料:
React Native - Layout Props
React Native - Transforms
React Native - Shadow Props
React Native Express - Flexbox
React Native Express - View

2016/07/06

Component Lifecycle API

Component 的生命週期有
建構, 掛載(mount), 渲染(render), 更新(update), 卸載(unmount), 銷毀( destroy)等

你可以在各生命週期中插入執行自定的程式碼來達到更細膩的控制

Mounting Cycle

constructor(object props)
從父 element 初始化 props, 此時我們也可以順便初始化 state
在 ES5 用的是 getDefaultProps / getInitialState 的方法
var MyComponent = React.createClass({
  propTypes: {
    text: React.PropTypes.string,
    anotherProp: React.PropTypes.string.isRequired,
    //以此類推...
  },
  getDefaultProps: function() {
    return {
      anotherProp: 'default value'
    };
  },
  getInitialState: function(){
    return { 
      key1: 'value1', 
      key2: null
    };
  },
  //...
});

在 ES6 則是用 constructor 的方式
至於 defaultProps 是在 Component 內用一個 static 變數來處理
import React, { PropTypes, Component } from 'react';
class MyComponent extends Component {
  static defaultProps = {
    text: 'default value',
  };
  static propTypes = {
    text: PropTypes.string.isRequired,
  };
  constructor(props){
    super(props);
    //initial state
    this.state = { 
      key1: 'value1', 
      key2: null
    };
  }
  //...
}

componentWillMount()
在第一次 render 發生前被呼叫, 此方法只會被呼叫一次

render() -> React Element
render 必須 return 一個 React Element (或是 null 表示不顯示任何東西)
在第一次 render 之前這個 element 不會有任何 native UI

componentDidMount()
在第一次 render 發生後被呼叫, 此方法只會被呼叫一次
此時這個 element 的 native UI 已經完成 render, 可以透過 this.refs 直接操作
如果需要進行非同步 API 呼叫或是用 setTimeout 執行 delayed code, 一般是在這裡進行


Updating Cycle

componentWillReceiveProps(object nextProps)
父 component 傳入了新的 props, 所以 component 即將重新渲染 (re-render)
在 render 方法被呼叫前我們可以用 this.setState() 更新 component 內部狀態

shouldComponentUpdate(object nextProps, object nextState) -> boolean
根據新的 props 跟 state, 該 component 必須決定是否該重新渲染
基底類別的 shouldComponentUpdate 實作總是會回傳 true
我們可以 override 這個方法並檢查每個 props 跟 state 是否已被更動(例如對每個 key/value 做 equal 檢查), 若回傳 false 就不會重新渲染

componentWillUpdate(object nextProps, object nextState)
componentWillUpdate 代表 component 此時已經確定要重新渲染
這時我們就不該用 this.setState() 改變狀態, 因為已經在進行更新了

render() -> React Element
既然要重新渲染當然就會再呼叫 render

componentDidUpdate(object prevProps, object prevState)
重新渲染完成後, 該 component 已經更新了從 render() 回傳來的 React Element


最後是卸載的部分
Uumount

componentWillUnmount()
若有使用 Timer, 卸載前應該清除以避免 Fatal
clearInterval/clearTimeout/clearImmediate

相關資料:
React Native Express - Lifecycle API
React - Reusable Components
React Native 中组件的生命周期

2016/05/16

在 Android Studio 2.1 開啟對 Lambda expressions 的支援

Android N 與 Android Studio 2.1 引進對 Java 8 語言的部分支援
像是:
Default and static interface methods
Repeatable annotations
Lambda expressionsMethod References (這兩項可向下支援到 API level 23 以下)
(其他支援的 Java 8 API 請參考文末連結)

而要使用這些支援必須認識一下 Jack

Jack (Java Android Compiler Kit) 是 Android 新的 toolchain
用來把 java code 編譯成 Android dex bytecode
Jack 使用 Java 1.7 並移植了一些新功能
不但是 Open source,還縮短編譯時間
而這個 toolchain 取代了之前的 javac, ProGuard, dx 等工具
傳統 javac 工具鏈: javac (.java --> .class) --> dx (.class --> .dex)
新的 Jack 工具鏈: Jack (.java --> .jack --> .dex)

Jack 是 Android M 的 default build toolchain
所以不用下任何特殊的 command 就可以使用

順便認識認識 Jack Intermediate Library Linker (Jill)
它負責的是把 jar 檔轉換成新的 lib 格式 (.jack)

在 build.gradle (app) 使用 Jack 的方式
android {
    ...(省略)

    defaultConfig {
        ...(省略)

        jackOptions{
            enabled true
        }
    }
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }
    ...(省略)
}
完成以上設定後就可以在專案內使用 lambda 了

同場加映:Lambda 表達式簡介

匿名類別 Anonymous Classes 常被使用於作為一個參數傳遞到 method 裡
但匿名類別(特別是只有一個方法的匿名類別) 寫起來常感覺有些繁瑣
而 Lambda Expressions 對於簡化只有單一方法的類別就可以發揮優點

Lambda 表達式語法:
* 逗號分隔的的參數
* 箭頭標記( -> )
* 表達式主體
- 若使用單行表達式則回傳結果值,或是直接使用 return,不過 return statement 不是表達式,如果 method 不是回傳 void 則 statement 需使用大括號( { } )

例如如果我們有一個 printPersons 方法,傳入 List<Person> 及 CheckPerson 介面的實體(其中有 test 方法)
可以輸出通過條件的 Person 訊息
public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}
實際呼叫時 code 是像這樣:
printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25;
        }
    }
);
改寫成 lambda 之後:
printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
);

相關資料:
android developers - Java 8 Language Features
Jack (Java Android Compiler Kit)

2016/04/18

adb monkey test

adb shell monkey
usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]
              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]
              [--ignore-crashes] [--ignore-timeouts]
              [--ignore-security-exceptions]
              [--monitor-native-crashes] [--ignore-native-crashes]
              [--kill-process-after-error] [--hprof]

              [--pct-touch PERCENT] [--pct-motion PERCENT]
              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]
              [--pct-nav PERCENT] [--pct-majornav PERCENT]
              [--pct-appswitch PERCENT] [--pct-flip PERCENT]
              [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]
              [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]
              [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]

              [--wait-dbg] [--dbg-no-events]
              [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]
              [--port port]
              [-s SEED] [-v [-v] ...]
              [--throttle MILLISEC] [--randomize-throttle] 每個動作的等待時間
              [--profile-wait MILLISEC]
              [--device-sleep-time MILLISEC] 
              [--randomize-script]
              [--script-log]
              [--bugreport]
              [--version]
              COUNT
範例:
adb shell monkey -p com.company.packagename --throttle 700 --pct-touch 20 --pct-motion 40 --pct-majornav 40 -v 100