2019年3月6日 星期三

Android 產生Signed Apk時錯誤 Keystore was tampered with, or password was incorrect

在產生signed apk時

明明輸入正確密碼卻出現 Keystore was tampered with, or password was incorrect 錯誤訊息

密碼打到懷疑人生...一直以為是密碼錯誤

 於是開始找密碼  

方法一: https://github.com/nanqiu/android-keystore-password-recover/blob/master/README.md

下載這個程式 把你的xxxx.jks放到同一資料夾

建立一個關鍵字wordlist.txt

然後想想可能的關鍵字 打在裡面

打開 cmd下指令
java -jar AndroidKeystoreBrute_v1.05.jar -m 3 -k xxxx.jks -d "wordlist.txt" 

 找到密碼

結果密碼沒錯啊

回到Android Studio再產生看看, 一樣是說密碼錯誤 wtf, 難道是其中有一個密碼錯誤

於是再繼續找密碼


https://stackoverflow.com/questions/28034899/how-to-retrieve-key-alias-and-key-password-for-signed-apk-in-android-studiomigr

這篇提到如果有用過Android Studio 2.2之前的

Android Studio會有Log檔案, 裡面竟然有密碼啊啊啊啊啊

Windows系統位置在
C:\Users\xxxxx\.AndroidStudio2.2\system\log
裡面有個 idea.log.1
用記事本打開他, 搜尋一下password,  you got it !!!
兩個密碼都有
結果都正確啊啊啊啊啊啊

於是我開始懷疑根本是Android Studio的問題

https://stackoverflow.com/questions/32987530/android-studio-keystore-was-tampered-with-or-password-was-incorrect
提到換密碼就好

https://stackoverflow.com/questions/2889238/keystore-change-passwords

先找到keytool位置
C:\Program Files\Java\jdk1.8.0_73\bin
然後將你的xxxx.jks放在這
cmd下指令
 
keytool -storepasswd -keystore xxxx.jks
會要你輸入舊密碼與新密碼
 
keytool -keypasswd  -alias xxxx -keystore xxxx.jks
alias名稱要正確
然後一樣輸入舊密碼與新密碼
完成


回到Android Studio, 一定要先Clean Project
再Generate Signed Bundle/APK

成功產生!!! 害我嚇到吃手手 WTF

2018年8月2日 星期四

iOS Swift 信用卡 Credit Card

使用套件
Braintree可以快速使用
但是目前還是測試階段的樣子
 
基本流程就是 
1.先取得Token(自己的server 這裡用官方測試提供的)
2.再將Token丟給braintree server
3.取得Nonce再丟給自己的server註冊
4.然後自己server再跟braintree server溝通

這邊提供iOS手機端方法

Podfile加入 
pod 'BraintreeDropIn'
下載套件完成後在storyboard拉個有按鈕的UI
Ex:
Braintree有提供測試用的信用卡號
可來這裡參考 
https://developers.braintreepayments.com/reference/general/testing/ruby
 
ViewController.swift

import UIKit
import BraintreeDropIn
import Braintree

class ViewController: UIViewController {

    @IBOutlet weak var btnShow: UIButton!
    @IBOutlet weak var indicator: UIActivityIndicatorView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func fetchClientToken() {
        self.btnShow.isEnabled = false
        self.indicator.isHidden = false
        // TODO: Switch this URL to your own authenticated API
        // 測試用官方取得Token Url
        let clientTokenURL = NSURL(string: "https://braintree-sample-merchant.herokuapp.com/client_token")!
        let clientTokenRequest = NSMutableURLRequest(url: clientTokenURL as URL)
        clientTokenRequest.setValue("text/plain", forHTTPHeaderField: "Accept")
        
        URLSession.shared.dataTask(with: clientTokenRequest as URLRequest) { (data, response, error) -> Void in
            // TODO: Handle errors
            let clientToken = String(data: data!, encoding: String.Encoding.utf8)
            print("clientToken:\(clientToken!)")
            self.showDropIn(clientTokenOrTokenizationKey: clientToken!)
            
            // As an example, you may wish to present Drop-in at this point.
            // Continue to the next section to learn more...
            }.resume()
    }

