show code block

2017年9月12日 星期二

Android方法 — 逾時登出

前言:

App會了保護使用者,通常都會製作一個閒置太久就登出的功能。

通常會有兩個使用情境:
情境A、當使用者不使用app且把app縮到背景運行時,過一段時間後自動登出。
情境B、當使用者不使用手機,直接把手機螢幕黑掉待機。

逾時登出Demo


第一種比較簡單
 


此時你要有三個條件
1、非必要條件
如果你的app有常駐的ToolBar,你可以不用每個Activity都寫一次ToolBar
可以直接寫一個底層帶有ToolBar的Activity,並讓其他的Activity都extands(繼承)它。

為什麼呢?

因為如果你沒有寫這個class讓大家都繼承,你每一個Activity都要加上逾時登出的代碼。

這邊就不再實作繼承的BaseActivity,我會直接寫在範例內的Activity內。

2、情境A
在Application內複寫onTrimMemory的方法。
這裡是讓情境A在縮到背景運行時,瞬間記錄下時間點。
簡單的介紹一下客製化的Application
 概念:
android系統會為每個程序運行時創建一個Application類的對象且僅創建一個,所以Application可以說是單例 (singleton)模式的一個類.且application對象的生命週期是整個程序中最長的,它的生命週期就等於這個程序的生命週期。因為它是全局的單例的,所以在不同的Activity,Service中獲得的對象都是同一個對象。所以通過Application來進行一些,數據傳遞,數據共享,數據緩存等操作。

簡單的說就是存放整個App共用的共同參數的地方。


3、情境B
寫一個廣播BroadcastReceiver,在螢幕黑掉時,把暫停的時間點帶入廣播中,在喚起手機和app時,會再帶入。


edit(2017/10/20)
寫了一個part2
ndroid元件(ActivityLifecycleCallbacks) — 逾時登出製作 (二) 
http://nikeru8.blogspot.tw/2017/10/android.html

重點程式碼:

先來完成情境A的情況吧

首先你要先畫一個xml

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.baseactivity.MainActivity">

    <TextView
        android:id="@+id/login_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="點我登入"
        android:textSize="50dp" />

</RelativeLayout>

 
此時我只有在中間佈局一個TextView,方便模擬登入登出效果
接下來來做自製的Application吧!
CustomApplication.java
public class CustomApplication extends Application {

    //用來判斷使用者登出與否
    public static boolean IsLogin = false; 

    @Override
    public void onCreate() {
        super.onCreate();
    }


}

 
裡面目前只塞一個boolean參數,用以判斷登入與否
/**
* true  登入狀態
* false 登出狀態
*/

此時Application是不會運行的!
你還要註冊自己的Application,告訴整個App,你要用我的喔!不可以使用系統預設的。
接下來把畫面切到AndroidManifest.xml中



 <application
        android:name=".CustomApplication"
        其他略...
    >  
    ...
    </application>
 



再來就是MainActivity.java
這邊稍微做麻煩一點
public class MainActivity extends AppCompatActivity {

    //元件
    private TextView mLoginButton;
    private Activity activity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = this;
        initView();//初始化 元件畫面
        initSet();
        initListener();//初始化 監聽功能
    }


    private void initView() {
        mLoginButton = (TextView) findViewById(R.id.login_btn);

    }

    private void initSet() {

    }

    private void initListener() {
        //設置按鈕點擊
        mLoginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //點擊後,把文字改成已登入/登出(模擬登入效果)
                if (!CustomApplication.IsLogin) {

                    mLoginButton.setText("已登入 \n 帳號:helloWorld");
                    CustomApplication.IsLogin = true; //告訴IsLogin你已經登入囉

                } else {

                    //出現Dialog和使用者確認是否要登出
                    new AlertDialog.Builder(activity)
                            .setMessage("確定要登出嗎?")
                            .setPositiveButton("ok", new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            mLoginButton.setText("點我登入");
                                            CustomApplication.IsLogin = false;//告訴IsLogin你已經登出囉
                                        }
                                    }
                            )
                            .setNegativeButton("no", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                }
                            }).show();


                }
            }
        });
    }
}

 
這邊我在程式碼內有做了AlertDialog詢問使用者是否登出。
這邊有一個CustomApplication.IsLogin的布林參數,用來判斷整個app的使用者登入的狀態。

