安卓项目总结——基于喜马拉雅SDK的安卓项目练习

写在前面

本APP代码已放在了github地址,需要的可以到这里去下载。

正文

  1. 需要导入网络的相关依赖:

    // 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'
    
  2. 出现了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">
    

    还有另外一种设置方法,可以去百度查询一下。

  3. 在创建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);

至于这二者的差别,根据百度的相关介绍,

具体的我也看不太懂,先做记录。

  1. 在写代码的过程中,我们需要把逻辑层的代码和UI层的代码分开(解耦),这样做一是可以让我们的代码思路更加清晰,二是更加安全,并且可以更好的复用代码。

    要实现解耦,首先我我们要定义一个获取接口来把需要的方法都写入,再提供回调接口,并在获取接口中提供对回调接口的注册和释放方法。之后我们书写获取接口的实现类,写入获取数据的逻辑代码。我们在activty或者fragment用到的时候,直接创建一个逻辑层接口(获取接口)的对象,并调用其对象的方法,然后注册回调接口,并实现回调即可。当回调成功后,我们就直接更新UI就可以了。

    其实仔细想一想,在安卓开发中回调这个概念用到的很多。这样的解释未免还是太过抽象了,还是要在实际开发中养成这样的习惯,不断优化自己的代码规范,从而加深自己的理解。

  2. 在我们写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);
    
        }
    

    这样的效果是我们可以让入口唯一,即都调用第三个构造方法。

  3. 使用UILoader的思路整理:

    我们首先发起网络请求,在请求前设置为加载中的UI,在请求成功和失败后各自根据条件判断加载哪个view(都调用callback里的方法),在主fragment中实现这几个callback的方法,调用UILoader来更新UI,UILoader截获请求后根据传入的状态来判断显示哪个。

  4. 隐藏导航栏,设置状态栏颜色为透明的代码:

    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
     getWindow().setStatusBarColor(Color.TRANSPARENT);
    
  5. 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();
    
        }
    }
    
  6. 在使用SharedPreference时,使用commit方法提交数据IDE提示考虑使用apply,于是百度了一下二者的差别:commit是同步提交,且有返回值表示是否提交成功,apply是异步提交,且无返回值。而SharedPreference是单实例,一般不会出现并发冲突,故如果对提交结果不关心建议使用apply,如果关心提交结果且需要同步提交的话可以使用commit。

  7. 在返回界面时,由于没有更新状态,需要在presenter层里写一个更新UI和进度条的方法:

        private void handlePlayState(IPlayerCallback iPlayerCallback) {
            int playerStatus = mPlayerManager.getPlayerStatus();
            // 根据状态调用接口的方法
            if (PlayerConstants.STATE_STARTED == playerStatus) {
                iPlayerCallback.onPlayStart();
            }else{
                iPlayerCallback.onPlayStop();
            }
        }
    
  8. 想要设置PopupWindow能够点击外部关闭的效果,可以使用如下代码:

            // 这里要注意:设置setOutsideTouchable要先设置:setBackgroundDrawable
            // 否则点击外部无法关闭pop
            setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            setOutsideTouchable(true);
    

    这里需要注意,最好先设置一下setBackgroundDrawable,否则可能会出现无法关闭的Bug

  9. 实现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);
        }
    
  10. 判空的重要性:在我们开发APP的时候,对于不确定的结果一定要判空处理,如该项目中,若用户网速太慢,没有正确的获取到图片路径,那么就会导致程序崩溃。一定要对容易为空的内容进行一个判空处理。

  11. 隐藏键盘的代码如下:

            // 隐藏键盘
            InputMethodManager imm = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(mInputBox.getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
    

    其中的HIDE_IMPLICIT_ONLY为一个常量,是该方法的默认使用值。

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

原文地址:https://www.cnblogs.com/wushenjiang/p/13428179.html