    @IBAction func jumpDropIn(_ sender: UIButton) {
        fetchClientToken()
    }
    
    
    func showDropIn(clientTokenOrTokenizationKey: String) {
        DispatchQueue.main.async {
        let request =  BTDropInRequest()
        let dropIn = BTDropInController(authorization: clientTokenOrTokenizationKey, request: request) { (controller, result, error) in
            if (error != nil) {
                print("ERROR:\(error.debugDescription)")
            } else if (result?.isCancelled == true) {
                print("CANCELLED")
            } else if let result = result {
                // Use the BTDropInResult properties to update your UI
                print("result paymentOptionType:\(result.paymentOptionType)")
                // 取得nonce後,拿nonce去後端註冊
                print("result paymentMethod.nonce:\(result.paymentMethod!.nonce)")
                print("result paymentIcon:\(result.paymentIcon)")
                print("result paymentDescription:\(result.paymentDescription)")
            }
            
                self.btnShow.isEnabled = true
                self.indicator.isHidden = true
            controller.dismiss(animated: true, completion: nil)
        }
        self.present(dropIn!, animated: true, completion: nil)
        }
    }
}





2017年8月21日 星期一

iOS Swift Multiple Language Localizable 切換多語系

不是認手機語系, 而是直接在app上切換語系

1.在Main.storyboard 拉三個按鈕與一個Label


2.與ViewController.swift建立連接
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var labMessage: UILabel!
    

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func btnEnglish(_ sender: UIButton) {
        UserDefaults.standard.set("Base", forKey: "lang") //儲存語系為基本
        setMessage()
    }
    
    @IBAction func btnJapan(_ sender: UIButton) {
        UserDefaults.standard.set("ja", forKey: "lang") //儲存語系為日文
        setMessage()
    }
    
    @IBAction func btnChinses(_ sender: UIButton) {
        UserDefaults.standard.set("zh-Hant", forKey: "lang") //儲存語系為繁體中文
        setMessage()
    }
    
    func setMessage() {
        labMessage.text = "A Song of Ice and Fire".localized //使用擴展功能 localized
    }

}

extension String{

    var localized: String {
                
        let lang = UserDefaults.standard.string(forKey: "lang")
        
        let bundle = Bundle(path: Bundle.main.path(forResource: lang, ofType: "lproj")!)

        return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
        
    }
}

3.建立Localizable.strings檔案
New File... -> 找到 String File  -> 輸入Localizable.strings -> Create
建立完成後點選檔案->右邊的 ->再點選Localize...

4.回到PROJECTInfo 點選Localizations的 + 會出現所有語言, 這裡選擇Japanese(ja)與Chinses(zh-Hant), 括弧內為設定文字對應的縮寫

勾選建立的Localizable.strings就好

 接著Localizable.strings會出現三個檔案

分別在 Localizable.strings(Base)輸入
"A Song of Ice and Fire" = "A Song of Ice and Fire";

Localizable.strings(Japanese)輸入
"A Song of Ice and Fire" = "氷と炎の歌";

Localizable.strings(Chinese(Traditional))輸入
"A Song of Ice and Fire" = "冰與火之歌"; 





檔案下載:
https://github.com/terryyamg/MultipleLanguageTest
參考連結:
https://stackoverflow.com/questions/25081757/whats-nslocalizedstring-equivalent-in-swift
https://medium.com/lean-localization/ios-localization-tutorial-938231f9f881

2017年8月10日 星期四

iOS Swift Cache Image 簡易的Image Cache

簡單的Cache Image, 讓圖片不用一直下載,造成卡卡的問題

1.建立一個Project, 先在Info.plist ->右鍵 Open AS -> 選Source Code
在dict內加入,
NSAppTransportSecurity
NSAllowsArbitraryLoads 

2.接著建立MyTableViewCell.swift, 後面在Cell建立Image View後拉過來對應
class MyTableViewCell: UITableViewCell {

    @IBOutlet weak var iv: UIImageView!
    
    var ivUrl: NSURL! //圖片來源url
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

3.然後在Main.storyboard拉一個Table View

在cell裡面加個Image View, 對應Class: MyTableViewCell
Table View拉一下dataSource與delegate到ViewController
記得給Cell的Identifier,這裡寫ImageCell

4.建立一個CustomImageCache.swift檔案
import UIKit

class CustomImageCache {
    
    static let sharedCache: NSCache = { () -> NSCache<AnyObject, AnyObject> in
        let cache = NSCache<AnyObject, AnyObject>()
        cache.name = "MyImageCache"
        cache.countLimit = 20 // Max 20 images in memory.
        cache.totalCostLimit = 10*1024*1024 // Max 10MB used.
        return cache
    }()
    
}

extension NSURL {
    