簡單的模擬登入登出就完成囉。
正式開始實作(誤

情境A


先覆寫CustomApplication.java
public class CustomApplication extends Application {

    //用來判斷使用者登出與否
    public static boolean IsLogin = false;
    //螢幕消失要存的時間點
    public static long PAUSE_TIME = 0;
    //螢幕恢復時的時間點
    public static long RESTART_TIME = 0;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onTrimMemory(int level) {
        //螢幕背景運行時,記錄時間
        if (level == TRIM_MEMORY_UI_HIDDEN) {
            PAUSE_TIME = System.currentTimeMillis();
        }
        super.onTrimMemory(level);
    }
}

 
這邊在參數部分加入的暫停時間,並且在onTrimMemory內紀錄暫停的時間
TRIM_MEMORY_UI_HIDDEN 可參考這裡。
簡述就是他會在onStop前調用,那為什麼不直接用onStop呢?
因為如果寫在onStop內,你每個activity的轉換、關閉都會被調用。
onTrimMemory則是只有在畫面被轉到背景運行時才會被調用。


然後把畫面轉回MainActivity.java
public class MainActivity extends AppCompatActivity {

    //元件
    private TextView mLoginButton;
    private Activity activity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = this;
        initView();//初始化 元件畫面
        initSet();
        initListener();//初始化 監聽功能
    }


    private void initView() {
        mLoginButton = (TextView) findViewById(R.id.login_btn);

    }

    private void initSet() {

    }

    private void initListener() {
        //設置按鈕點擊
        mLoginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //點擊後,把文字改成已登入/登出(模擬登入效果)
                if (!CustomApplication.IsLogin) {

                    mLoginButton.setText("已登入 \n 帳號:helloWorld");
                    CustomApplication.IsLogin = true; //告訴IsLogin你已經登入囉

                } else {

                    //出現Dialog和使用者確認是否要登出
                    new AlertDialog.Builder(activity)
                            .setMessage("確定要登出嗎?")
                            .setPositiveButton("ok", new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            //系統登出
                                            memberLogOut();
                                        }
                                    }
                            )
                            .setNegativeButton("no", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                }
                            }).show();


                }
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();

        //假如登出狀態,設置所有時間歸零 參數
        boolean isMemberlogout = false;

        //如果喚醒時,暫停時間不為零
        if (CustomApplication.PAUSE_TIME != 0) {

            //獲取喚醒時的時間
            CustomApplication.RESTART_TIME = System.currentTimeMillis();

            //在這邊喚醒時間 - 暫停時間 每1000單位 = 1 second 這邊用三秒代替
            if ((CustomApplication.RESTART_TIME - CustomApplication.PAUSE_TIME) > Long.valueOf("3000")) {
                if (CustomApplication.IsLogin) {


                     //告訴它要變成登出狀態了
                    isMemberlogout = true;
                }
            }

            //告訴他要變成登出狀態了,請歸零時間
            if (isMemberlogout) {
                CustomApplication.PAUSE_TIME = 0;
                CustomApplication.RESTART_TIME = 0;
                memberLogOut();
            }
        }
    }

    //系統登出
    private void memberLogOut() {
        mLoginButton.setText("點我登入");
        //告訴IsLogin你已經登出囉
        CustomApplication.IsLogin = false;
    }
}

 
這裡因為登出元件重複到,所以額外拉出來當成一個方法,這樣可以減少籠code
memberLogOut
並且在onResume複寫,在這邊計算喚醒時間和暫停時間的秒數。
如果你要螢幕離開一秒後登出 單位為1000,看你自身需求。
這邊就完成簡單的情境A囉!
當你想切換到Line或是按到home鍵,只要過了三秒會自動幫你變成登出狀態。



情境B

寫一個廣播BroadcastReceiver,在螢幕黑掉時,把暫停的時間點帶入廣播中,在喚起手機和app時,會再帶入。

那就開始著手寫廣播吧

MyReceiver.java
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String actionStr = intent.getAction();
        //假如螢幕按掉了
        if (actionStr.equals(Intent.ACTION_SCREEN_OFF)) {
            //記錄時間點
            CustomApplication.PAUSE_TIME = System.currentTimeMillis();
        }
    }
}
 
這裏Intent有一個查看螢幕按掉的方法,直接調用,如果螢幕按掉就記錄時間。

之後再回到MainActivity.java寫一個MyReceiver的方法
 //如果螢幕黑頻按掉時,存取記錄時間點
    private void register() {

        IntentFilter fliter = new IntentFilter();
        fliter.addAction(Intent.ACTION_SCREEN_OFF);
        mReceiver = new MyReceiver();
        registerReceiver(mReceiver, fliter);
    }
 
上面這個方法可以在onCreate內調用它。

之後不要忘記在你離開App的同時,反註冊它。
@Override
    protected void onDestroy() {
        //在結束app的同時,關掉廣播
        if (mReceiver != null) {
            unregisterReceiver(mReceiver);
            mReceiver = null;
        }
        super.onDestroy();
    }
 

這時候試試看你的新功能吧!!

DEMO: https://github.com/nikeru8/timeoutLogout


這篇看完以興趣可以去看看
Android元件(ActivityLifecycleCallbacks) — 逾時登出製作 (二)




沒有留言:

張貼留言

協程(coroutine) - 協程為什麼要學它?

 Coroutine 協程 再強調一次 協程就是由kotlin官方所提供的線程api //Thread Thread { }.start() //Executor val execu...