安卓项目总结——基于喜马拉雅SDK的安卓项目练习
写在前面
本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上我一直没有做过这方面的处理,就算做过也只是简单的做了一个访问错误界面。或许正规点的项目都会这样搞吧。通过这次学习,大概了解了安卓开发的思路,但也让我意识到自己对于某些基础组件的了解还是太少了。这几天老师的任务也下来了,要让我们学大数据方面的内容,接下来学习什么再看吧。