    typealias ImageCacheCompletion = (UIImage) -> Void
    
    
    var cachedImage: UIImage? {
        return CustomImageCache.sharedCache.object(
            forKey: absoluteString as AnyObject) as? UIImage
    }
    
    func fetchImage(completion: @escaping ImageCacheCompletion) {
        // 如果需要客製化取得資料在此做
        let task = URLSession.shared.dataTask(with: self as URL) {
            data, response, error in
            if error == nil {
                if let data = data, let image = UIImage(data: data) {
                    CustomImageCache.sharedCache.setObject(
                        image,
                        forKey: self.absoluteString as AnyObject,
                        cost: data.count)
                    DispatchQueue.main.async() {
                        completion(image)
                    }
                }
            }
        }
        task.resume()
    }
    
}

5.最後在ViewController.swift
import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    var url = [String]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 重複50張圖片
        url = Array(repeating: "http://g03.a.alicdn.com/kf/HTB1hvNUIFXXXXbBXFXXq6xXFXXX2/3D-Diamond-Embroidery-Paintings-Rhinestone-Pasted-diy-Diamond-painting-cross-Stitch-font-b-coffe-b-font.jpg", count: 50)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return url.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell: MyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as! MyTableViewCell
        let ivUrl = NSURL(string: url[indexPath.row])
    
        cell.ivUrl = ivUrl // For recycled cells' late image loads.
        if let image = ivUrl?.cachedImage { //抓過了 -> 直接顯示
            cell.iv.image = image
            cell.iv.alpha = 1
        } else { //沒抓過 ->下載圖片
            cell.iv.alpha = 0
            // 下載圖片
            ivUrl?.fetchImage { image in
                // Check the cell hasn't recycled while loading.
                if cell.ivUrl == ivUrl {
                    cell.iv.image = image
                    UIView.animate(withDuration: 0.3) {
                        cell.iv.alpha = 1
                    }
                }
            }
        }
        
        return cell
    }
}



檔案下載:
https://github.com/terryyamg/CacheImag
參考來源:
http://www.splinter.com.au/2015/09/24/swift-image-cache/

2017年7月1日 星期六

iOS Swift Custom View Xib 客製化 視窗

建立一個上下左右跑進來的視窗,使用xib客製化元件

1.建立一個xib檔案 File -> New -> File ... 選擇View
2.MyView.xib


a.建立 MyView.xib 並拉一個Label到視窗
b.選擇View
c.設定屬性Size:Freeform 其他None

3.建立MyView.Swift
//
//  MyView.swift
//  JumpViewTest
//
//  Created by Terry Yang on 2017/6/30.
//  Copyright © 2017年 terryyamg. All rights reserved.
//

import UIKit

class MyView: UIView {
    
    @IBOutlet weak var labCustom: UILabel!
    
    var view:UIView!
    
    // Label Inspectable
    @IBInspectable
    var mytitleLabelText: String? {
        get {
            return labCustom.text
        }
        set(mytitleLabelText) {
            labCustom.text = mytitleLabelText
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    
    func setup() {
        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [
            UIViewAutoresizing.flexibleWidth,
            UIViewAutoresizing.flexibleHeight
        ]
        addSubview(view)
    }
    
    func loadViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: "MyView", bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
        
        return view
    }
    
}

4.在MyView.xib

a.選擇File;s Owner
b.點擊Identity並選擇Class 對應到 MyView

5.連結Label到MyView.swift
6.於Main.storyboard建立四個按鈕

7.ViewController.swift


//
//  ViewController.swift
//  JumpViewTest
//
//  Created by Terry Yang on 2017/6/30.
//  Copyright © 2017年 terryyamg. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    var indexOfTop : Int    = 0
    var indexOfLeft : Int   = 0
    var indexOfRight : Int  = 0
    var indexOfBottom : Int = 0
    
    var topView : MyView?
    var leftView : MyView?
    var rightView : MyView?
    var bottomView : MyView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    //  Top 按鈕
    @IBAction func btnShowTop(_ sender: UIButton) {
        indexOfTop += 1
        
        if topView == nil {
            //建立客製化View視窗
            topView = MyView(frame: CGRect(x: 0, y: -150, width: UIScreen.main.bounds.size.width, height: 150)) //設定初始位置與大小
            topView?.backgroundColor = .red //背景顏色
            topView?.labCustom.text = "Oh! Top View" //顯示文字
            self.view.addSubview(topView!) //加入此視窗
        }
        
        if indexOfTop % 2 == 1 { //點擊展開視窗
            UIView.animate(withDuration: 0.5, animations: {
                self.topView?.frame.origin.y += 150
            }, completion: nil)
        } else { //點擊收起視窗
            UIView.animate(withDuration: 0.5, animations: {
                self.topView?.frame.origin.y -= 150
            }, completion: nil)
        }
    }
    
