精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
關于service啟動Activity
前言
在討論今天的內容之前,我們先看一段代碼:
TextService.java
Intent intent = new Intent(this, SecondActivity.class);this.startActivity(intent);
請問?上述代碼是否正確?
沒錯,這就是一個簡單的Activity啟動。此時,機智的童鞋A默默的祭出demo大法,安裝至自己的魅族16th(android8.0)然后發現流暢運行,毫無障礙。然后童鞋B飛速效仿之,一個分毫不差的demo在低端三星機(Android4.4)上安裝后,結果被無情的送了一個異常。
java.lang.RuntimeException: Error receiving broadcast Intent { act=com....}...Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
從上面報錯信息我們可以知道Intent缺少FLAG_ACTIVITY_NEW_TASK flag信息,添加后解決。
Intent intent = new Intent(this, SecondActivity.class);intent .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); this.startActivity(intent);
正文
那么,看到這里就有了一些疑問。
為什么Activity啟動Activity時就沒有需不需要添加的問題
為什么Service有時候需要添加flag,有的不需要添加?
什么時候需要添加而什么時候不用添加?或者說到底添加是對的還是不添加是對?
添加了以后和正常啟動有什么區別,會造成哪些影響。
為什么這么設計。
一、 Context的繼承關系圖
首先我們來看一張圖,這張圖表示了Context里的基本繼承關系。
Context繼承關系圖
我們可以看到:
最上面的是Context,它其實是一個抽象類,他有兩個重要的子類ContextImpl和ContextWrapper。
ContextImpl,是Context功能實現的主要類。
ContextWrapper,顧名思義它就是一個包裝類,主要功能都是通過調用ContextImpl去實現的。
ContextThemeWrapper,包括一些主題的包裝,由于
Service沒有主題,所以直接繼承ContextWrapper;但是Activity就需要繼承ContextThemeWrapper。
二、啟動activity方法startActivity
看了上面的Context繼承圖,相信大家對為什么Activity啟動Activity和Service啟動Activity有差異有了一定的想法。那么到底差異在哪里呢?
首先我們看下Activity是如何啟動的,當Activity啟動Activity時會調用Activity.startActivity(intent)
Activity @Override public void startActivity(Intent intent) { this.startActivity(intent, null); } //最終會調 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); ......//省略無關代碼 } else { ......//省略無關代碼 } }
這里的mParent是ActivityThread.scheduleLaunchActivity()里的ActivityClientRecord傳給他的,默認為空。而mInstrumentation.execStartActivity方法就表示開始啟動Activity了。
上面我們可以看到,activity調用startActivity時并不需要flag。但是service到底是需要還是不需要flag呢?為什么會出現有時候需要有時候不需要的情況?
來!關門,放狗!
不好意思!是放圖!
Activity啟動權限圖
上圖是Activity啟動權限圖,我們可以看到,除了Activiy以外其他組件都是不允許啟動Activity的。但是上述A同學的魅族16th(android8.0)卻能流暢運行,這是為什么呢?
那么Sevice啟動Activity的流程具體到底是怎樣呢?來看一下源碼:
像Service,BroadcastReceiver,Application等都是ContextWrapper的子類,調用startActivity()時會調用ContextWrapper的startActivity()
ContextWrapper @Override public void startActivity(Intent intent) { mBase.startActivity(intent); }
這個mBase就是ContextImpl.這個ContextImpl是在ActivityThread里賦值的:
Activity-->ActivityThread::performLaunchActivity()
Service-->ActivityThread::handleCreateService()
Application-->LoadedApk::makeApplication()
有興趣的同學自行查看.
那我們來看下ContextImpl的startActivity().
先看低版本的(android6.0)
ContextImpl @Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); } @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1, options); }
我們只需要關注這個判斷條件(intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0。很簡單,就是非Activity的Context啟動Activity時如果不給intent設置FLAG_ACTIVITY_NEW_TASK就會報錯。那我們就可以通過給intent設置FLAG_ACTIVITY_NEW_TASK,來解決B同學的問題。
然后我們來看看高版本(android 8.0)
ContextImpl @Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); } @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }
和上面的android6.0相比。判斷條件里多了一些判斷options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1這里因為options這個參數傳入的就是null,也就是說options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1為false。會直接繞過異常拋出,這里應該是一個bug。就算不給intent設置FLAG_ACTIVITY_NEW_TASK,也會順利啟動。查了一下,這個更改是從android(7.0)開始的。
android(9.0)
ContextImpl @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We // maintain this for backwards compatibility. final int targetSdkVersion = getApplicationInfo().targetSdkVersion; if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }
查看android(9.0)源碼,這里判斷條件修改為(intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)貌似已經解決了這個問題。暫時沒有條件測試,有條件的同學可以測試一下。
通過上面我們可以看到,其實是因為google的大佬,在android7.0 android8.0版本出現了一個bug導致程序邏輯繞過了flag參數的驗證。所以,對應的正確邏輯應該如Activity啟動權限圖所示,其他組件要啟動actvicity是需要添加flag標記的。
FLAG_ACTIVITY_NEW_TASK導致的問題
既然添加了flag后其他組件就可以正常調用startActivity啟動Activity。那為什么activity啟動權限表上除了Activity其他組件都是NO!這樣調用與我們正常使用Activity調用有什么區別呢?會不會出現一些其他問題?答案是會!
首先我們需要搞清楚,我們添加的flag的作用。給Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)就代表著,啟動的Activity會位于一個新的Task。舉個例子,如果你在電話本里啟動的,那么你的最近任務列表就會有兩個電話本,因為有兩個task嘛。那怎么解決呢?
很簡單,將需要其他組件啟動的Activity配置屬性android:excludeFromRecents=”true”,那么該Activity將不會顯示在最近任務列表中。
android 為什么要這樣設計
為什么要將activity與其他組件區分對待呢?同樣的我們舉個栗子。
假設:我們有這樣一個需求:
我們的電話本里有一個Service,然后它執行5分鐘后,將啟動一個Activity.那么很有可能5分鐘以后已經不在電話本頁面了,假設在瀏覽器頁面了,此時的Task就是瀏覽器Task,如果這個Activity在當前Task的話,也就是在瀏覽器Task的話。那么用戶會感覺莫名其妙,因為這個Activity本來是屬于電話本的。所以對于Service而言,干脆強制定義啟動的Activity需要創建一個新的Task,這樣設計會比較合理。
Service啟動Dialog
由于Dialog是依賴于Activity存在的,所以service啟動Dialog主要有兩種方法:
首先啟動一個半透明的Activity,然后在Activity里啟動Dialog。
使用WindowManage實現
使用WindowManage應該注意,此時的Dialog是SYSTEM級別的,如果程序在后臺啟動這個Dialog,Dialog會浮在桌面上。(使用小米等有自己權限管理的系統時,需要申請一定權限才可以在桌面顯示這個 Dialog,否則只能在自己 APP 前臺時才顯示)
使用WindowsManage實現
AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setMessage("是否接受文件?").setPositiveButton("是", new DialogInterface.OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {}}).setNegativeButton("否", new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {}});AlertDialog ad = builder.create();// ad.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); //系統中關機對話框就是這個屬性ad.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);ad.setCanceledOnTouchOutside(false); //點擊外面區域不會讓dialog消失ad.show();
權限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />11
使用Activity實現
Activity主題
<style name="DialogTransparent" parent="@android:style/Theme.Dialog"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowAnimationStyle">@android:style/Animation</item> <item name="android:windowNoTitle">true</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowIsFloating">false</item> <item name="android:windowIsTranslucent">true</item> </style>
或者直接使用
@android:style/Theme.Dialog
文章來自:
https://www.jianshu.com/p/aa4ec93cc194
https://blog.csdn.net/fang323619/article/details/74388804
本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
總結篇之三:Activity的task相關
android Notification 的使用
Android之解決在非Activity中使用startActivity
Android面試題
友盟推送通知欄問題
對Android近期任務列表(Recent Applications)的簡單分析
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 新野县| 哈尔滨市| 永福县| 黔西| 上饶县| 临朐县| 太谷县| 巴里| 潞城市| 南开区| 雅安市| 深圳市| 濮阳县| 根河市| 连江县| 澄江县| 马关县| 仁化县| 黄骅市| 甘泉县| 平昌县| 石首市| 板桥市| 毕节市| 子长县| 乌苏市| 秀山| 遵化市| 凉城县| 安塞县| 哈密市| 公安县| 乌拉特后旗| 博野县| 巴林左旗| 陕西省| 滨州市| 宜兴市| 明溪县| 北京市| 清原|