Android單一實(shí)例全局可調(diào)用網(wǎng)絡(luò)加載彈窗
最近因?yàn)轫?xiàng)目需求,需要完成一個(gè)全局的網(wǎng)絡(luò)加載彈窗需求,真正完成這個(gè)需求之后,感覺最重要的不是結(jié)果,而是思維。
我剛開始接到這個(gè)需求的時(shí)候,第一種想到的方案是 基類加單例。但是實(shí)際做起來之后發(fā)現(xiàn),因?yàn)閱卫脑颍愕膹棿爸荒茉诘谝淮蝿?chuàng)建這個(gè)單例的activity中顯示出來。
那么發(fā)現(xiàn)這個(gè)問題之后在這個(gè)的基礎(chǔ)上改進(jìn)一下,如果我不用activity的上下文,而是采用類似于Application的一種全局上下文呢?當(dāng)然,個(gè)人能力有限,這種想法就給斃掉了,后來由導(dǎo)師指點(diǎn),利用service的上下文,dialog的style設(shè)置為系統(tǒng)級(jí)彈窗,那么這時(shí)候就會(huì)有一種潛在的情況,如果APP退到后臺(tái)的話,加載網(wǎng)絡(luò)的時(shí)候不管用戶在那個(gè)頁面,都會(huì)顯示這個(gè)彈窗,嚴(yán)重影響用戶體驗(yàn)。
后來把思路又回到起點(diǎn),需要實(shí)現(xiàn)兩個(gè)點(diǎn),一:全局可調(diào)用。二:?jiǎn)我粚?shí)例。
總結(jié)一下遇到的問題:
一、dialog必須依賴activity
二、因?yàn)閱卫脑?,dialog只能在第一次創(chuàng)建單例的activity顯示
三、不能使用系統(tǒng)級(jí)彈窗
OK,基于這些問題和要求,結(jié)合自己所掌握的知識(shí)。
dialog必須依賴activity,那我就創(chuàng)建一個(gè)activity,專門去承載這個(gè)dialog,activity背景設(shè)置為透明,效果達(dá)到。
這時(shí)又會(huì)出現(xiàn)新的問題,如果在單例中去開啟這個(gè)activity,那么就會(huì)有很多dialog對(duì)象,違反初衷,如果在單例中創(chuàng)建dialog,那么開啟activity的時(shí)候又會(huì)有很多intent對(duì)象,得不償失。解決方法,創(chuàng)建兩個(gè)單例,保證intent對(duì)象和dialog對(duì)象都保持唯一。
實(shí)際測(cè)試發(fā)現(xiàn),第一次可以正常顯示,第二次就會(huì)崩潰。
原因:當(dāng)activity被銷毀,又重新創(chuàng)建的時(shí)候,上下文會(huì)改變。因?yàn)閱卫脑?,你dialog的上下文還是第一次activity被創(chuàng)建時(shí)候的上下文,那么你再次調(diào)用這個(gè)dialog的時(shí)候,就會(huì)報(bào)activity不存在的異常。
到這里似乎沒有辦法解決了。
再次思考這個(gè)問題,突然靈光一閃,為什么我非要用dialog呢?我既然已經(jīng)創(chuàng)建出一個(gè)專門承載這個(gè)dialog的activity了,而且activity的死活是完全和dialog一致的,那么我為什么還要再去創(chuàng)建一個(gè)dialog呢?直接把dialog的布局寫在activity里不行嗎?當(dāng)外部去創(chuàng)建這個(gè)activity的時(shí)候直接播放動(dòng)畫,同時(shí)提供一個(gè)暴露給外部的關(guān)閉方法。而且這樣也能用單例保證這個(gè)activity實(shí)例的單一性。
想到就去做,經(jīng)過嘗試和優(yōu)化,問題完美解決。
下面是具體實(shí)現(xiàn)代碼:
要顯示的activity:
public class NetWaitDialogActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_net_wait_dialog); //將Activity實(shí)例添加到AppManager的堆棧 MyActivityManager.getAppManager().addActivity(this); Transparentstatusbar(); SimpleDraweeView netwait_dialog_gif = (SimpleDraweeView) findViewById(R.id.netwait_dialog_gif); //展示動(dòng)圖 DraweeController draweeController_phone_wait = Fresco.newDraweeControllerBuilder() .setAutoPlayAnimations(true) //設(shè)置uri,加載本地的gif資源 .setUri(Uri.parse("res://"+this.getPackageName()+"/"+R.drawable.wait)) .build(); netwait_dialog_gif.setController(draweeController_phone_wait); } /** * 透明狀態(tài)欄 */ private void Transparentstatusbar() { ViewGroup contentFrameLayout = (ViewGroup) findViewById(Window.ID_ANDROID_CONTENT); View parentView = contentFrameLayout.getChildAt(0); if (parentView != null && Build.VERSION.SDK_INT >= 14) { parentView.setFitsSystemWindows(true); getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } @Override protected void onDestroy() { super.onDestroy(); Log.d("網(wǎng)絡(luò)加載彈窗", "NetWaitDialogActivity.onDestroy"); } public static void dismiss(){ Log.d("網(wǎng)絡(luò)加載彈窗", "調(diào)用dismiss()方法"); if (MyActivityManager.getAppManager().isActivityExist(NetWaitDialogActivity.class)){ //結(jié)束指定類名的Activity Log.d("網(wǎng)絡(luò)加載彈窗", "調(diào)用Activity管理工具結(jié)束Activity"); MyActivityManager.getAppManager().finishActivity(NetWaitDialogActivity.class); } else { Log.d("網(wǎng)絡(luò)加載彈窗", "指定類不存在,調(diào)用備用方法"); if (((Activity)NetWaitDialogContext).isFinishing() || ((Activity)NetWaitDialogContext).isDestroyed()) { Log.d("網(wǎng)絡(luò)加載彈窗", "網(wǎng)絡(luò)加彈窗不存在"); } else { Log.d("網(wǎng)絡(luò)加載彈窗", "調(diào)用強(qiáng)制關(guān)閉"); ((Activity)NetWaitDialogContext).finish(); } } } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:fresco="http://schemas.android.com/apk/res-auto"> <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/netwait_dialog_gif" android:layout_width="360dp" android:layout_height="100dp" android:layout_centerInParent="true" fresco:roundedCornerRadius="20dp"></com.facebook.drawee.view.SimpleDraweeView> </RelativeLayout>
style.xml中創(chuàng)建透明樣式:
<!-- 網(wǎng)絡(luò)加載activity背景 --> <style name="Transparent" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@android:style/Animation</item> <item name="android:windowNoTitle">true</item> </style>
AndroidManifest.xml中設(shè)置樣式:
<activity android:name=".NetWaitDialogActivity" android:theme="@style/Transparent"></activity>
單例工具類:
public class NetWaitStatusUtils { private static NetWaitStatusUtils instance; private Intent intent; private Context context; private NetWaitStatusUtils(Context context) { this.context = context; intent = new Intent(context, NetWaitDialogActivity.class); } public static NetWaitStatusUtils getInstance(Context context) { if (instance == null) { instance = new NetWaitStatusUtils(context); } return instance; } public void show(){ context.startActivity(intent); } public void dismiss(){ NetWaitDialogActivity.dismiss(); } }
在基類中獲取實(shí)例:
netWaitDialog = NetWaitStatusUtils.getInstance(getApplication());
外部調(diào)用:
public class MainActivity extends IActivity { private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button load = findViewById(R.id.load); Button gotwo = findViewById(R.id.gotwo); load.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { netWaitDialog.show(); handler.postDelayed(new Runnable(){ @Override public void run() { netWaitDialog.dismiss(); } }, 3000); } }); gotwo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,Main2Activity.class)); finish(); } }); } @Override protected int getLayoutId() { return R.layout.activity_main; } }
這里還有一點(diǎn)需要注意,就是activity的啟動(dòng)模式,推薦使用singletask。但是這樣也會(huì)有一個(gè)弊端,就是它會(huì)把自它到棧頂?shù)乃衋ctivity實(shí)例都銷毀,具體大家可以自行百度。
我這里是用到一個(gè)activity的管理類:
package com.example.a9focus.sxt.base; import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.os.Build; import android.util.Log; import java.util.Stack; /** * Created by HXY on 18-12-1. */ public class MyActivityManager { private static Stack<Activity> activityStack; private static MyActivityManager instance; private MyActivityManager(){} /** * 單一實(shí)例 */ public static MyActivityManager getAppManager(){ if(instance==null){ instance=new MyActivityManager(); } return instance; } /** * 添加Activity到堆棧 */ public void addActivity(Activity activity){ if(activityStack==null){ activityStack=new Stack<Activity>(); } activityStack.add(activity); Log.d("MyActivityManager", activityStack.toString()); } /** * 獲取當(dāng)前Activity(堆棧中最后一個(gè)壓入的) */ public Activity currentActivity(){ Activity activity=activityStack.lastElement(); return activity; } /** * 結(jié)束當(dāng)前Activity(堆棧中最后一個(gè)壓入的) */ public void finishActivity(){ Activity activity=activityStack.lastElement(); if(activity!=null){ activity.finish(); activity=null; } } /** * 結(jié)束指定的Activity */ public void finishActivity(Activity activity){ if(activity!=null){ activityStack.remove(activity); activity.finish(); activity=null; } } /** * 結(jié)束指定類名的Activity */ public void finishActivity(Class<?> cls){ // try { for (Activity activity : activityStack) { if(activity.getClass().equals(cls) ){ finishActivity(activity); } } // }catch (Exception e){ // Log.d("MyActivityManager", "指定類不存在"); // } } /** * 判斷一個(gè)Activity 是否存在 * * @param clz * @return */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public boolean isActivityExist(Class<?> clz) { boolean res; Activity activity = getActivity(clz); if (activity!=null) Log.d("MyActivityManager", "判斷是否存在的Activity實(shí)例 --> "+activity.toString()); if (activity == null) { res = false; } else { if (activity.isFinishing() || activity.isDestroyed()) { res = false; } else { res = true; } } Log.d("MyActivityManager", "指定Activity存在狀態(tài)" + res); return res; } /** * 獲得指定activity實(shí)例 * * @param clazz Activity 的類對(duì)象 * @return */ public Activity getActivity(Class<?> clazz) { Activity returnActivity = null; for (Activity activity : activityStack) { if(activity.getClass().equals(clazz) ){ returnActivity = activity; return returnActivity; } } return null; } /** * 結(jié)束所有Activity */ public void finishAllActivity(){ for (int i = 0, size = activityStack.size(); i < size; i++){ if (null != activityStack.get(i)){ activityStack.get(i).finish(); } } activityStack.clear(); } /** * 退出應(yīng)用程序 */ public void AppExit(Context context) { try { finishAllActivity(); ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); activityMgr.restartPackage(context.getPackageName()); System.exit(0); } catch (Exception e) { e.printStackTrace(); } } }
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實(shí)現(xiàn)底部半透明彈出框PopUpWindow效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)底部半透明彈出框PopUpWindow效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Flutter實(shí)現(xiàn)自定義搜索框AppBar的示例代碼
開發(fā)中,頁面頭部為搜索樣式的設(shè)計(jì)非常常見,為了可以像系統(tǒng)AppBar那樣使用,本文將利用Flutter自定義一個(gè)搜索框,感興趣的可以了解一下2022-04-04zxing二維碼位矩陣轉(zhuǎn)換成Bitmap位圖的實(shí)戰(zhàn)教程
二維碼的應(yīng)用已經(jīng)可以說是非常廣泛了,下面這篇文章主要給大家介紹了關(guān)于zxing二維碼位矩陣轉(zhuǎn)換成Bitmap位圖的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09Android實(shí)現(xiàn)讀寫JSON數(shù)據(jù)的方法
這篇文章主要介紹了Android實(shí)現(xiàn)讀寫JSON數(shù)據(jù)的方法,以完整實(shí)例形式分析了Android解析及生成json數(shù)據(jù)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android編程實(shí)現(xiàn)Listview點(diǎn)擊展開和隱藏的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)Listview點(diǎn)擊展開和隱藏的方法,涉及Android中Listview的響應(yīng)點(diǎn)擊與樣式變換相關(guān)操作技巧,需要的朋友可以參考下2015-12-12