    // Left 按鈕
    @IBAction func btnShowLeft(_ sender: UIButton) {
        
        indexOfLeft += 1
        
        if leftView == nil {
            leftView = MyView(frame: CGRect(x: -150, y: 0, width: 150, height: UIScreen.main.bounds.size.height))
            leftView?.backgroundColor = .brown
            leftView?.labCustom.text = "It's Left"
            self.view.addSubview(leftView!)
        }
        
        if indexOfLeft % 2 == 1 {
            UIView.animate(withDuration: 0.5, animations: {
                self.leftView?.frame.origin.x += 150
            }, completion: nil)
        } else {
            UIView.animate(withDuration: 0.5, animations: {
                self.leftView?.frame.origin.x -= 150
            }, completion: nil)
        }
        
    }
    
    // Right 按鈕
    @IBAction func btnShowRight(_ sender: UIButton) {
        
        indexOfRight += 1
        
        if rightView == nil {
            rightView = MyView(frame: CGRect(x: view.frame.maxX, y: 0, width: 150, height: UIScreen.main.bounds.size.height))
            rightView?.backgroundColor = .green
            rightView?.labCustom.text = "It's Right"
            self.view.addSubview(rightView!)
        }
        
        if indexOfRight % 2 == 1 {
            UIView.animate(withDuration: 0.5, animations: {
                self.rightView?.frame.origin.x -= 150
            }, completion: nil)
        } else {
            UIView.animate(withDuration: 0.5, animations: {
                self.rightView?.frame.origin.x += 150
            }, completion: nil)
        }
        
    }
    
    // Bottom 按鈕
    @IBAction func btnShowBootom(_ sender: UIButton) {
        indexOfBottom += 1
        
        if bottomView == nil {
            bottomView = MyView(frame: CGRect(x: 0, y: view.frame.maxY, width: UIScreen.main.bounds.size.width, height: 150))
            bottomView?.backgroundColor = .black
            bottomView?.labCustom.text = "Hi! Bottom View"
            self.view.addSubview(bottomView!)
        }
        
        if indexOfBottom % 2 == 1 {
            UIView.animate(withDuration: 0.5, animations: {
                self.bottomView?.frame.origin.y -= 150
            }, completion: nil)
        } else {
            UIView.animate(withDuration: 0.5, animations: {
                self.bottomView?.frame.origin.y += 150
            }, completion: nil)
        }
    }
    
    
}


 檔案下載:
https://github.com/terryyamg/JumpViewTest
 參考連結:
https://stackoverflow.com/documentation/ios/1362/custom-uiviews-from-xib-files#t=201706280220545656796
https://www.youtube.com/watch?v=EBYdsYwoJVI&t=492s
https://www.youtube.com/watch?v=MMgfnrddOxY

2017年5月23日 星期二

Firebase Cloud Messaging 接收訊息 PHP Service

FCM兩種推播通知方式 - Notification與Data

----------------------------------FCM前置步驟可觀看----------------------------------
Step1:
https://litotom.com/2016/06/24/android-firebase-cloud-messaging-1/
注意:
apply plugin: 'com.google.gms.google-services'
要放在build.gradle(Module: app)這個檔案裡面的最下面

Step2:
https://litotom.com/2016/06/26/firebase-android-send-2/

-----------------------------------------------------------------------------------------------

1.在MyFirebaseMessagingService.java加入推播通知訊息
package com.terryyamg.fcmtest;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "MyFirebaseMessaging";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.i(TAG, "onMessageReceived:" + remoteMessage.getFrom());
        Intent intent = new Intent();   //點擊啟動到MainActivity頁面
        intent.setClass(this, MainActivity.class);
        showNotification(this, remoteMessage, intent);
    }

    // 顯示通知
    // remoteMessage.getData()         -  無論app在什麼狀態下皆會執行 MyFirebaseMessagingService(需對應Service設定的字串文字)
    // remoteMessage.getNotification() -  只會在app顯示時,執行app的接收訊息 MyFirebaseMessagingService
    private void showNotification(Context context, RemoteMessage remoteMessage, Intent intent) {
        NotificationManagerCompat manager = NotificationManagerCompat.from(this);
        PendingIntent iPending = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(this)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setContentTitle(remoteMessage.getData().get("title")) //需對應Service設定的字串文字
                        .setWhen(System.currentTimeMillis())
                        .setDefaults(Notification.DEFAULT_SOUND)
                        .setContentText(remoteMessage.getData().get("body")) //需對應Service設定的字串文字
                        .setContentIntent(iPending)
                        .setAutoCancel(true);

        Notification notification = mBuilder.build();
        manager.notify(1, notification);

    }
}

