Android 程序打开即启动指定页面,比如密码检查页。

本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。

前言:
一些app设置完手势密码或者数字密码之后,每次打开这个app都会打开解锁页面。
这个解锁页面的弹起分为两种:一种是按下home下次打开即弹起解锁页面 ,一种是锁屏之后。这种方式大同小异,区别只是广播接收者的监听的区别。我们这里以按下home键为例子,展开描述。

一、需求

不管当前我们正在操作我们的app的哪个页面,当按下home键之后,下次打开app,即打开指定检查页,比如密码页。

演示git.gif

二、 分析

既然是按下home触发的,那么我们就不能再Activity里面去打开指定检查页,因为我们不知道到底是在哪一个页面被按下home的。

1、按下home触发,联想到广播接收者。
2、这个需求是需要后台一直执行的,需要服务的协助。又因为这个home键的监听比较敏感,所以需要在的服务里面代码注册广播接收者才能生效。
总的来说就是这样,服务加广播。

情景想像:
当我选择了打开检查页,就利用Sp作为将检查页选项作为boolean缓存起来,并且开启服务。
当我们打开启动页,就判断 检查页选择项 ,如果打开,就判断服务是否开启,无开启则开启服务。
服务里面的代码很简单,服务开启就代码注册广播接收者,服务销毁就取消广播接收者。
广播接收者里面做的事情是,当我们按下home键,就由广播接收者开启检查页。

有了上面的准备之后,当我们打开开启检查页,按下home键就会顺利弹检查页了。
但是如果这样会有问题,那就是当用户狂按home键,那么就会打开很多个重复的检查页。
所以我们在广播接收者里面还需要做一个判断,即可当前手机的前台app是否是我们的当前app,如果是,那么才允许跳检查页,如果不是,就不跳了,避免3下home键打开3个检查页。

看起来差不多,但是判断不够严谨,那就是,当我们在当前程序按下home键,我们的程序由前台任务栈转为后台任务栈的这个时间换成毫秒还是挺长的,足够用户快速按好多下,也就是说,当我们在当前程序处于前台任务线的时候,用户猛点了好几下home键,起始还是可以足够广播响应好几次打开好几次检查页的。

解决办法:
1、本来可以判断当前手机前台任务栈的栈顶是哪一个Activity,但是在android5.0以后这个权限被限制了,有其他迂回的实现方式,详情可以百度,我们这里暂时不考虑。

2、我们在广播接收者打开检查页在最外层再做一个boolean判断到Sp。我们在启动页让这个 检查页判断项 先置为true,在接收者的onReceive里面,先做这个最外层的判断,因为启动页置为true,所以第一次是可以通过的,当我们启动一次之后,就把这个值置为false,只有当检查页被销毁执行onDestroy时才重新把这个最外层检查项目置为true。

三、代码

三.1、先看看服务和广播和检查页

看看服务

public class HomeWatchService extends Service {
    private HomeWatcherReceiver receiver;
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void onCreate() {
        // 服务onCreate里面代码注册广播接收者
        System.out.println("=========== 开启服务");
        receiver = new HomeWatcherReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.CLOSE_SYSTEM_DIALOGS");
        System.out.println("=========== 服务 代码注册广播接收者");
        this.registerReceiver(receiver, filter);
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        // 服务onCreate里面代码取消广播接收者
        System.out.println("=========== 服务 代码取消广播接收者");
        unregisterReceiver(receiver);
        super.onDestroy();
    }
}

看看广播:

public class HomeWatcherReceiver extends BroadcastReceiver {
    private static final String LOG_TAG = "HomeReceiver";
    private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
    private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
    private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
    private static final String SYSTEM_DIALOG_REASON_LOCK = "lock";
    private static final String SYSTEM_DIALOG_REASON_ASSIST = "assist";


