寫在前面
本APP代碼已放在了github地址,需要的可以到這里去下載。
正文
-
需要導入網絡的相關依賴:
// SDK在解析請求返回的JSON數據時用到 api 'com.google.code.gson:gson:2.8.1' // SDK聯網框架使用okhttp api 'com.squareup.okhttp3:okhttp:3.11.0' // SDK聯網框架使用okhttp api 'com.squareup.okio:okio:1.15.0' // v4包 api 'com.android.support:support-v4:28.0.0'
-
出現了604網絡請求錯誤,首先檢查是否導入了網絡權限:
<!--連接網絡--> <uses-permission android:name="android.permission.INTERNET" /> <!--用於管理監聽網絡狀態的變化--> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!--用於管理監聽網絡狀態的變化--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!--用於管理監聽網絡狀態的變化--> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!--獲取電話的狀態,在打電話的時候暫停播放--> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!--target >=28 需要設置此權限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
然后再檢查自己的API版本,如果是大於27的版本(在build.grade里,如下圖)
那么已經不再支持http請求了(即默認的請求訪問),需要切換成https請求:
CommonRequest mXimalaya = CommonRequest.getInstanse(); mXimalaya.setUseHttps(true);
或者直接設置支持明文訪問:(在manifest.xml里)
<application android:usesCleartextTraffic="true">
還有另外一種設置方法,可以去百度查詢一下。
-
在創建MainContentAdapter時候,使用了AndroidX后,舊版的FragmentPagerAdapter的構造方法被標注過時,建議我們使用新版的:
public MainContentAdapter(@NonNull FragmentManager fm, int behavior) { super(fm, behavior); }
這里需要多傳一個參數behavior,我們點開源碼查看注釋,發現有兩種behavior:
一種是已經標注過時的,一種是新版的。我們傳入新版的:
MainContentAdapter mainContentAdapter = new MainContentAdapter(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
至於這二者的差別,根據百度的相關介紹,
具體的我也看不太懂,先做記錄。
-
在寫代碼的過程中,我們需要把邏輯層的代碼和UI層的代碼分開(解耦),這樣做一是可以讓我們的代碼思路更加清晰,二是更加安全,並且可以更好的復用代碼。
要實現解耦,首先我我們要定義一個獲取接口來把需要的方法都寫入,再提供回調接口,並在獲取接口中提供對回調接口的注冊和釋放方法。之后我們書寫獲取接口的實現類,寫入獲取數據的邏輯代碼。我們在activty或者fragment用到的時候,直接創建一個邏輯層接口(獲取接口)的對象,並調用其對象的方法,然后注冊回調接口,並實現回調即可。當回調成功后,我們就直接更新UI就可以了。
其實仔細想一想,在安卓開發中回調這個概念用到的很多。這樣的解釋未免還是太過抽象了,還是要在實際開發中養成這樣的習慣,不斷優化自己的代碼規范,從而加深自己的理解。
-
在我們寫UILoader時,我們把前兩個構造方法都改成了this的方式,如下:
public UILoader(@NonNull Context context) { this(context,null); } public UILoader(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } public UILoader(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
這樣的效果是我們可以讓入口唯一,即都調用第三個構造方法。
-
使用UILoader的思路整理:
我們首先發起網絡請求,在請求前設置為加載中的UI,在請求成功和失敗后各自根據條件判斷加載哪個view(都調用callback里的方法),在主fragment中實現這幾個callback的方法,調用UILoader來更新UI,UILoader截獲請求后根據傳入的狀態來判斷顯示哪個。
-
隱藏導航欄,設置狀態欄顏色為透明的代碼:
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); getWindow().setStatusBarColor(Color.TRANSPARENT);
-
Toast去掉前面的應用名稱的方法:我們可以先生成一個Toast對象,通過對這個Toast對象實例設置text再顯示就不會有應用名了,可以直接使用下面的工具類:
/** * FileName: ToastUtil * Author: LiuGe * Date: 2020/7/29 18:15 * Description: Toast工具類 */ public class ToastUtil { public static void showShort(Context context, CharSequence message) { Toast toast = Toast.makeText(context, null, Toast.LENGTH_SHORT); toast.setText(message); toast.show(); } public static void showLong(Context context, CharSequence message) { Toast toast = Toast.makeText(context, null, Toast.LENGTH_LONG); toast.setText(message); toast.show(); } }
-
在使用SharedPreference時,使用commit方法提交數據IDE提示考慮使用apply,於是百度了一下二者的差別:commit是同步提交,且有返回值表示是否提交成功,apply是異步提交,且無返回值。而SharedPreference是單實例,一般不會出現並發沖突,故如果對提交結果不關心建議使用apply,如果關心提交結果且需要同步提交的話可以使用commit。
-
在返回界面時,由於沒有更新狀態,需要在presenter層里寫一個更新UI和進度條的方法:
private void handlePlayState(IPlayerCallback iPlayerCallback) { int playerStatus = mPlayerManager.getPlayerStatus(); // 根據狀態調用接口的方法 if (PlayerConstants.STATE_STARTED == playerStatus) { iPlayerCallback.onPlayStart(); }else{ iPlayerCallback.onPlayStop(); } }
-
想要設置PopupWindow能夠點擊外部關閉的效果,可以使用如下代碼:
// 這里要注意:設置setOutsideTouchable要先設置:setBackgroundDrawable // 否則點擊外部無法關閉pop setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); setOutsideTouchable(true);
這里需要注意,最好先設置一下setBackgroundDrawable,否則可能會出現無法關閉的Bug
-
實現PopupWindow的顯示后有一定透明度的思路:獲取Window對象,對其中的attributes里的alpha屬性做一個設置(alpha屬性:透明度),如下代碼:
/** * 設置彈出框的透明度 * @param alpha */ public void updateBgAlpha(float alpha){ Window window = getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); attributes.alpha = alpha; window.setAttributes(attributes); }
-
判空的重要性:在我們開發APP的時候,對於不確定的結果一定要判空處理,如該項目中,若用戶網速太慢,沒有正確的獲取到圖片路徑,那么就會導致程序崩潰。一定要對容易為空的內容進行一個判空處理。
-
隱藏鍵盤的代碼如下:
// 隱藏鍵盤 InputMethodManager imm = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mInputBox.getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
其中的HIDE_IMPLICIT_ONLY為一個常量,是該方法的默認使用值。
-
在做訂閱和取消訂閱功能時,出了一個Bug:點擊訂閱后UI會更改,但實際上只更新了UI,點擊取消訂閱時還是會彈出訂閱成功,因為只更新了UI,沒有更新presenter里保存的map,需要在presenter的添加刪除方法里在更新UI前先查詢數據庫,更新里面的map:
@Override public void onAddResult(final boolean isSuccess) { // 調用查詢方法,更新mData,確保能正確更新UI listSubscriptions(); // 添加結果的回調 BaseApplication.getHandler().post(new Runnable() { @Override public void run() { for (ISubscriptionCallback callback : mCallbacks) { callback.onAddResult(isSuccess); } } }); }
(應該是我在跟着視頻敲的時候漏寫了。)
總結
總的來說,安卓的開發與web的開發相差還是很大的,在安卓開發中,需要考慮很多的情況,而在web中很多情況都會被瀏覽器處理掉,如沒有數據,網絡錯誤,數據為空等等這都需要開發者去處理,但在web上我一直沒有做過這方面的處理,就算做過也只是簡單的做了一個訪問錯誤界面。或許正規點的項目都會這樣搞吧。通過這次學習,大概了解了安卓開發的思路,但也讓我意識到自己對於某些基礎組件的了解還是太少了。這幾天老師的任務也下來了,要讓我們學大數據方面的內容,接下來學習什么再看吧。