2.Service推播 fcm_service.php
    #API access key from Google API's Console
    define( 'API_ACCESS_KEY', 'AAAA6iG_xzk:APA91bHxlAkx5byr66cIFk-CXAr2k3hvs4tGQV9nAzj2nw75kPC8_9eIyyZVaeUPYFYzAk10qV-D0Pwl5BsIGslvv32PI2ECap1KcmW7Iu6lyzh1A3eEE2TJ5jVsnPydWFgKjvh3J5hh' );
    $registrationIds = "dTDs6rF7h04:APA91bECmREiN_qgUJ2IBwagJRzWmVxDMWVpmjEnkgJi1FWMLAtFHGTSYVXA-0VTHMf-42mZH8qLBEdKHFs9Z6oCL1JUf0uJ232z26XJdpEuwMvVPFxunwAZPN9E4-6qKImQEb7b9iHw";
    #prep the bundle
    $msg = array
    (
     'body'  => 'Body  Of Notification',
     'title' => 'Title Of Notification'
     );
    //'notification' - 只會在app顯示時,執行app的接收訊息Service
    //'data'         - 無論app在什麼狀態皆會執行app的接收訊息Service
    $fields = array
    (
     'to'  => $registrationIds,
     'data'     => $msg
     );
    
    
    $headers = array
    (
     'Authorization: key=' . API_ACCESS_KEY,
     'Content-Type: application/json'
     );
    #Send Reponse To FireBase Server
    $ch = curl_init();
    curl_setopt( $ch,CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send' );
    curl_setopt( $ch,CURLOPT_POST, true );
    curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
    curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
    curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
    $result = curl_exec($ch );
    curl_close( $ch );
    #Echo Result Of FireBase Server
    echo $result;




檔案下載:
參考資料:




2016年12月19日 星期一

Android library Realm 資料庫

簡單的Realm資料庫CRUD應用

1.build.gradle(Project:xxxx)加入classpath "io.realm:realm-gradle-plugin:2.2.1"
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath "io.realm:realm-gradle-plugin:2.2.1"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
2.build.gradle(Module:app)加入apply plugin: 'realm-android'
apply plugin: 'com.android.application'
apply plugin: 'realm-android'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.1"
    defaultConfig {
        applicationId "com.terryyamg.realmtest"
        minSdkVersion 19
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support:design:24.2.1'
    testCompile 'junit:junit:4.12'
}
3.AndroidManifest.xml的application內加入android:name=".MyApplication"


    
        
            
                

                
            
        
    


4.MyApplication.java
package com.terryyamg.realmtest;
import android.app.Application;
import io.realm.Realm;
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // Initialize Realm. Should only be done once when the application starts.
        Realm.init(this);

    }
}
5.MainActivity.java
package com.terryyamg.realmtest;

import android.app.Dialog;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import io.realm.Realm;
import io.realm.RealmResults;

public class MainActivity extends AppCompatActivity {

    private Realm realm;
    TextView tvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        realm = Realm.getDefaultInstance();

        tvMessage = (TextView) findViewById(R.id.tvMessage);