    @Override
    public void onReceive(Context context, Intent intent) {


        String action = intent.getAction();
        Log.i(LOG_TAG, "onReceive: action: " + action);
        if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
            // android.intent.action.CLOSE_SYSTEM_DIALOGS
            String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
            Log.i(LOG_TAG, "reason: " + reason);

            // 第一层判断,能否打开新的检查页,在启动页我们只为true,下面成功打开检查页之后就只为false
            // 之后检查页指定onDestroy之后才会将这个值置为false
            if(CacheUtils.getBoolean(context,"IS_CAN_OPEN")){
                // 是否短按home
                if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
                    // 短按Home键
                    Log.i(LOG_TAG, "homekey");
                    // 当前app是否处有前台进程,只有处于前台进程才允许打开检查页
                    if(isAppOnForeground(context)){
                        System.out.println("============= 当前app在属于前台任务栈");
                        Intent openCheck = new Intent(context,CheckActivity.class);
                        openCheck.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        context.startActivity(openCheck);
                    }else{
                        System.out.println("============= 当前app在不属于前台任务栈");
                    }
                }
                CacheUtils.setBoolean(context,"IS_CAN_OPEN",false);
            }

            else if (SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
                // 长按Home键 或者 activity切换键
                Log.i(LOG_TAG, "long press home key or activity switch");

            }
            else if (SYSTEM_DIALOG_REASON_LOCK.equals(reason)) {
                // 锁屏
                Log.i(LOG_TAG, "lock");
            }
            else if (SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) {
                // samsung 长按Home键
                Log.i(LOG_TAG, "assist");
            }
        }
    }

    /**
     * 判断当前app是否处于前台进程
     * @param context
     * @return
     */
    public boolean isAppOnForeground(Context context) {
        // Returns a list of application processes that are running on the
        // device

        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        String packageName = context.getPackageName();

        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager
                .getRunningAppProcesses();
        if (appProcesses == null)
            return false;

        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            // The name of the process that this object is associated with.
            if (appProcess.processName.equals(packageName)
                    && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true;
            }
        }
        return false;
    }
}

.
.

看看 检查页

Paste_Image.png
public class CheckActivity extends Activity{
    private TextView mTvPass;
    private boolean isLastIsSelf;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_check);
        mTvPass = (TextView) findViewById(R.id.mTvPass);

        mTvPass.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        CacheUtils.setBoolean(CheckActivity.this,"IS_CAN_OPEN",true);
    }
}

三.2 看看3个页面

页面分为三个,MainActivity、SecondActivity、ThirdActivity三个。
其中MainActivity包括对检查页的开关

页面一、MainActivity

Paste_Image.png

MainActivity

public class MainActivity extends AppCompatActivity {

    private TextView mTvOpenSecond;
    private TextView mTvOpenThird;
    private TextView mTvOpenCheck;
    private TextView mTvcCloseCheck;
    private TextView mTvServiceState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTvOpenSecond = (TextView) findViewById(R.id.mTvOpenSecond);
        mTvOpenThird = (TextView) findViewById(R.id.mTvOpenThird);
        mTvOpenCheck = (TextView) findViewById(R.id.mTvOpenCheck);
        mTvcCloseCheck = (TextView) findViewById(R.id.mTvcCloseCheck);
        mTvServiceState = (TextView) findViewById(R.id.mTvServiceState);


        mTvOpenSecond.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            }
        });

        mTvOpenThird.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,ThirdActivity.class));
            }
        });

        mTvOpenCheck.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Toast.makeText(MainActivity.this,"打开检查锁",Toast.LENGTH_SHORT).show();

                if(ServiceUtils.isServiceWork(MainActivity.this,"com.am.alwayscheckview.service.HomeWatchService")){
                    System.out.println("按钮开启服务的判断  服务已经开启");
                }else{
                    System.out.println("按钮开启服务的判断  服务未开启");
                    startService(new Intent(MainActivity.this, HomeWatchService.class));
                }
                // 把密码的开启状态缓存起来
                CacheUtils.setBoolean(MainActivity.this,"Check",true);
            }
        });

        mTvcCloseCheck.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CacheUtils.setBoolean(MainActivity.this,"Check",false);
                Toast.makeText(MainActivity.this,"关闭检查锁",Toast.LENGTH_SHORT).show();
                if(ServiceUtils.isServiceWork(MainActivity.this,"com.am.alwayscheckview.service.HomeWatchService")){
                    System.out.println("按钮关闭服务的判断  服务已经开启,进行关闭");
                    stopService(new Intent(MainActivity.this, HomeWatchService.class));
                }
            }
        });

        mTvServiceState.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this,"当前开启状态:"+ServiceUtils.isServiceWork(MainActivity.this,"com.am.alwayscheckview.service.HomeWatchService")
                ,Toast.LENGTH_SHORT).show();
            }
        });
    }
}

