栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

打造一款高性能的IM+直播app

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

打造一款高性能的IM+直播app

  • 项目注意事项

    • 主题色
    • 打包
    • 原生项目地址
    • h5项目地址
    • 减小apk安装包
  • 链信公开课功能架构

  • 叽喳功能架构

  • MUI项目注意事项

  • 云信IM

    • IM即时通讯
    • 音视频通话1.0
    • 直播
    • 互动直播1.0
    • 点播
    • 互动白板
    • IM常规配置
      • 币圈
      • 币圈背景
      • 群投诉,举报
      • 初次登陆,头像编辑
      • 直播课件编辑
      • 视频课视频
  • 优化

    • 减小apk安装包
    • notifyDataSetChanged导致图片闪烁解决方案
  • 引入的第三方库

    • ijkPlayer
    • ExoPlayer
    • EventBus
    • Glide
    • Retrofit
    • Okhttp
    • 云信IM
      • arm64-v8a
        • libnrtc_mp4v2.so
        • libnrtc_sdk.so
        • librts_network.so
        • libyxbase.so
      • armeabi-v7a
        • libnrtc_mp4v2.so
        • libnrtc_sdk.so
        • librts_network.so
        • libyxbase.so
      • nim-basesdk-7.9.0.jar
      • nim-avchat-7.9.0.jar
      • nim-chatroom-7.9.0.jar
      • nim-push-7.9.0.jar
      • nim-rts-7.9.0.jar
      • nrtc-sdk.jar
      • 图片加载框架 universal-image-loader-1.9.4.jar
      • 云信红包钱包 JrmfRpWallet-2.2.0.jar
      • 推送
        • 小米推送 MiPush_SDK_Client_3_7_0.jar
        • vivo推送 vivo_pushsdk_v2.3.4.jar
        • oppo推送 com.coloros.mcssdk.jar
        • 华为推送 com.huawei.agconnect:agcp:1.4.1.300
    • ffmpeg
    • uni SDK
      • audio-mp3aac-release.aar(在app)
      • gallery-dmcBig-release.aar (在app 已废弃,只在叽喳使用)
      • lib.5plus.base-release.aar(在app)
      • media-release.aar(在app)
      • msa_mdid_1.0.13.aar(在app Android10安全联盟)
      • oauth-weixin-release.aar(在app)
      • payment-weixin-release.aar(在app)
      • share-weixin-release.aar(在app)
      • uniapp-v8-release.aar (在uikit)
    • mui SDK
      • lib.5plus.base-release-gchat-adapt-mui.aar (只在叽喳使用)
    • 阿里SDK
      • 高德地图 AMap2DMap_3.0.0_AMapSearch_3.6.1_AMapLocation_3.2.1_20161228.jar
      • fastjson-1.1.71.android.jar
    • 腾讯SDK
      • X5webview tbs_sdk_thirdapp_v4.3.0.3_43903_sharewithdownloadwithfile_withoutGame_obfs_20200402_121309.jar
      • 支付、登录、分享 com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+
    • 音频转码 ffmpeg com.github.microshow:RxFFmpeg:4.8.0-lite
    • [组件化 io.github.prototypez:app-joint-core:1.6.1](#组件化 io.github.prototypez:app-joint-core:1.6.1)
    • 权限请求 pub.devrel:easypermissions:0.3.0
    • butterknife com.jakewharton:butterknife-compiler:10.2.1
    • 流式布局 com.hyman:flowlayout-lib:1.1.2
  • 主要用到的技术

    • RecyclerView
    • Glide
    • Retrofit
    • Okhttp
    • View
      • View工作流程
        • measure
        • layout
        • draw
      • View测量模式
        • UNSPECIFIED
        • EXACTLY
        • AT_MOST
      • Canvas
      • Paint
      • ViewRoot与DecorView
    • EventBus
    • WebRtc
    • rtmp
    • Mediaplayer
    • 音视频直播
    • MediaCodec
    • MVC架构
    • MVVM架构
    • 设计模式
      • 单例
        • 饿汉式单例
        • 懒汉式单例
      • 观察者模式
      • 责任链模式
      • 代理模式
      • 工厂模式
      • 建造者模式
    • java反射
    • 类加载机制
  • 适配

    • 折叠屏
    • [uni app底部白条解决方案](#uni app底部白条解决方案)
  • 主题色:

  • 打包:

  • 原生项目地址:

  • h5项目地址:

  • uni项目注意事项:

  • apk:

  • 云信:

  • uni SDK:

  • ThirdPart

  • 折叠屏:

  • android 国际化多语言:

  • EventBus

  • ThirdPart

  • 折叠屏:

  • android 国际化多语言

  • android 多渠道(马甲包)开发

  • 解决notifyDataSetChanged时导致图片闪烁

  • Material Design

  • View

  • Java反射

  • ffmpeg

  • 云信IM

主题色:

链信公开课:

  • 主题色: #FF8048
  • 深主题色:#FB5E19

叽喳:

  • 主题色: #39A6FF
  • 深主题色:#308CD8
打包:

叽喳:

  • idea左侧 Build Variants 切换到 gchat
  • 更新 app/src/gchat/assets/apps/com.wggs.gchat/www(h5路径) 下的h5代码
  • 配置h5路径下的 js/config 正式或测试环境,h5路径下的manifest.json 版本号
  • 配置根目录下 gradle.properties 正式或测试环境
  • 还原叽喳代码SDK.startWebApp 全局搜:叽喳需要打开此注释

链信公开课:

  1. idea左侧 Build Variants 切换到 lianxinClass
  2. 将打包后uni包放到 app/src/lianxinClass/assets/apps/__UNI__xxx/www(h5路径) 下
  3. 配置根目录下 gradle.properties 正式或测试环境
  4. dcloud_control设为
  5. 注释叽喳代码SDK.startWebApp 全局搜:叽喳需要打开此注释

ChainChat Class:

  1. 切换到 lianxinMP 分支
原生项目地址: uni项目注意事项:
  1. App.vue 的 appSwitch:true
  2. request.js 的 改成
let baseUrl = process.env.NODE_ENV
  1. uni已添加jar包:
    1. uniapp-release
    2. payment-weixin-release 微信支付
    3. share-weixin-release 微信分享
    4. auth-weixin-release 微信授权
    5. media-release 视频
apk:

减小apk:全局搜:减小apk,已经减掉(由于音视频用到,已还原)

  1. faceunity已减 assets 文件
云信 点播视频:
  1. 添加vcloudnosupload依赖
  2. vcloudnosupload 添加nos-android-sdk-allin-2.0.5jar包
uni SDK:
  1. audio-mp3aac-release.aar (在app)
  2. gallery-dmcBig-release.aar (在app 已废弃,只在叽喳使用)
  3. lib.5plus.base-release.aar (在app)
  4. media-release.aar (在app)
  5. msa_mdid_1.0.13.aar (或者 miit_mdid_1.0.10.aar)(在app)
  6. oauth-weixin-release.aar (在app)
  7. payment-weixin-release.aar (在app)
  8. share-weixin-release.aar (在app)
  9. uniapp-v8-release.aar (在uikit)
ThirdPart 腾讯X5webview:

tbs_sdk_thirdapp_v4.3.0.3_43903_sharewithdownloadwithfile_withoutGame_obfs_20200402_121309.jar

折叠屏

已删除今日头条屏幕适配方案
implementation 'me.jessyan:autosize:1.1.2'



华为折叠屏适配方案:

1.在每个activity里加:
android:configChanges="screenSize|smallestScreenSize|screenLayout"

2.在 manifest 文件的 < application > 节点中增加 < meta-data > 数据:


国际化多语言
  1. 获取本地国家 Locale.getDefault().getLanguage()
  2. 获取本地区域(如:香港 HK、台湾 TW) Locale.getDefault().getCountry()
  3. 适配聊天列表(全局搜:适配多语言需要用到)
    1. Attachment
@Override
protected void parseData(JSonObject data) {
    try {
        msgJson = getLanguage(data);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
protected JSonObject packData() {
    JSonObject data = new JSonObject();
    try {
        if (null != msgJson) {
            data.put(MSG_JSON, msgJson);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return data;
}

private LanguageBean msgJson;

public String getMsgJson() {
    return TextUtil.getMsgJson(msgJson);
}
2. MsgViewHolder
Attachment attachment = (Attachment) message.getAttachment();

String text = "";
String msgJson = attachment.getMsgJson();
if (!TextUtils.isEmpty(msgJson)) {
    text = msgJson;
} else if (!TextUtils.isEmpty(attachment.getMsg())) {
    text = attachment.getMsg();
}

textView.setText(text);
  1. 适配链信公开课无多语言,叽喳需多语言

    1. 中文多语言包放入 common的srcmainresvaluesstrings.xml
    2. 链信公开课
      1. 在app的srclianxinClassresvaluesstrings.xml放入区分链信公开课和叽喳的文字
    3. 叽喳
      1. 在app的srcgchatresvaluesstrings.xml放入区分链信公开课和叽喳的文字
      2. 在app的srcgchatresvalues-enstrings.xml放入区叽喳英文文字
      3. 在app的srcgchatresvalues-ja-rJPstrings.xml放入区叽喳日文文字
      4. 在app的srcgchatresvalues-ko-rKRstrings.xml放入区叽喳韩文文字
      5. 在app的srcgchatresvalues-zh-rHKstrings.xml放入区叽喳繁体中文
      6. 在app的srcgchatresvalues-zh-rTWstrings.xml放入区叽喳繁体中文
  2. 获取文字:

    1. activity获取文字 this.getContext().getResources().getString(R.string.ok)
    2. xml获取文字 android:text="@string/ok"
    3. 跨module获取文字AppJoint.service(ImService.class).getString(R.string.ok)
    4. 静态类获取文字baseApp.getContext().getResources().getString(R.string.ok)
EventBus
  1. 先EventBus.getDefault() 生成一个EventBus()单例,并初始化EventBus所需数据

  2. 注册,注销EventBus
    a. EventBus.getDefault().register(this) 注册EventBus
    b. EventBus.getDefault().unregister(this) 注销EventBus
    c. subscriber.getClass() 获取每个注册的Class 放入 subscriptionsByEventType

  3. EventBus.getDefault().post("hello") 调用post(Object event)方法,post方法执行:

    1. 放入队列
      用ThreadLocal创建currentPostingThreadState,
      PostingThreadState postingState = currentPostingThreadState.get(); List eventQueue = postingState.eventQueue; eventQueue.add(event);把event装入eventQueue队列

    2. 获取event的类型
      获取event的event放入eventTypes
      获取event的Class和event的类型(Object、String、Comparable、com.hande.common.event.Events$ClearMessageInList等类型)放入
      Map, List>> eventTypesCache

    3. 遍历eventTypes 得到 eventType 再遍历 subscriptions

    4. subscription.subscriberMethod.method.invoke(subscription.subscriber, event) 发送事件

    5. EmoticonPickerView 接收到事件 threadMode = ThreadMode.MAIN 就是主线程处理事件

    6. ThirdPart 腾讯X5webview:

      tbs_sdk_thirdapp_v4.3.0.3_43903_sharewithdownloadwithfile_withoutGame_obfs_20200402_121309.jar

      折叠屏

      如有今日头条屏幕适配方案需去除
      implementation 'me.jessyan:autosize:1.1.2'

      
      
      
      华为折叠屏适配方案:
      1. 在每个activity里加:
        android:configChanges="screenSize|smallestScreenSize|screenLayout"

      2. 在 manifest 文件的 < application > 节点中增加 < meta-data > 数据:

      
      
      国际化多语言
      1. 获取本地国家 Locale.getDefault().getLanguage()

      2. 获取本地区域(如:香港 HK、台湾 TW) Locale.getDefault().getCountry()

      多渠道(马甲包)开发

      前往Android多渠道打包

      1. 使用gradle进行动态配置
        • 通过productFlavors配置打包渠道
        在app/build.gradle里:
        productFlavors {
            anzhi {
                applicationId "com.muitlchannelpack.anzhi.jks"
                manifestPlaceholders = [
                        UMENG_APPKEY : UM_ANZHI,
                        UMENG_CHANNEL: ANZHI,
                ]
            }
            wandoujia {
                applicationId "com.muitlchannelpack.wandoujia"
                manifestPlaceholders = [
                        UMENG_APPKEY : UM_HUAWEI,
                        UMENG_CHANNEL: WANDOUJIA,
                ]
            }
            huawei {
                applicationId "com.muitlchannelpack.huawei"
                manifestPlaceholders = [
                        UMENG_APPKEY : UM_WANDOUJIA,
                        UMENG_CHANNEL: HUAWEI,
                ]
            }
        }
        
      2. 资源文件的替换
        • 在app/src目录下面创建productFlavors对应的文件夹
        • 分别在不同的渠道配置不同的AndroidManifest.xml启动项,在main下的AndroidManifest.xml可以让如公共项
        • 用BuildConfig.FLAVOR.equals("wandoujia")可以判断当前的渠道
      解决notifyDataSetChanged时导致图片闪烁
      1. 调用方:
      recyclerView.Adapter.setHasStableIds(true);
      
      1. recyclerView.Adapter里:
          @Override
          public long getItemId(int position) {
              return position;
          }
      
      Material Design
      1. 普通背景:添加水纹:
        有边界
              android:foreground="?android:attr/selectableItemBackground"
              android:background="?android:attr/selectableItemBackground"
      

      无边界

              android:foreground="?android:attr/selectableItemBackgroundBorderless"
              android:background="?android:attr/selectableItemBackgroundBorderless"
      
      1. 按钮
            
      1. cardView
              xmlns:card_view="http://schemas.android.com/apk/res-auto"
              
      
                  
      
              
      
      1. 阴影 elevation
              android:elevation="8dp"
              或  android:translationZ="20dp"
      
      View
      1. 获取view 宽高
          View view = findViewById(R.id.view);
          view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
              @Override
              public boolean onPreDraw() {
                  Log.e("view", view.getHeight() + "");
                  view.getViewTreeObserver().removeOnPreDrawListener(this);
                  return false;
              }
          });
      
      Java反射
      1. 反射工具类 ReflectUtils
      import android.text.TextUtils;
      import java.lang.annotation.Annotation;
      import java.lang.reflect.Field;
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      import java.lang.reflect.Modifier;
      import java.util.ArrayList;
      import java.util.List;
      
      public class ReflectUtils {
      
          
          public static  T invokeClassMethod(String classPath, String methodName, Class[] paramClasses, Object[] params) {
              return (T) executeClassLoad(getClass(classPath), methodName, paramClasses, params);
          }
      
          
          public static Object invokeMethod(Object obj, String methodName, Object[] params) {
              return invokeMethod(obj, methodName, null, params);
          }
      
          
          public static Object invokeMethod(Object obj, String methodName, Class[] paramTypes, Object[] params) {
              if (obj == null || TextUtils.isEmpty(methodName)) {
                  return null;
              }
      
              Class clazz = obj.getClass();
              try {
                  if (paramTypes == null) {
                      if (params != null) {
                          paramTypes = new Class[params.length];
                          for (int i = 0; i < params.length; ++i) {
                              paramTypes[i] = params[i].getClass();
                          }
                      }
                  }
                  Method method = clazz.getMethod(methodName, paramTypes);
                  method.setAccessible(true);
                  return method.invoke(obj, params);
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
              return null;
          }
      
          
          public static Object getFieldValue(Object obj, String fieldName) {
              if (obj == null || TextUtils.isEmpty(fieldName)) {
                  return null;
              }
      
              Class clazz = obj.getClass();
              while (clazz != Object.class) {
                  try {
                      Field field = clazz.getDeclaredField(fieldName);
                      field.setAccessible(true);
                      return field.get(obj);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
                  clazz = clazz.getSuperclass();
              }
      
              return null;
          }
      
          
          public static void setFieldValue(Object obj, String fieldName, Object value) {
              if (obj == null || TextUtils.isEmpty(fieldName)) {
                  return;
              }
      
              Class clazz = obj.getClass();
              while (clazz != Object.class) {
                  try {
                      Field field = clazz.getDeclaredField(fieldName);
                      field.setAccessible(true);
                      field.set(obj, value);
                      return;
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
                  clazz = clazz.getSuperclass();
              }
          }
      
          
          public static List getClassStaticField(Class clazz) {
              Field[] fields = clazz.getFields();
              List ret = new ArrayList<>();
      
              for (Field field : fields) {
                  String m = Modifier.toString(field.getModifiers());
                  if (m.contains("static")) {
                      ret.add(field);
                  }
              }
              return ret;
          }
      
          
          public static List getClassStaticFieldValue(Class clazz) {
              Field[] fields = clazz.getFields();
              List ret = new ArrayList<>();
      
              for (Field field : fields) {
                  String m = Modifier.toString(field.getModifiers());
                  if (m.contains("static")) {
                      try {
                          ret.add(field.get(null));
                      } catch (IllegalAccessException e) {
                          e.printStackTrace();
                      }
                  }
              }
              return ret;
          }
      
          
          public static  A getFieldAnnotationsByType(Field field, Class annotationType) {
              return field.getAnnotation(annotationType);
          }
      
          
          public static boolean hasMethod(String classPath, String methodName, Class[] paramClasses) {
              return getMethod(getClass(classPath), methodName, paramClasses) != null;
          }
      
          
          private static Class getClass(String str) {
              Class cls = null;
              try {
                  cls = Class.forName(str);
              } catch (ClassNotFoundException ignored) {
                  ignored.printStackTrace();
              }
              return cls;
          }
      
          private static Object executeClassLoad(Class cls, String str, Class[] clsArr, Object[] objArr) {
              Object obj = null;
              if (!(cls == null || checkObjExists(str))) {
                  Method method = getMethod(cls, str, clsArr);
                  if (method != null) {
                      method.setAccessible(true);
                      try {
                          obj = method.invoke(null, objArr);
                      } catch (IllegalAccessException e) {
                          e.printStackTrace();
                      } catch (InvocationTargetException e) {
                          e.printStackTrace();
                      }
                  }
              }
              return obj;
          }
      
          private static Method getMethod(Class cls, String str, Class[] clsArr) {
              Method method = null;
              if (cls == null || checkObjExists(str)) {
                  return null;
              }
              try {
                  cls.getMethods();
                  cls.getDeclaredMethods();
                  return cls.getDeclaredMethod(str, clsArr);
              } catch (Exception e) {
                  try {
                      return cls.getMethod(str, clsArr);
                  } catch (Exception e2) {
                      return cls.getSuperclass() != null ? getMethod(cls.getSuperclass(), str, clsArr) : method;
                  }
              }
          }
      
          private static boolean checkObjExists(Object obj) {
              return obj == null || obj.toString().equals("") || obj.toString().trim().equals("null");
          }
      }
      
      
      
      我们一直用心在做
      关于我们 文章归档 网站地图 联系我们

      版权所有 (c)2021-2022 MSHXW.COM

      ICP备案号:晋ICP备2021003244-6号