        //Create
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                create();
            }
        });
        //Update
        FloatingActionButton fabUpdate = (FloatingActionButton) findViewById(R.id.fabUpdate);
        fabUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                update();
            }
        });
        //Delete
        FloatingActionButton fabDelete = (FloatingActionButton) findViewById(R.id.fabDelete);
        fabDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                delete();
            }
        });

        read();
    }

    private void create() {
        final Dialog dialog = new Dialog(MainActivity.this);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.table_dialog);
        final EditText etName = (EditText) dialog.findViewById(R.id.etName);
        final EditText etAge = (EditText) dialog.findViewById(R.id.etAge);
        Button btnSave = (Button) dialog.findViewById(R.id.btnSave);
        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                realm.executeTransaction(new Realm.Transaction() {
                    @Override
                    public void execute(Realm realm) {
                        int personCount;
                        //PrimaryKey增加
                        try {
                            personCount = realm.where(Person.class).max("id").intValue() + 1;
                        } catch (Exception e) {
                            personCount = 1; //從1開始
                        }
                        Log.i("personCount", personCount + "");
                        // 人 Table
                        Person personC = realm.createObject(Person.class, personCount);
                        personC.setName(etName.getText().toString());   // 輸入姓名
                        personC.setAge(Integer.parseInt(etAge.getText().toString()));   //輸入年齡

                        /* 公司 Table 測試 */
//                        Company company1 = realm.createObject(Company.class);
//                        company1.setId(4);
//                        company1.setCompanyName("公司2");
//                        personC.getCompany().add(company1);
//
//                        Company company2 = realm.createObject(Company.class);
//                        company2.setId(5);
//                        company2.setCompanyName("公司3");
//                        personC.getCompany().add(company2);
//
//                        Company company3 = realm.createObject(Company.class);
//                        company3.setId(6);
//                        company3.setCompanyName("公司4");
//                        personC.getCompany().add(company3);

                        read();
                        dialog.dismiss();
                    }
                });
            }
        });

        dialog.show();
    }

    private void read() {
        /* 公司 Table 測試 */
//        RealmResults<Person> person = realm.where(Person.class).equalTo("company.companyName", "公司2").equalTo("company.companyName", "公司1").findAll();
        RealmResults<Person> person = realm.where(Person.class).findAll();
        String message = "";
        Log.i("person.size()",person.size()+"");
        for (int i = 0; i < person.size(); i++) {
            message += person.get(i).getName() + " - " + person.get(i).getAge() + "\n";
            /* 公司 Table 測試 */
//            Log.i("getCompany.size()",person.get(i).getCompany().size()+"");
//            for(int j = 0; j <person.get(i).getCompany().size();j++) {
//                message += person.get(i).getName() + " - " + person.get(i).getAge() + ":" + person.get(i).getCompany().get(j).getCompanyName() + "\n";
//            }
        }
        tvMessage.setText(message);
    }

    private void update() {
        final Dialog dialog = new Dialog(MainActivity.this);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.table_dialog);
        final EditText etName = (EditText) dialog.findViewById(R.id.etName);
        final EditText etAge = (EditText) dialog.findViewById(R.id.etAge);
        Button btnSave = (Button) dialog.findViewById(R.id.btnSave);
        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                realm.executeTransaction(new Realm.Transaction() {
                    @Override
                    public void execute(Realm realm) {
                        //修改第一筆
                        Person person = realm.where(Person.class).equalTo("id", 1).findFirst();
                        person.setName(etName.getText().toString());
                        person.setAge(Integer.parseInt(etAge.getText().toString()));

                        read();
                        dialog.dismiss();
                    }
                });
            }
        });

        dialog.show();
    }

    private void delete() {
        final RealmResults<Person> results = realm.where(Person.class).findAll();
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                //刪除某一筆
//                Person p = results.get(1);
//                p.deleteFromRealm();
                //刪除第一筆
//                results.deleteFirstFromRealm();
                //刪除最後一筆
//                results.deleteLastFromRealm();
                //刪除全部
                results.deleteAllFromRealm();
            }
        });
        read();
    }

}

6.Person.java
package com.terryyamg.realmtest;


import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

public class Person extends RealmObject{

    @PrimaryKey
    private int id;

    private String name;
    private int age;
    private RealmList<Company> company;

    public void setId(int id){
        this.id = id;
    }

    public int getId(){
        return id;
    }

    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    public void setCompany(RealmList<Company> company){
        this.company = company;
    }

    public RealmList<Company> getCompany(){
        return company;
    }

}

7.Company.java
package com.terryyamg.realmtest;


import io.realm.RealmObject;

public class Company extends RealmObject{

    private int id;

    private String companyName;

    public void setId(int id){
        this.id = id;
    }

    public int getId(){
        return id;
    }

    public void setCompanyName(String companyName){
        this.companyName = companyName;
    }

    public String getCompanyName(){
        return companyName;
    }

}

8./res/layout/activity_main.xml


    

        

    

    

    

    

    



9./res/layout/content_main.xml


    


10./res/layout/table_dialog.xml


    

    

    



參考資料:
https://realm.io/docs/java/latest/#getting-started
檔案下載:
https://github.com/terryyamg/RealmTest