Android 7.1.1 25 Nougat N_MR1 牛轧糖。
示例场景:
--> 功能页E
闪屏A --> 登录B --> 主页C --> 功能页D
--> 功能页F
快捷菜单--------------------------> 功能页D
正常进入C后,A和B都是销毁(finish)的。C保持存活,C退出即程序结束。
从C可以进入D或者E或者F。然后从桌面快捷菜单可以直接进入D。
●情景1:程序未启动,快捷进入D,即启动程序并进入D,退出D,则进入C。
相当于把D作为一个入口了。
●情景2:程序启动了,在A或B,快捷进入D,退出D,则销毁A/B进入C。
相当于创建一个D到栈顶,退出后清空栈并创建C。
●情景3:程序启动了,在C/E/F,快捷进入D,退出D,则回到C/E/F。
相当于创建一个D到栈顶,退出后移出栈顶。
●情景4:程序启动了,在D,快捷进入D,退出D,则回到C。
相当于D已经在栈顶了,快捷启动时打开这个D。
▶重复快捷启动,可以单纯依靠Intent的flag避免再次创建D。
退出D回到上一个Activity,则要判断栈的情况。并且也要避免重复创建。
1.工具类
只存储2个元素,且须要手动保证顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/** * 固定长度的集合 * 新加入元素导致数量超出时,自动移除最早加入的 * * @param <E> */ public class UtilLimitList<E> extends ArrayList<E> { private static final int maxSize = 2; // 最大元素数量 public UtilLimitList() { super(maxSize); for (int i = 0; i < maxSize; i++) { super.add(null); } } public boolean add(E e) {// 达到长度先出队 E second = getSecond(); set(0, second); // 移除首位索引的元素 set(1, e); // 加到末尾 return true; // 本处return无意义 } /** * @param index 0-最早的,1-最近的 * @param e * @return */ public E set(int index, E e) { if (index > maxSize - 1) { throw new IndexOutOfBoundsException("超出最大容量" + maxSize + "的位置:" + index); } return super.set(index, e); } // 最早的 public E getFirst() { return get(0); } // 最近的 public E getSecond() { return get(1); } public void reverse() { E f = getFirst(); E s = getSecond(); set(0, s); set(1, f); } } |
2.上下文
这里监听Activity的运行状态,保存最近2个进入后台的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
public class App extends Application { // 最近的Activity private static Activity historyActivity = null; // 最近两个Activity private static UtilLimitList<Activity> limitList = new UtilLimitList(); @Override public void onCreate() { super.onCreate(); Log.i("ard", "aty-app onCreate 应用启动"); registerActivityLifecycleCallbacks(baseActivityLifecycleCallback); } public static Activity getHistoryActivity() { return historyActivity; } public static UtilLimitList getLastActivity() { return limitList; } private ActivityLifecycleCallbacks baseActivityLifecycleCallback = new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { Log.i("ard", "aty-app onActivityCreated:" + activity.getLocalClassName()); } @Override public void onActivityStarted(@NonNull Activity activity) { Log.i("ard", "aty-app onActivityStarted:" + activity.getLocalClassName()); } @Override public void onActivityResumed(@NonNull Activity activity) { Log.i("ard", "aty-app onActivityResumed:" + activity.getLocalClassName()); } @Override public void onActivityPaused(@NonNull Activity activity) { Log.i("ard", "aty-app onActivityPaused:" + activity.getLocalClassName()); } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { Log.i("ard", "aty-app onActivitySaveInstanceState:" + activity.getLocalClassName()); } @Override public void onActivityStopped(@NonNull Activity activity) { String current = activity.getLocalClassName(); Log.i("ard", "aty-app onActivityStopped:" + current); historyActivity = activity; Log.i("ard", "后台的aty:" + current); Activity second = limitList.getSecond(); if (null != second) { String history = second.getLocalClassName(); if (current.equals(history)) { // do nth. 连续两次从桌面快捷菜单启动。 } else { limitList.add(activity); } } else { limitList.add(activity); } } @Override public void onActivityDestroyed(@NonNull Activity activity) { String current = activity.getLocalClassName(); Log.i("ard", "aty-app onActivityDestroyed:" + current); if (null != historyActivity) { String history = historyActivity.getLocalClassName(); if (current.equals(history)) { historyActivity = null; } } Log.i("ard", "后台的aty:" + (null == historyActivity ? "null" : historyActivity.getLocalClassName())); // A进入B,A销毁 // B进入C,B销毁 // 销毁的从历史记录移除 // // D/E/F返回C,D/E/F也销毁 // 并从历史记录移除 Activity first = limitList.getFirst(); Activity second = limitList.getSecond(); if (null != first) { String history = first.getLocalClassName(); if (current.equals(history)) { limitList.set(0, null); } } if (null != second) { String history = second.getLocalClassName(); if (current.equals(history)) { limitList.set(1, null); } } // 保证最近的优先为非null Activity last = limitList.getSecond(); if (null == last) { limitList.reverse(); } } }; } |
3.快捷菜单
这里是动态创建。可以放在上下文、闪屏,或者交互事件里。运行一次即可,程序退出后快捷方式不会移出,除非调用manager remove了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
private ShortcutManager shortcutManager; private final String desktopMenuId = "ACTIVITY_D_MENU_ID"; // 添加桌面快捷菜单 private void addDesktopMenu() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { Context context = getApplicationContext(); //shortcutManager = (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE); shortcutManager = context.getSystemService(ShortcutManager.class); if (null != shortcutManager) { // 一般流程里不放入这个data Uri uri = Uri.parse(D.DATA_FROM); // 协议 // action:ACTION_MAIN // uri:自定义的协议,可区分是桌面启动的;还是从C一般启动的 // packageContext, // Class<?> cls Intent intent = new Intent(Intent.ACTION_MAIN, uri, context, D.class); // 如果应用已经是从桌面快捷方式启动的目标aty进入的,再次调用菜单启动时, // 【1】FLAG_ACTIVITY_REORDER_TO_FRONT,不会进入目标aty,仅将app唤到前台。 // intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); // 【2】FLAG_ACTIVITY_CLEAR_TOP,可再次调起目标aty。但如果目标aty已经在顶,则onDestory销毁,重新onCreate创建 // intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 【3】加上FLAG_ACTIVITY_SINGLE_TOP,可再次调起目标aty。 // 如果目标不存在,则onCreate,onResume一般流程启动; // 如果目标aty已经在顶,则onNewIntent,onRestart,onStart,onResume 重新启动 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); ShortcutInfo shortcut = new ShortcutInfo.Builder(context, desktopMenuId) // 菜单项的id .setShortLabel("直接启动D界面") // 菜单的文字 .setShortLabel("直接启动D界面") // .setIcon(Icon.createWithResource(context, R.mipmap.ic_launcher)) // 图标 .setIntent(intent) // 跳转的目标,定义Activity .build(); shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut)); } } } // 移除桌面快捷菜单 private void delDesktopMenu() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { if (null != shortcutManager) { shortcutManager.removeDynamicShortcuts(Arrays.asList(desktopMenuId)); // 菜单项的id标识 } } } |
4.目标Activity
进入不复杂,退出真复杂。
业务上,如果通过快捷菜单进入,退出时要判断后台是A/B,C,D,E/F的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
public class D extends LogAty { // LogAty用于打印生命周期 public static final String DATA_FROM = "from:desktop"; private Uri dataFrom; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_d); dataFrom = getIntent().getData(); if (null != dataFrom && dataFrom.toString().equals(DATA_FROM)) { Log.e("ard", "来自桌面"); } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Uri data = getIntent().getData(); Log.e("ard", "data:" + data); // 如果Activity是一般流程启动的,则此时data是null。 // 应对 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); setIntent(intent); } @Override public void finish() { // 如果是从桌面快捷菜单进来的 if (null != dataFrom && dataFrom.toString().equals(DATA_FROM)) { Log.e("ard", "准备启动main或其它已运行的aty"); // normalResumeActivity(); advancedResumeActivity(); } super.finish(); } // 一般性的返回。历史栈顶只保留1个。 // 如果打开的是A或B、E或F,然后从桌面快捷启动1次,正常返回, // 如果多次快捷启动,则创建新栈启动C。 private void normalResumeActivity() { Activity historyActivity = App.getHistoryActivity(); Intent intent; if (null == historyActivity) { // null说明app是未启动状态。即首次从快捷菜单启动 Log.e("ard", "没有历史activity,前往C。"); intent = getC(); // 进入主页 } else { // app是运行状态,有activity存在了 String c = C.class.getName(); String d = D.class.getName(); String history = historyActivity.getComponentName().getClassName(); if (c.equals(history)) { // 如果上个界面是主页C Log.e("ard", "主页C是后台activity,返回。"); return; } else if (d.equals(history)) { // 如果连续两次从快捷方式启动D,后台activity就成了D。不管是不是已经一般流程启动到C了,都打开新的栈启动C Log.e("ard", "后台activity是D,前往C。"); intent = getC(); } else { // 其它情况,后台为A/B,E/F。如果单次快捷启动,然后返回,则进入这里回到原界面 Log.e("ard", "后台activity是:" + history); intent = getOther(historyActivity); } } ResolveInfo resolveInfo = getPackageManager().resolveActivity(intent, 0); if (null != resolveInfo) { startActivity(intent); } } // 历史栈顶保留最近2个 // 且不重复!即连续快捷启动只保留1次。 public void advancedResumeActivity() { UtilLimitList<Activity> lastActivityList = App.getLastActivity(); Activity first = lastActivityList.getFirst(); Activity second = lastActivityList.getSecond(); Log.e("ard", "后台aty,最早的:" + first + ",最近的:" + second); Intent intent; if (null == first) { // 这里在BaseApplication保证second总是优先于first不为null if (null == second) { intent = getC(); // first和second都为null。即首次从快捷菜单启动 } else { String main = C.class.getName(); String history = second.getComponentName().getClassName(); if (main.equals(history)) { // 没有更早的Activity了,只有最近的主页C,则唤起就好 intent = getOther(second); } else { // 没有更早的Activity了,有最近的 但不是主页C,则启动主页C,即后台是A或B的情况。 intent = getC(); } } } else { intent = getOther(second); // 如果最早的不为null。则最近的也不是null,同时存在2级历史Activity即主页C启动D/E/F的情况,唤起即可。 } ResolveInfo resolveInfo = getPackageManager().resolveActivity(intent, 0); if (null != resolveInfo) { startActivity(intent); } } private Intent getC() { Intent intent = new Intent(this, C.class); // 主页C // CLEAR_TASK和NEW_TASK 同时使用,先移除栈底的activity。然后创建新栈,创建C。 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } private Intent getOther(Activity history) { String currentClassName = getComponentName().getClassName(); Log.i("ard", "当前要退出的aty:" + currentClassName); String lastClassName = history.getComponentName().getClassName(); Intent intent = new Intent(); // 主页Main 还是 轨迹列表Traces? intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); intent.setClassName(getApplicationContext(), lastClassName); return intent; } } |
- end
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/4220.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设