show code block

顯示具有 使用者逾時登出 標籤的文章。 顯示所有文章
顯示具有 使用者逾時登出 標籤的文章。 顯示所有文章

2017年10月6日 星期五

Android元件(ActivityLifecycleCallbacks) — 逾時登出製作 (二)

前言:

伊隆·馬斯克 space x 的創辦人,曾經說過把事情簡化的重要性。

像是把一件不可能的事情,中間的步驟不斷分解簡化。
就像他提出的第一性原理
就是讓我們把目光從那些別人做的事上挪開,不管做什麼選擇,都以最根本的那個原則為參照點,不斷用那個參照點為自己糾偏。

 上一次做逾時登出也是用這方法,把每一個想得到的情況都包含進去,在不斷簡化。

但在簡化的過程,其實也可以抬頭看一下有沒有其他平行更好的方法。

剛剛我發現了一個更快的方式,一個方法就概括監控所有Activity的方法
 ActivityLifecycleCallbacks
 https://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html

 ActivityLifecycleCallbacks

他能瞬間監控所有的activity,也能在其中做一些變化,像是ToolBar
關於ToolBar我是參考
http://www.jianshu.com/p/75a5c24174b2

這篇讓你實現每個頁面實現toolbar的方式。

有興趣可以連過去看看,這邊只講逾時登出。



 重點程式碼:

Application內 可以直接使用ActivityLifecycleCallbacks
public class MyApplicationTwo extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {
                
            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

 

上述就和Activity的生命週期幾乎一模一樣,在每個方法內打上Log。

設置兩三個Activity就可以做測試了!

在逾時登出的案子裡,我們還原一下裡面的CustomApplication這個方法
http://nikeru8.blogspot.tw/2017/09/android.html

把整個App關於逾時登出的部分全部砍掉,包含情境A情境B


這裡一行就完成ActivityLifecycleCallbacks
public class CustomApplication extends Application {

    private static final String TAG = "CustomApplication";
    private static long PAUSE_TIME = 0;//暫停的時間點
    private static long RESTART_TIME = 0;//喚醒的時間點

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {
                Log.d(TAG, "onActivityCreated");
            }

            @Override
            public void onActivityStarted(Activity activity) {
                Log.d(TAG, "onActivityStarted");
            }

            @Override
            public void onActivityResumed(Activity activity) {
                Log.d(TAG, "onActivityResumed");
                if (PAUSE_TIME != 0) {
                    RESTART_TIME = System.currentTimeMillis();
                    long totalTime = RESTART_TIME - PAUSE_TIME;
                    Log.d("checkpoint", "checkpoint PAUSE_TIME = " + PAUSE_TIME);
                    Log.d("checkpoint", "checkpoint RESTART_TIME = " + RESTART_TIME);
                    Log.d("checkpoint", "checkpoint = " + totalTime);
                }

            }

            @Override
            public void onActivityPaused(Activity activity) {
                Log.d(TAG, "onActivityPaused");
            }

            @Override
            public void onActivityStopped(Activity activity) {
                Log.d(TAG, "onActivityStopped");
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
                Log.d(TAG, "onActivitySaveInstanceState");
            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                Log.d(TAG, "onActivityDestroyed");
            }
        });
    }
}

 
這就和Activity的生命週期一模一樣。
我在喚醒activity的Resume部分,寫了方法。

假如停止時間點有值,喚醒時,拿PAUSE_TIME(暫停的時間點)比較一下 RESTART_TIME (喚醒的時間點)
long 每一千個單位 等於 一秒。
相減下來如果1204,就等於app暫停了1.204



好啦,試試看自己的App能不能有逾時登出的效果吧!
以下是我的Log
checkpoint 就是相扣的單位 2934
所以是2.934


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...