-
项目注意事项
- 主题色
- 打包
- 原生项目地址
- 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
- arm64-v8a
- 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
- View工作流程
- 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 全局搜:叽喳需要打开此注释
链信公开课:
- idea左侧 Build Variants 切换到 lianxinClass
- 将打包后uni包放到 app/src/lianxinClass/assets/apps/__UNI__xxx/www(h5路径) 下
- 配置根目录下 gradle.properties 正式或测试环境
- dcloud_control设为
- 注释叽喳代码SDK.startWebApp 全局搜:叽喳需要打开此注释
ChainChat Class:
- 切换到 lianxinMP 分支
- App.vue 的 appSwitch:true
- request.js 的 改成
let baseUrl = process.env.NODE_ENV
- uni已添加jar包:
- uniapp-release
- payment-weixin-release 微信支付
- share-weixin-release 微信分享
- auth-weixin-release 微信授权
- media-release 视频
减小apk:全局搜:减小apk,已经减掉(由于音视频用到,已还原)
- faceunity已减 assets 文件
- 添加vcloudnosupload依赖
- vcloudnosupload 添加nos-android-sdk-allin-2.0.5jar包
- 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 (或者 miit_mdid_1.0.10.aar)(在app)
- oauth-weixin-release.aar (在app)
- payment-weixin-release.aar (在app)
- share-weixin-release.aar (在app)
- uniapp-v8-release.aar (在uikit)
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 > 数据:
国际化多语言
- 获取本地国家 Locale.getDefault().getLanguage()
- 获取本地区域(如:香港 HK、台湾 TW) Locale.getDefault().getCountry()
- 适配聊天列表(全局搜:适配多语言需要用到)
- 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);
-
适配链信公开课无多语言,叽喳需多语言
- 中文多语言包放入 common的srcmainresvaluesstrings.xml
- 链信公开课
- 在app的srclianxinClassresvaluesstrings.xml放入区分链信公开课和叽喳的文字
- 叽喳
- 在app的srcgchatresvaluesstrings.xml放入区分链信公开课和叽喳的文字
- 在app的srcgchatresvalues-enstrings.xml放入区叽喳英文文字
- 在app的srcgchatresvalues-ja-rJPstrings.xml放入区叽喳日文文字
- 在app的srcgchatresvalues-ko-rKRstrings.xml放入区叽喳韩文文字
- 在app的srcgchatresvalues-zh-rHKstrings.xml放入区叽喳繁体中文
- 在app的srcgchatresvalues-zh-rTWstrings.xml放入区叽喳繁体中文
-
获取文字:
- activity获取文字 this.getContext().getResources().getString(R.string.ok)
- xml获取文字 android:text="@string/ok"
- 跨module获取文字AppJoint.service(ImService.class).getString(R.string.ok)
- 静态类获取文字baseApp.getContext().getResources().getString(R.string.ok)
-
先EventBus.getDefault() 生成一个EventBus()单例,并初始化EventBus所需数据
-
注册,注销EventBus
a. EventBus.getDefault().register(this) 注册EventBus
b. EventBus.getDefault().unregister(this) 注销EventBus
c. subscriber.getClass() 获取每个注册的Class 放入 subscriptionsByEventType -
EventBus.getDefault().post("hello") 调用post(Object event)方法,post方法执行:
-
放入队列
用ThreadLocal创建currentPostingThreadState,
PostingThreadState postingState = currentPostingThreadState.get(); List -
获取event的类型
获取event的event放入eventTypes
获取event的Class和event的类型(Object、String、Comparable、com.hande.common.event.Events$ClearMessageInList等类型)放入
Map, List >> eventTypesCache -
遍历eventTypes 得到 eventType 再遍历 subscriptions
-
subscription.subscriberMethod.method.invoke(subscription.subscriber, event) 发送事件
-
EmoticonPickerView 接收到事件 threadMode = ThreadMode.MAIN 就是主线程处理事件
-
tbs_sdk_thirdapp_v4.3.0.3_43903_sharewithdownloadwithfile_withoutGame_obfs_20200402_121309.jar
折叠屏如有今日头条屏幕适配方案需去除
implementation 'me.jessyan:autosize:1.1.2'
华为折叠屏适配方案:
-
在每个activity里加:
android:configChanges="screenSize|smallestScreenSize|screenLayout" -
在 manifest 文件的 < application > 节点中增加 < meta-data > 数据:
国际化多语言
-
获取本地国家 Locale.getDefault().getLanguage()
-
获取本地区域(如:香港 HK、台湾 TW) Locale.getDefault().getCountry()
前往Android多渠道打包
- 使用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, ] } } - 资源文件的替换
- 在app/src目录下面创建productFlavors对应的文件夹
- 分别在不同的渠道配置不同的AndroidManifest.xml启动项,在main下的AndroidManifest.xml可以让如公共项
- 用BuildConfig.FLAVOR.equals("wandoujia")可以判断当前的渠道
- 调用方:
recyclerView.Adapter.setHasStableIds(true);
- recyclerView.Adapter里:
@Override
public long getItemId(int position) {
return position;
}
Material Design
- 普通背景:添加水纹:
有边界
android:foreground="?android:attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackground"
无边界
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:background="?android:attr/selectableItemBackgroundBorderless"
- 按钮
- cardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
- 阴影 elevation
android:elevation="8dp"
或 android:translationZ="20dp"
View
- 获取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反射
- 反射工具类 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 