.
.

页面二、SecondActivity

Paste_Image.png
public class SecondActivity extends AppCompatActivity {

    private TextView mTvOpenMain;
    private TextView mTvOpenThird;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        mTvOpenMain = (TextView) findViewById(R.id.mTvOpenMain);
        mTvOpenThird = (TextView) findViewById(R.id.mTvOpenThird);

        mTvOpenMain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(SecondActivity.this,MainActivity.class));
            }
        });

        mTvOpenThird.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(SecondActivity.this,ThirdActivity.class));
            }
        });
    }
}

.
.

页面三、ThirdActivity

Paste_Image.png
public class ThirdActivity extends AppCompatActivity {

    private TextView mTvOpenMain;
    private TextView mTvOpenSecond;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);

        mTvOpenMain = (TextView) findViewById(R.id.mTvOpenMain);
        mTvOpenSecond = (TextView) findViewById(R.id.mTvOpenSecond);

        mTvOpenMain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(ThirdActivity.this,MainActivity.class));
            }
        });

        mTvOpenSecond.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(ThirdActivity.this,SecondActivity.class));
            }
        });
    }
}

两个工具类

本地缓存工具类

public class CacheUtils
{
    private final static String         SP_NAME = "AlwaysCheck";
    private static SharedPreferences mPreferences;      // SharedPreferences的实例

    private static SharedPreferences getSp(Context context)
    {
        if (mPreferences == null)
        {
            mPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        }

        return mPreferences;
    }

    /**
     * 通过SP获得boolean类型的数据,没有默认为false
     *
     * @param context
     *            : 上下文
     * @param key
     *            : 存储的key
     * @return
     */
    public static boolean getBoolean(Context context, String key)
    {
        SharedPreferences sp = getSp(context);
        return sp.getBoolean(key, false);
    }

    /**
     * 通过SP获得boolean类型的数据,没有默认为false
     *
     * @param context
     *            : 上下文
     * @param key
     *            : 存储的key
     * @param defValue
     *            : 默认值
     * @return
     */
    public static boolean getBoolean(Context context, String key, boolean defValue)
    {
        SharedPreferences sp = getSp(context);
        return sp.getBoolean(key, defValue);
    }

    /**
     * 设置int的缓存数据
     *
     * @param context
     * @param key
     *            :缓存对应的key
     * @param value
     *            :缓存对应的值
     */
    public static void setBoolean(Context context, String key, boolean value)
    {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putBoolean(key, value);
        edit.commit();
    }

    public static int getInt(Context context, String key, int defValue)
    {
        SharedPreferences sp = getSp(context);
        return sp.getInt(key, defValue);
    }

    public static String getString(Context context, String key, String defValue)
    {
        SharedPreferences sp = getSp(context);
        return sp.getString(key, defValue);
    }

    /**
     * 设置int的缓存数据
     *
     * @param context
     * @param key
     *            :缓存对应的key
     * @param value
     *            :缓存对应的值
     */
    public static void setInt(Context context, String key, int value)
    {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putInt(key, value);
        edit.commit();
    }



    public static void setString(Context context, String key, String value)
    {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putString(key, value);
        edit.commit();
    }


    public static void setInt(Context context, String key, String value)
    {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putString(key, value);
        edit.commit();
    }

}

.
.
判断服务是否存活的工具类

public class ServiceUtils {

    /**
     * 判断某个服务是否正在运行的方法
     *
     * @param mContext
     * @param serviceName 是包名+服务的类名(例如:aa.bb.com.HomeWatchService)
     * @return true代表正在运行,false代表服务没有正在运行
     */
    public static boolean isServiceWork(Context mContext, String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager) mContext
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(40);
        if (myList.size() <= 0) {
            return false;
        }
        for (int i = 0; i < myList.size(); i++) {
            String mName = myList.get(i).service.getClassName().toString();
            if (mName.equals(serviceName)) {
                isWork = true;
                break;
            }
        }
        return isWork;
    }
}

下载链接

本篇完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容