数据峰会
公司(视频 社交)项目分享
http://blog.csdn.net/u011733020/article/details/46786471 最近公司工作比较轻松,就把以前的项目 拿来整理下。以前公司做视频社交这一块,类似于YY直播。 展示 先来个动态图 再简单看一下主要界面 首页第一个界面,这里可以看美女,看直播
这是任务列界面,可以领取每日任务,任务分成长 和推荐
好友列表
用户的个人中心页
直播间
用户的个人中心页
充值方式页
银联支付页
项目组成 这个项目主要的重点就两个地方,看视频,和 与主播互动。
整个视频流是用RTMP协议的,文字聊天走的是openfire+asmack .
PS:整个项目,我们产品设计的很不错的,只是我水平有限,有些功能 实现不了!
再介绍下,整个项目都用到了什么?
程序框架:SlidingMenu+Viewpager+fragment
请求服务器: asynchttpclient
解析数据:Gson
消息推送: Jpush
页面数据分析: Umeng
充值方式: alipay +银联+yeepay+短信充值
图片缓存: afinal
自定义view: Pulltoresfresh+拼音排序联系人+horizontallistview+verticalviewpager…
另,我把公司的项目写出来,是因为公司的服务器停掉了,所以,一些数据,是我自己抓出其他应用里面的(抓取数据的方法,在上面一篇文章里)….
首先就是整个项目最下面是mainactivity,这个大家是都有共识的,在mainactivity 上面 我们就要 引入slidingmenu,
关于slidingmenu的下载,就不介绍了,这里直接拿来用
包结构比较清晰
,
整个项目首先,由splashactivity 欢迎界面 ,进行检查,然后跳转到mainactivity,mainactivity 中包含slidingmenu,slidingmenu的中间界面 添加了viewpager,viewpager 里面添加了四个fragment。 由于默认v4包里的viewpager 会默认混存数据,即使你设置了setOffscreenPageLimit(0),所以这里替换掉原生的v4包,并且通过fragment 的 setuservisibilityhint 方法来,控制 fragment 界面的动态刷新。 主界面的第一个fragment ,也就是约美女的界面,通过fragmenttabhost,来实现约美女,和看直播的切换。
在约美女中,就是一个简单地pulltorefreshlistview,适配了一个item.点击item 进入用户的个人中心, 个人中心 顶部 是个人的宣传适配,下面,是用户自己上传的 公开专辑,或者私密专辑。可以点赞,关注他。 做任务界面是有两种任务,一种是 每日任务,就是 登陆,签到,另一种是下载app 得积分任务。 下载app 可以控制 下载 暂停。 在我的关注界面。是你关注的好友,这个跟微信的联系人控件是一样的。首字母排序,用到了比较器。
首先 把汉字,对应首字母提取出来,然后与A-Z 排序。以及特殊字符~。 基本代码 整个项目 基本框架 简单描述下
在MainActiviy中初始化 slidingmenu。MainActivity 布局文件 <?xml version="1.0" encoding="utf-8"?>
<com.os.slidingmenu.SlidingMenu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sliding="http://schemas.android.com/apk/res-auto"
android:id="@+id/slidingmenu"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
sliding:behindOffset="60dp"
sliding:fadeEnabled="true"
sliding:mode="left"
sliding:secondaryShadowDrawable="@drawable/sliding_shadow_right"
sliding:shadowDrawable="@drawable/sliding_shadow_left"
sliding:shadowWidth="10dp"
sliding:touchModeAbove="fullscreen"
sliding:touchModeBehind="margin" />
MainActivity这个类进行初始化。代码语言:javascript复制<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.os.activity;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.lang.ref.WeakReference;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.activity.base.BaseFragmentActivity;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.activity.base.BaseSlidingFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.activity.sliding.LeftFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.activity.sliding.RightFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.slidingmenu.R;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.slidingmenu.SlidingMenu;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.ui.MainHallFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Bundle;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Handler;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Message;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.support.v4.app.Fragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.support.v4.app.FragmentManager;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.support.v4.app.FragmentTransaction;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MainActivity</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BaseFragmentActivity</span> {</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Fragment mCurFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> SlidingMenu mSlidingMenu;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Handler handler = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MyHandler(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MyHandler</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Handler</span> {</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> WeakReference<MainActivity> mActivity;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">MyHandler</span>(MainActivity activity) {
mActivity = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> WeakReference<MainActivity>(activity);
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">handleMessage</span>(Message msg) {
MainActivity activity = mActivity.get();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (activity == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
}
activity.handleMsg(msg);
}
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">handleMsg</span>(Message msg) {
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (savedInstanceState != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
mCurFragment = getSupportFragmentManager().getFragment(savedInstanceState, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"mCurContent"</span>);
}
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onSaveInstanceState</span>(Bundle outState) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"mCurContent"</span>, mCurFragment);
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">initViews</span>() {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 通过id 找到slidingmenu </span>
mSlidingMenu = (SlidingMenu) findViewById(R.id.slidingmenu);
mSlidingMenu.setMenu(R.layout.sliding_left_frame);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//给slidingmenu 添加左边的布局</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (getFragmentByTag(LeftFragment.class) == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//添加左边fragment</span>
getSupportFragmentManager().beginTransaction().add(R.id.left_frame, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LeftFragment(), LeftFragment.class.getName()).commit();
}
mSlidingMenu.setContent(R.layout.sliding_center_frame);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//添加一个空布局,后面承载 中间的fragment</span>
mSlidingMenu.setSecondaryMenu(R.layout.sliding_right_frame);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//添加右面的布局,添加右边的fragment</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (getFragmentByTag(RightFragment.class) == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
getSupportFragmentManager().beginTransaction().add(R.id.right_frame, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RightFragment(), RightFragment.class.getName()).commit();
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mCurFragment != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
postSwitchFragment();
}
mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//设置滑动模式,边缘 还是整个界面</span>
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* slidingMenu中的内容Fragment切换(左侧菜单触发)
*
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> clazz
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">switchCenterFragment</span>(Class<? extends Fragment> clazz) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mSlidingMenu == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
removeAllFragments();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isInit = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment userFragment = fm.findFragmentByTag(clazz.getName());
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (userFragment == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
isInit = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
userFragment = clazz.newInstance();
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InstantiationException e) {
e.printStackTrace();
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (IllegalAccessException e) {
e.printStackTrace();
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
e.printStackTrace();
}
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mCurFragment != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> mCurFragment != userFragment) {
ft.hide(mCurFragment);
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!userFragment.isAdded() isInit) {
ft.add(R.id.center_frame, userFragment, clazz.getName());
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
ft.show(userFragment);
}
ft.commitAllowingStateLoss();
mCurFragment = userFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (MainHallFragment.class.getName().equals(clazz.getName())) {
mSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!isInit) {
((MainHallFragment) userFragment).postScrollTop();
}
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
mSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
}
postShowContent(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">200</span>);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
e.printStackTrace();
}
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* lidingMenu中的内容Fragment内容过滤(右侧菜单触发)
*
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> clazz
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> type
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">filterCenterFragment</span>(Class<? extends BaseSlidingFragment> clazz, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> type) {
BaseSlidingFragment userFragment = (BaseSlidingFragment) getFragmentByTag(clazz);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (userFragment != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
userFragment.filter(type);
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mSlidingMenu != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>)
mSlidingMenu.showContent();
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 延迟切换Fragment
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">postSwitchFragment</span>() {
handler.postDelayed(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Runnable() {
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
switchCenterFragment(mCurFragment.getClass());
}
}, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">50</span>);
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 清除FragmentManager中所有Fragment
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">removeAllFragments</span>() {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < LeftFragment.FRAGMENTS_CLASSES.length; i++) {
Fragment fragment = getFragmentByTag(LeftFragment.FRAGMENTS_CLASSES[i].getName());
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (fragment != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
ft.remove(fragment);
}
}
ft.commitAllowingStateLoss();
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 延时mSlidingMenu.showContent()
*
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> delayMillis 延时时间 单位毫秒
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">postShowContent</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> delayMillis) {
handler.postDelayed(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Runnable() {
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mSlidingMenu!=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> !MainActivity.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.isFinishing()) {
mSlidingMenu.showContent();
}
}
}, delayMillis);
}
}
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li><li style="box-sizing: border-box; padding: 0px 5px;">135</li><li style="box-sizing: border-box; padding: 0px 5px;">136</li><li style="box-sizing: border-box; padding: 0px 5px;">137</li><li style="box-sizing: border-box; padding: 0px 5px;">138</li><li style="box-sizing: border-box; padding: 0px 5px;">139</li><li style="box-sizing: border-box; padding: 0px 5px;">140</li><li style="box-sizing: border-box; padding: 0px 5px;">141</li><li style="box-sizing: border-box; padding: 0px 5px;">142</li><li style="box-sizing: border-box; padding: 0px 5px;">143</li><li style="box-sizing: border-box; padding: 0px 5px;">144</li><li style="box-sizing: border-box; padding: 0px 5px;">145</li><li style="box-sizing: border-box; padding: 0px 5px;">146</li><li style="box-sizing: border-box; padding: 0px 5px;">147</li><li style="box-sizing: border-box; padding: 0px 5px;">148</li><li style="box-sizing: border-box; padding: 0px 5px;">149</li><li style="box-sizing: border-box; padding: 0px 5px;">150</li><li style="box-sizing: border-box; padding: 0px 5px;">151</li><li style="box-sizing: border-box; padding: 0px 5px;">152</li><li style="box-sizing: border-box; padding: 0px 5px;">153</li><li style="box-sizing: border-box; padding: 0px 5px;">154</li><li style="box-sizing: border-box; padding: 0px 5px;">155</li><li style="box-sizing: border-box; padding: 0px 5px;">156</li><li style="box-sizing: border-box; padding: 0px 5px;">157</li><li style="box-sizing: border-box; padding: 0px 5px;">158</li><li style="box-sizing: border-box; padding: 0px 5px;">159</li><li style="box-sizing: border-box; padding: 0px 5px;">160</li><li style="box-sizing: border-box; padding: 0px 5px;">161</li><li style="box-sizing: border-box; padding: 0px 5px;">162</li><li style="box-sizing: border-box; padding: 0px 5px;">163</li><li style="box-sizing: border-box; padding: 0px 5px;">164</li><li style="box-sizing: border-box; padding: 0px 5px;">165</li><li style="box-sizing: border-box; padding: 0px 5px;">166</li><li style="box-sizing: border-box; padding: 0px 5px;">167</li><li style="box-sizing: border-box; padding: 0px 5px;">168</li><li style="box-sizing: border-box; padding: 0px 5px;">169</li><li style="box-sizing: border-box; padding: 0px 5px;">170</li><li style="box-sizing: border-box; padding: 0px 5px;">171</li><li style="box-sizing: border-box; padding: 0px 5px;">172</li><li style="box-sizing: border-box; padding: 0px 5px;">173</li><li style="box-sizing: border-box; padding: 0px 5px;">174</li><li style="box-sizing: border-box; padding: 0px 5px;">175</li><li style="box-sizing: border-box; padding: 0px 5px;">176</li><li style="box-sizing: border-box; padding: 0px 5px;">177</li><li style="box-sizing: border-box; padding: 0px 5px;">178</li><li style="box-sizing: border-box; padding: 0px 5px;">179</li><li style="box-sizing: border-box; padding: 0px 5px;">180</li><li style="box-sizing: border-box; padding: 0px 5px;">181</li><li style="box-sizing: border-box; padding: 0px 5px;">182</li><li style="box-sizing: border-box; padding: 0px 5px;">183</li><li style="box-sizing: border-box; padding: 0px 5px;">184</li><li style="box-sizing: border-box; padding: 0px 5px;">185</li><li style="box-sizing: border-box; padding: 0px 5px;">186</li><li style="box-sizing: border-box; padding: 0px 5px;">187</li><li style="box-sizing: border-box; padding: 0px 5px;">188</li><li style="box-sizing: border-box; padding: 0px 5px;">189</li><li style="box-sizing: border-box; padding: 0px 5px;">190</li><li style="box-sizing: border-box; padding: 0px 5px;">191</li><li style="box-sizing: border-box; padding: 0px 5px;">192</li><li style="box-sizing: border-box; padding: 0px 5px;">193</li><li style="box-sizing: border-box; padding: 0px 5px;">194</li><li style="box-sizing: border-box; padding: 0px 5px;">195</li><li style="box-sizing: border-box; padding: 0px 5px;">196</li><li style="box-sizing: border-box; padding: 0px 5px;">197</li><li style="box-sizing: border-box; padding: 0px 5px;">198</li><li style="box-sizing: border-box; padding: 0px 5px;">199</li><li style="box-sizing: border-box; padding: 0px 5px;">200</li><li style="box-sizing: border-box; padding: 0px 5px;">201</li></ul>
*/ 左边的fragment代码语言:javascript复制<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.os.activity.sliding;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.util.Arrays;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.activity.MainActivity;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.activity.base.BaseSlidingFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.slidingmenu.R;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.ui.FollowFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.ui.MainHallFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.os.ui.RankFragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.app.ProgressDialog;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.Bitmap;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.BitmapFactory;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.Color;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Bundle;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.support.v4.app.Fragment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.text.Spannable;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.text.SpannableStringBuilder;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.text.style.ForegroundColorSpan;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.view.Gravity;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.view.View;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.view.View.OnClickListener;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.view.ViewGroup.LayoutParams;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.Button;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.ImageView;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.LinearLayout;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.PopupWindow;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.TextView;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.Toast;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">LeftFragment</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BaseSlidingFragment</span> {</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> MENU_NORMAL_ICONS[] = { R.drawable.sliding_livehall_icon_normal,
R.drawable.sliding_follow_icon_normal, R.drawable.sliding_rank_icon_normal};
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> MENU_CHECKED_ICONS[] = { R.drawable.sliding_livehall_icon_checked,
R.drawable.sliding_follow_icon_checked, R.drawable.sliding_rank_icon_checked };
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Class[] FRAGMENTS_CLASSES = { MainHallFragment.class, FollowFragment.class,
RankFragment.class};<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//左侧切换显示中间的三个界面</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> View[] mMenuLayouts;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> ImageView[] mMenuIcons;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> TextView[] mMenuTexts;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Bitmap mLoadingBitmap;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> mCurrentIndex = -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.sliding_left);
setData();
changeMenuByClass(MainHallFragment.class);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//默认中间的界面显示的是这个MainHallFragment</span>
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">initViews</span>() {
mMenuLayouts = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> View[] { findViewById(R.id.menu_livehall_layout),
findViewById(R.id.menu_follow_layout), findViewById(R.id.menu_rank_layout) };
mMenuIcons = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ImageView[] {
(ImageView) findViewById(R.id.menu_livehall_icon), (ImageView) findViewById(R.id.menu_follow_icon), (ImageView) findViewById(R.id.menu_rank_icon) };
mMenuTexts = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TextView[] {
(TextView) findViewById(R.id.menu_livehall_text), (TextView) findViewById(R.id.menu_follow_text), (TextView) findViewById(R.id.menu_rank_text),};
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">addListener</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < mMenuLayouts.length; i++) {
mMenuLayouts[i].setTag(i);
mMenuLayouts[i].setOnClickListener(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> OnClickListener() {
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(View v) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index = (Integer) v.getTag();
changeMenuByIndex(index);
}
});
}
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">setData</span>() {
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 通过索引改变Menu
*
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> index
*/</span>
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@SuppressWarnings</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"unchecked"</span>)
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">changeMenuByIndex</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index) {
Class<? extends Fragment> clazz = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mCurrentIndex != index) {
clearMenu();
setMenuChecked(index);
}
clazz = FRAGMENTS_CLASSES[index];
getFragmentActivity(MainActivity.class).switchCenterFragment(clazz);
mCurrentIndex = index;
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
* 通过Fragment类改变menu
*
*<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> clazz
*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">changeMenuByClass</span>(Class<? extends Fragment> clazz) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index = Arrays.asList(FRAGMENTS_CLASSES).indexOf(clazz);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (index != -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) {
changeMenuByIndex(index);
}
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@SuppressWarnings</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"deprecation"</span>)
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">clearMenu</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; i <= mMenuLayouts.length; i++) {
mMenuLayouts[i-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>].setBackgroundDrawable(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>);
mMenuIcons[i - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>].setImageResource(MENU_NORMAL_ICONS[i - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>]);
mMenuTexts[i - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>].setTextColor(getResources().getColor(R.color.gray7));
}
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">setMenuChecked</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index) {
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// if (index == 0) {</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// return;</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// }</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (index != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> index != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>) {
mMenuLayouts[index].setBackgroundResource(R.drawable.sliding_menu_checked_bg);
}
mMenuIcons[index ].setImageResource(MENU_CHECKED_ICONS[index ]);
mMenuTexts[index ].setTextColor(getResources().getColor(R.color.white));
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onDestroy</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mLoadingBitmap != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> !mLoadingBitmap.isRecycled()) {
mLoadingBitmap.recycle();
mLoadingBitmap = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onDestroy();
}
}
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li><li style="box-sizing: border-box; padding: 0px 5px;">135</li><li style="box-sizing: border-box; padding: 0px 5px;">136</li><li style="box-sizing: border-box; padding: 0px 5px;">137</li><li style="box-sizing: border-box; padding: 0px 5px;">138</li><li style="box-sizing: border-box; padding: 0px 5px;">139</li><li style="box-sizing: border-box; padding: 0px 5px;">140</li><li style="box-sizing: border-box; padding: 0px 5px;">141</li><li style="box-sizing: border-box; padding: 0px 5px;">142</li><li style="box-sizing: border-box; padding: 0px 5px;">143</li><li style="box-sizing: border-box; padding: 0px 5px;">144</li><li style="box-sizing: border-box; padding: 0px 5px;">145</li><li style="box-sizing: border-box; padding: 0px 5px;">146</li><li style="box-sizing: border-box; padding: 0px 5px;">147</li><li style="box-sizing: border-box; padding: 0px 5px;">148</li><li style="box-sizing: border-box; padding: 0px 5px;">149</li><li style="box-sizing: border-box; padding: 0px 5px;">150</li><li style="box-sizing: border-box; padding: 0px 5px;">151</li><li style="box-sizing: border-box; padding: 0px 5px;">152</li><li style="box-sizing: border-box; padding: 0px 5px;">153</li><li style="box-sizing: border-box; padding: 0px 5px;">154</li><li style="box-sizing: border-box; padding: 0px 5px;">155</li><li style="box-sizing: border-box; padding: 0px 5px;">156</li><li style="box-sizing: border-box; padding: 0px 5px;">157</li><li style="box-sizing: border-box; padding: 0px 5px;">158</li><li style="box-sizing: border-box; padding: 0px 5px;">159</li><li style="box-sizing: border-box; padding: 0px 5px;">160</li><li style="box-sizing: border-box; padding: 0px 5px;">161</li><li style="box-sizing: border-box; padding: 0px 5px;">162</li><li style="box-sizing: border-box; padding: 0px 5px;">163</li><li style="box-sizing: border-box; padding: 0px 5px;">164</li><li style="box-sizing: border-box; padding: 0px 5px;">165</li><li style="box-sizing: border-box; padding: 0px 5px;">166</li><li style="box-sizing: border-box; padding: 0px 5px;">167</li><li style="box-sizing: border-box; padding: 0px 5px;">168</li><li style="box-sizing: border-box; padding: 0px 5px;">169</li><li style="box-sizing: border-box; padding: 0px 5px;">170</li><li style="box-sizing: border-box; padding: 0px 5px;">171</li><li style="box-sizing: border-box; padding: 0px 5px;">172</li></ul>
*/ 主要就是 oncreate 中 初始化 刚启动应用后中间显示的fragment 是 MainHallFragment. 视频时RTMP 协议,解码用的ffmpeg. 代码就不贴了,在工程的jni 目录下,都有注释。 聊天的代码 聊天室界面是 ChatroomActivity. 这是 一初始化MultiUserChat 聊天室对象的代码,具体代码 ,在这个类里面。代码语言:javascript复制<code class="hljs axapta has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Thread() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> run() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) {
e.printStackTrace();
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">count</span> = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isRandom = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">count</span> <= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span> !create_flag) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">count</span>++;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mHostInfo != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
mUserNickName = getNickName(isRandom);
muc = ConnectionUtils.getMultiUserChat(mHostInfo.room_id, mHostInfo.room_service, mUserNickName, mPassword, ChatroomActivity.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (muc != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
create_flag = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 创建聊天室成功,监听聊天室返回的消息</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 监听消息</span>
muc.addMessageListener(packetListener);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// muc.addParticipantListener(participantListener);</span>
muc.addParticipantStatusListener(statusListener);
muc.addUserStatusListener(userStatusListener);
mHandler.sendEmptyMessageDelayed(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">9</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">500</span>);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
create_flag = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
}
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
create_flag = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// } else {</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// }</span>
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (NotFoundException e) {
e.printStackTrace();
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (SameException e) {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 昵称重复</span>
isRandom = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (BannedException e) {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 禁止加入房间</span>
sendBandHandle(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">15</span>);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (XMPPException e) {
e.printStackTrace();
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
e.printStackTrace();
}
}</code>
Flarum搭建论坛详细教程
1、关于Flarum程序完美支持军哥lnmp环境/宝塔等,下载程序上传到网站根目录并需要伪静态+fileinfo扩展。程序本地下载:FlarumChina-beta7.zipGithub下载地址:https://github.com/skywalker512/FlarumChinafileinfo拓展
需要支持php扩展fileinfo。
1、对于lnmp1.3默认没有支持。修改include/upgrade_php.sh这个文件,把其中的:--disable-fileinfo,全部替换:--enable-fileinfo,再执行./upgrade.sh升级php就行了。
2、对于宝塔,安装后,找到你的PHP程序,里面有扩展可以选择安装。2、centos安装LNMP支持flarun2 | 1 数据库安装代码语言:javascript复制sudo wget https://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm
yum -y install mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql-community-server
systemctl start mysqld.service2 | 2 mysql5.7获取密码的方式代码语言:javascript复制sudo grep 'temporary password' /var/log/mysqld.log代码语言:javascript复制vi /etc/my.cnf代码语言:javascript复制#添加validate_password_policy配置
validate_password_policy=0
#关闭密码策略
validate_password = off修改root密码代码语言:javascript复制mysql -uroot -p密码代码语言:javascript复制set global validate_password_policy=0;
set global validate_password_length=1;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass4!';
grant all privileges on *.* to root@"%" identified by "new password";
flush privileges;代码语言:javascript复制systemctl restart mysql2 | 3 安装php7.2代码语言:javascript复制sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
yum install php72w-fpm php72w-cli php72w-curl php72w-dom php72w-gd php72w-json php72w-mbstring php72w-openssl php72w-pdo_mysql php72w-tokenizer php72w-zip -y代码语言:javascript复制vim /etc/php-fpm.d/www.conf代码语言:javascript复制#找到以下两处共四行代码并修改如下:
user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx3、安装Flarum方法一:下载=即使用整合包https://github.com/skywalker512/FlarumChina/releases/tag/v0.1.0-beta.12方法二:composer拉取 (比较建议后面装插件都是用的composer)3 | 1 安装composer下载composer.phar, 如果是网络原因失败,多试几次代码语言:javascript复制curl -sS https://getcomposer.org/installer | php把composer.phar移动到环境下让其变成可执行:代码语言:javascript复制mv composer.phar /usr/local/bin/composer测试代码语言:javascript复制composer -v修改镜像地址代码语言:javascript复制composer config repo.packagist composer https://mirrors.aliyun.com/composer/3 | 2 安装ningx代码语言:javascript复制yum -y install nginx unzip进入wwwroot目录中使用composer命令来安装flarum:代码语言:javascript复制mkdir /var/www/flarum/
cd /var/www/flarum/代码语言:javascript复制composer create-project flarum/flarum . --stability=beta #不用使用管理员权限命令sudo。完毕后修改nginx中的默认配置文件:代码语言:javascript复制sudo vim /etc/nginx/nginx.conf代码语言:javascript复制#将server代码段下的所有代码都用#注释掉
#server {
# listen 80 default_server;
# listen [::]:80 default_server;
# server_name _;
# root /usr/share/nginx/html;
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
# location / {
# }
# error_page 404 /404.html;
# location = /40x.html {
# }
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
#}
#然后修改
server {
listen 80;
server_name your.website.url;
root /var/www/flarum/public;
index index.php index.html;
#include /home/flarum/wwwroot/.nginx.conf;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# The following directives are based on best practices from H5BP Nginx Server Configs
# https://github.com/h5bp/server-configs-nginx
# Expire rules for static content
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
add_header Cache-Control "max-age=0";
}
location ~* \.(?:rss|atom)$ {
add_header Cache-Control "max-age=3600";
}
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|mp4|ogg|ogv|webm|htc)$ {
add_header Cache-Control "max-age=2592000";
access_log off;
}
location ~* \.(?:css|js)$ {
add_header Cache-Control "max-age=31536000";
access_log off;
}
location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
add_header Cache-Control "max-age=2592000";
access_log off;
}
# Gzip compression
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;
}注意:因为Flarum遵守了最新的安全规范,其程序的运行目录其实是在public文件夹内。4、插件安装https://bbs.csur.fun/d/84-flarum5、虚拟机搭建买个虚拟机做网页,诸多限制,虚拟机服务商的提供的服务确实就两字“操蛋”。
为什么没有买云服务器?就两字 “没钱”!哈哈。
虚拟机的购买途径自寻,但是很多服务商真的很坑爹。
虚拟机要求php支持7.2以上mysql支持5.7以上能够支持更改网站跟目录6、更改网站根目录层级将 public 目录(包括 .htaccess)中的所有文件移动到 Flarum 根目录。然后编辑 .htaccess 取消第 9-14 行的注释,以保护敏感资源。
然后编辑 index.php 文件,更改以下行:代码语言:javascript复制$site = require './site.php';最后,在 site.php 更新路径,以反映新的目录结构:代码语言:javascript复制'base' => __DIR__,
'public' => __DIR__,
'storage' => __DIR__.'/storage',config.php 更新 url 路径代码语言:javascript复制'url' => 'https://xxx.com/community',不更改网站层级目录请根据你的虚拟主机面板的不同,将网站运行目录指向public文件夹。
如果您使用的是虚拟主机无法更改目录,请按照一下的方法进行设置
在根目录下新建一个名为 .htaccess的文件代码语言:javascript复制RewriteEngine on
RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^(.*)$ /public/$1 [L]7、其他底部加统计代码语言:javascript复制vendor/flarum/core/views/app.blade.php去掉网络字体代码语言:javascript复制vendor/flarum/core/src/Http/WebApp/WebAppView.php伪静态设置Nginx的话在站点配置文件处添加下面一段话。代码语言:javascript复制location / {
try_files $uri $uri/ /index.php?$query_string;
}
location /api {
try_files $uri $uri/ /api.php?$query_string;
}
location /admin {
try_files $uri $uri/ /admin.php?$query_string;
}
location /flarum {
deny all;
return 404;
}
location ~ .php$ {
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
一些关于网站推广问题合集。
网站怎么快速上权重?要让一个网站快速提高权重,需要实施以下一些有效的策略:提供高质量的内容:提供高质量、原创、有用的内容是最重要的。这可以吸引更多的用户访问,并增加用户留存时间,提高用户体验。同时,高质量的内容也会被其他网站引用和分享,这将有助于增加外部链接,提高网站的权重。内部链接优化:内部链接是指在网站内部不同页面之间的链接。通过优化内部链接结构,可以增加搜索引擎对网站的理解,提高网站权重。在内部链接时,要使用相关的关键字作为链接文本,而不是使用"点击这里"这样无意义的链接文本。外部链接建设:获取来自其他网站的高质量链接对于提高网站权重非常重要。可以通过主动进行友情链接、媒体报道、论坛发帖等方式增加外部链接。但是请注意,建立外部链接是需要花费时间和精力的,必须确保链接是高质量的、自然的,否则可能会适得其反。社交媒体推广:在社交媒体平台上建立专业的社交媒体账号,提供网站相关的信息、文章、图像、视频等,让更多的用户了解网站,增加网站的曝光度,进而增加网站权重。网站结构优化:优化网站结构可以帮助搜索引擎更好地抓取和理解网站内容,提高网站权重。可以通过优化网站的HTML代码结构、网站的目录结构、网站的图片和视频的优化等方式实现。需要注意的是,这些策略需要长期坚持和不断完善,才能够取得长期、稳定的效果。怎么养站?要养好一个网站,需要考虑以下几个方面:网站内容:一个网站需要有高质量、有价值的内容来吸引用户。内容可以是文字、图片、视频等形式,需要有足够的量和质量,同时需要及时更新。网站设计:网站的设计需要符合用户的视觉需求,界面简洁、易于操作、美观大方,同时需要考虑不同设备(如手机、平板、电脑)的适配。网站优化:通过搜索引擎优化(SEO)、网站性能优化(如减少页面加载时间)、提高用户体验(如减少广告干扰),来提高网站的流量和用户满意度。社交媒体:通过社交媒体来吸引用户,增加网站的曝光率和影响力,比如可以在微信公众号、微博、知乎等平台上发布内容,吸引用户关注。安全保护:确保网站的安全性,包括用户信息保护、反垃圾邮件、反病毒等安全措施。数据分析:通过数据分析工具,了解用户访问情况,优化网站的运营,提高用户满意度和转化率。综合考虑上述因素,不断更新和改进,就可以养好一个网站。网站怎么发外链有用?要使外链对你的网站有用,你需要遵循以下几个步骤:确定目标受众和内容:确定你要向哪些人推广内容,以及他们可能对哪些内容感兴趣。通过创建高质量的内容,吸引用户来阅读你的文章并点击你的链接。找到相关的网站:寻找与你的内容相关的网站,并联系网站所有者以获得一个外链。你可以通过搜索引擎、社交媒体、行业博客等方式来找到这些网站。站长百科网创建有价值的内容:创建有价值的内容可以吸引其他网站链接到你的网站。确保你的内容是高质量的,包含有用的信息,且易于阅读和分享。联系其他网站:直接联系其他网站的所有者,请求他们在他们的网站上添加你的链接。你可以通过电子邮件、社交媒体或其他渠道来联系他们。使用社交媒体:在社交媒体上分享你的内容,并鼓励其他用户分享你的文章。这样可以提高你的内容的曝光率,并吸引更多的用户来访问你的网站。需要注意的是,对于外链的数量和质量,搜索引擎有一定的要求,如果采用不当的方式获取外链,可能会对你的网站产生负面影响。因此,你需要遵守搜索引擎的规则,采用合法的方式获取外链,从而为你的网站带来更多的流量和排名提升。2023年建什么类型的网站有前景?2023年,移动端和云端应用程序将继续受到极大的关注,尤其是基于AI的应用程序。有预测认为,2023年将会出现大量的智能家居、自动驾驶、语音助手等应用,这些网站将具有更大的前景。收藏 | 0点赞 | 0打赏
红鲱鱼发布欧洲100佳创业企业名单
红鲱鱼欧洲100佳创业企业评选活动上周在阿姆斯特丹结束,这次活动评出了欧洲100家最具潜力的创业企业。此次论坛讨论了2016年及未来几年欧洲创业者面临的前景。众多顶级投资人参与了讨论,指导创业企业如何吸引合适的投资机构。演讲嘉宾和听众都认为在欧洲融资仍然是主要挑战,这对于创业者来说尤其重要。论坛讨论结束,企业开始登台宣讲,评选标准和去年一样严格。参与评选的企业来自整个欧洲,从俄罗斯到法国到英国到德国。这100家最佳企业都是经过严格的层层筛选评出的,荣誉来之不易,也是众望所归。这些企业来自不同的领域,他们在欧洲以及全球市场的发展将备受瞩目,也会受到密切跟踪。 2016年欧洲100佳创业企业榜单公司行业国家12Return软件荷兰3megawatt GmbH清洁技术德国4th Office云技术英国6Tribes社交媒体英国Acast AB娱乐媒体瑞典Accellta Ltd生命科学/生物技术以色列Agiboo BV云技术荷兰agile42其他德国Anders Innovations网络/互联网芬兰Appentra Solutions软件西班牙arivis AG软件德国Beyond Sports虚拟技术荷兰Billage云技术西班牙Biovotion AG医疗设备瑞士BISEES INFORMATION SYSTEMS软件爱尔兰CARENITY社交媒体法国CloudEndure云技术以色列Compliance Risks专业服务爱尔兰Create Intelligence Ltd软件/分析英国Cree GmbH清洁技术奥地利CropX云技术以色列DCA (Data-Centric Alliance)大数据/存储俄罗斯Diviac AG互联网/网络瑞士EasyPark Group其他瑞典Educated Change Ltd娱乐媒体英国Enso Detego GmbH软件奥地利eSMART Technologies清洁技术瑞士EVRYTHNG云技术英国Fairsail云技术英国Featurespace其他英国FINALCAD软件法国GetIntent广告技术美国Grapeshot软件英国GuardSquare网络安全比利时HeadSense Medical医疗设备以色列HELLO STAGE娱乐媒体美国Herta Security网络安全西班牙High-Tech Bridge网络安全瑞士homePad Solutions互联网/网络瑞士ICS2大数据/存储以色列ID Finance银行/并购西班牙indoo.rs GmbH软件奥地利Kameleoon广告技术法国Keypasco网络安全瑞典KTH Event Agency清洁技术土耳其Lemon Way互联网/网络法国Leverate软件以色列LibraEsva Srl网络安全意大利MediSapiens Ltd生命科学/生物技术芬兰MeetApp AB移动技术瑞典Metafused Ltd广告技术英国moblin大数据/软件/广告技术以色列Mols Media BV软件荷兰MYMobile Security网络安全英国nestpick其他德国Nexthink软件瑞士OneSoon Limited软件英国Openhost, SL云技术西班牙Optimal Plus大数据/存储以色列OurCrowd风险资本/投资以色列Overleaf云技术英国P.I.Works电讯土耳其Padawan Ltd网络英国Parx Plastics BV清洁技术荷兰PleaseTech Ltd软件英国Polar OLED Ltd硬件英国Privatequity.biz互联网/网络以色列Productsup GmbH软件德国Pyreos Limited硬件英国Qosmos软件法国QUASARDB软件法国Radisens Diagnostics医疗设备爱尔兰rational motion清洁技术德国RedCloud Technologies软件英国Relay42云技术荷兰Roima Intelligence其他芬兰Securosys SA网络安全瑞士Sentiance软件比利时Shadow Technologies网络/互联网以色列Smoltek AB纳米技术瑞典Spideo软件法国Sportswik AB娱乐媒体dia瑞典Stratoscale虚拟技术以色列Takeaway.com B.V.网络/互联网荷兰Teamnet Group软件罗马尼亚The Nostrum Group Ltd软件英国TimeLog A/S软件丹麦Tinitell电讯瑞典TitanHQ网络安全爱尔兰Umbilical Design其他瑞典VATBOX云技术英国Venture Spirit云技术比利时VerseOne软件英国Visma Solutions Oy云技术芬兰Wax Digital Ltd云技术英国网络lib大数据/存储法国WeekCal BV移动技术荷兰Zapgocharger Ltd消费电子英国Zebra Medical Vision生命科学/生物技术以色列ZeroLight软件英国
《中国网络媒体的未来2014》71页PPT
点击标题下「大数据文摘」可快捷关注[摘要]腾讯科技企鹅智酷栏目与中国人民大学新闻学院新媒体研究所共同发布了移动媒体趋势报告《中国网络媒体的未来2014》11月12日下午,“2014腾讯网媒体高峰论坛”在北京举行。腾讯科技企鹅智酷栏目与中国人民大学新闻学院新媒体研究所共同发布了移动媒体趋势报告《中国网络媒体的未来2014》。中国人民大学新闻学院教授彭兰做了演讲。以下是彭兰的演讲要点。报告指出,移动互联网正是我们要面对的未来,这在中国尤为明显。2014年6月,中国手机上网比例首次超过PC机上网比例,手机网民的规模超过了八成。在移动时代,媒体的疆域也在拓展。移动媒体就等于新闻客户端?当然不是。移动媒体=内容媒体+关系媒体+服务媒体。中国移动媒体升级综合新闻客户端仍然是用户首选。每天多次打开新闻客户端的用户占比达到了77.5%。新闻客户端形式的革新:新闻客户端对内容品质要求越来越高,比肩甚至超越传统媒体。短板:在移动端如何平衡轻量化阅读和深度内容,仍是需要持续优化之处。综合新闻客户端机会有限,但在垂直领域可谓空间广阔。时政、财经、科技等领域拥有天然的关注度和广泛的信息数据,可称为垂直移动应用的新窗口。移动自媒体:公众号成催化剂在微信平台上,平均每天人均阅读文章5.86篇。日均阅读文章数超过3篇的用户占比达到51%。自媒体面临可持续化的挑战。1.受政策、平台因素影响大;2.需要高度的自我规范与自我约束;3.专业能力决定生存能力。中国用户如何使用移动媒体?近半数用户使用移动终端每天超过3小时。近七成用户更多使用移动终端阅读新闻资讯,而依赖电脑看新闻的用户不到一成。近八成用户会在社交平台分享新闻。中国移动媒体未来五个趋向新闻客户端的社交深化;UGC和内容众包模式升级;入口向平台转化加速;可穿戴设备带来数据资源扩张;移动服务客户端的“媒体化”可能。PPT全文如下:摘自:腾讯科技
Discourse 论坛激活邮件问题
根据 Discourse 的官方推荐,我们使用的是 MailGun 的服务。在大部分情况下都没有问题,但是在一些特定的邮件地址,例如 iCloud,我们在发送激活邮件的时候有提示为:代码语言:javascript复制"message": "5.5.1 Error: need MAIL command - MAIL FROM error: 450 4.1.8 <[email protected]>: Sender address rejected: Domain not found",下面的图片显示了邮件服务提供商提供的错误日志信息:问题和解决上面问题的主要原因是一些特定的邮件服务商有垃圾邮件校验策略。尤其是 iCloud,在 iCloud 中对发送电子邮件的域名地址会进行校验。因为我们的服务使用了子域名的发送,我们发送的域名地址实际为:mail.visafn.com而不是传统的visafn.com因此,iCloud 没有办法校验上面的地址,而认为系统发送的邮件为垃圾邮件而拒收了。解决办法解决的办法也非常简单,需要确定mail.visafn.com我们发送邮件使用的这个二级域名的 MX 记录在 DNS 上要存在。如上图标记的内容。二级域名的 MX 记录需要添加到你的 DNS 上,并且上面的记录要显示为被校验。当然更新 DNS 不会马上生效,你如果继续往 iCloud 中发送的话,可能还是会出现间歇性错误。等待几个小时后再进行测试,通常能够解决发送邮件被标记为垃圾邮件的问题。https://www.ossez.com/t/discourse/14216
BBS论坛(二十五)
25.1.发布帖子后台逻辑完成(1)apps/models.py代码语言:javascript复制class PostModel(db.Model):
__tablename__ = 'post'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
create_time = db.Column(db.DateTime, default=datetime.now)
board_id = db.Column(db.Integer, db.ForeignKey('board.id'))
board = db.relationship('BoardModel', backref='posts')(2)front/decorater.py代码语言:javascript复制from flask import session,redirect,url_for
from functools import wraps
import config
def login_requried(func):
@wraps(func)
def wrapper(*args,**kwargs):
if config.FRONT_USER_ID in session:
return func(*args,**kwargs)
else:
return redirect(url_for('front.signin'))
return wrapper(3)front/forms.py代码语言:javascript复制class AddPostForm(BaseForm):
title=StringField(validators=[InputRequired(message='请输入标题')])
content=StringField(validators=[InputRequired(message='请输入内容')])
board_id=IntegerField(validators=[InputRequired(message='请选择版块')])(4)front/views.py代码语言:javascript复制@bp.route('/apost/', methods=['POST', 'GET'])
@login_requried
def apost():
if request.method == 'GET':
boards = BoardModel.query.all()
return render_template('front/front_apost.html', boards=boards)
else:
form = AddPostForm(request.form)
if form.validate():
title = form.title.data
content = form.content.data
board_id = form.board_id.data
board = BoardModel.query.get(board_id)
if not board:
return restful.params_error(message='没有这个版块')
post = PostModel(title=title, content=content, board_id=board_id)
post.board = board
db.session.add(post)
db.session.commit()
return restful.success()
else:
return restful.params_error(message=form.get_error())25.2.发布帖子界面布局完成(1)把ueditor静态文件放到static目录下(2)apps目录下新建ueditor目录init.py代码语言:javascript复制from .ueditor import bpueditor.py代码语言:javascript复制#encoding: utf-8
from flask import (
Blueprint,
request,
jsonify,
url_for,
send_from_directory,
current_app as app
)
import json
import re
import string
import time
import hashlib
import random
import base64
import sys
import os
from urllib import parse
# 更改工作目录。这么做的目的是七牛qiniu的sdk
# 在设置缓存路径的时候默认会设置到C:/Windows/System32下面
# 会造成没有权限创建。
os.chdir(os.path.abspath(sys.path[0]))
try:
import qiniu
except:
pass
from io import BytesIO
bp = Blueprint('ueditor',__name__,url_prefix='/ueditor')
UEDITOR_UPLOAD_PATH = ""
UEDITOR_UPLOAD_TO_QINIU = False
UEDITOR_QINIU_ACCESS_KEY = ""
UEDITOR_QINIU_SECRET_KEY = ""
UEDITOR_QINIU_BUCKET_NAME = ""
UEDITOR_QINIU_DOMAIN = ""
@bp.before_app_first_request
def before_first_request():
global UEDITOR_UPLOAD_PATH
global UEDITOR_UPLOAD_TO_QINIU
global UEDITOR_QINIU_ACCESS_KEY
global UEDITOR_QINIU_SECRET_KEY
global UEDITOR_QINIU_BUCKET_NAME
global UEDITOR_QINIU_DOMAIN
UEDITOR_UPLOAD_PATH = app.config.get('UEDITOR_UPLOAD_PATH')
if UEDITOR_UPLOAD_PATH and not os.path.exists(UEDITOR_UPLOAD_PATH):
os.mkdir(UEDITOR_UPLOAD_PATH)
UEDITOR_UPLOAD_TO_QINIU = app.config.get("UEDITOR_UPLOAD_TO_QINIU")
if UEDITOR_UPLOAD_TO_QINIU:
try:
UEDITOR_QINIU_ACCESS_KEY = app.config["UEDITOR_QINIU_ACCESS_KEY"]
UEDITOR_QINIU_SECRET_KEY = app.config["UEDITOR_QINIU_SECRET_KEY"]
UEDITOR_QINIU_BUCKET_NAME = app.config["UEDITOR_QINIU_BUCKET_NAME"]
UEDITOR_QINIU_DOMAIN = app.config["UEDITOR_QINIU_DOMAIN"]
except Exception as e:
option = e.args[0]
raise RuntimeError('请在app.config中配置%s!'%option)
csrf = app.extensions.get('csrf')
if csrf:
csrf.exempt(upload)
def _random_filename(rawfilename):
letters = string.ascii_letters
random_filename = str(time.time()) + "".join(random.sample(letters,5))
filename = hashlib.md5(random_filename.encode('utf-8')).hexdigest()
subffix = os.path.splitext(rawfilename)[-1]
return filename + subffix
@bp.route('/upload/',methods=['GET','POST'])
def upload():
action = request.args.get('action')
result = {}
if action == 'config':
config_path = os.path.join(bp.static_folder or app.static_folder,'ueditor','config.json')
with open(config_path,'r',encoding='utf-8') as fp:
result = json.loads(re.sub(r'\/\*.*\*\/','',fp.read()))
elif action in ['uploadimage','uploadvideo','uploadfile']:
image = request.files.get("upfile")
filename = image.filename
save_filename = _random_filename(filename)
result = {
'state': '',
'url': '',
'title': '',
'original': ''
}
if UEDITOR_UPLOAD_TO_QINIU:
if not sys.modules.get('qiniu'):
raise RuntimeError('没有导入qiniu模块!')
buffer = BytesIO()
image.save(buffer)
buffer.seek(0)
q = qiniu.Auth(UEDITOR_QINIU_ACCESS_KEY, UEDITOR_QINIU_SECRET_KEY)
token = q.upload_token(UEDITOR_QINIU_BUCKET_NAME)
ret,info = qiniu.put_data(token,save_filename,buffer.read())
if info.ok:
result['state'] = "SUCCESS"
result['url'] = parse.urljoin(UEDITOR_QINIU_DOMAIN,ret['key'])
result['title'] = ret['key']
result['original'] = ret['key']
else:
image.save(os.path.join(UEDITOR_UPLOAD_PATH, save_filename))
result['state'] = "SUCCESS"
result['url'] = url_for('ueditor.files',filename=save_filename)
result['title'] = save_filename,
result['original'] = image.filename
elif action == 'uploadscrawl':
base64data = request.form.get("upfile")
img = base64.b64decode(base64data)
filename = _random_filename('xx.png')
filepath = os.path.join(UEDITOR_UPLOAD_PATH,filename)
with open(filepath,'wb') as fp:
fp.write(img)
result = {
"state": "SUCCESS",
"url": url_for('files',filename=filename),
"title": filename,
"original": filename
}
return jsonify(result)
@bp.route('/files/<filename>/')
def files(filename):
return send_from_directory(UEDITOR_UPLOAD_PATH,filename)(3)perfect_bbs.py代码语言:javascript复制from apps.ueditor import bp as ueditor_bp
app.register_blueprint(ueditor_bp)(4)front/index.html代码语言:javascript复制<a class="btn btn-warning btn-block" href="{{ url_for('front.apost') }}" id="add-post-btn">发布帖子</a>(5)front/apost.html代码语言:javascript复制{% extends 'front/front_base.html' %}
{% from 'common/_macros.html' import static %}
{% block title %}
发布帖子
{% endblock %}
{% block head %}
<script src="{{ static('ueditor/ueditor.config.js') }}"></script>
<script src="{{ static('ueditor/ueditor.all.min.js') }}"></script>
<script src="{{ static('front/js/front_apost.js') }}"></script>
{% endblock %}
{% block body %}
<form method="post">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">标题</span>
<input type="text" name="title" class="form-control">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">版块</span>
<select name="board_id" class="form-control">
{% for board in boards %}
<option value="{{ board.id }}">{{ board.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<script id="ueditor" type="text/plain" style="height:500px;"></script>
</div>
<div class="form-group">
<button class="btn btn-danger" id="submit-btn">发布帖子</button>
</div>
</form>
{% endblock %}(6)front/js/apost.js代码语言:javascript复制$(function () {
var ue=UE.getEditor('ueditor',{'serverUrl':'/ueditor/upload/'});
});
论坛系统
Discuz!是一个功能强大、灵活可定制的论坛软件系统,适用于搭建各种规模和类型的在线社区。由于其在中文网络社区的广泛应用,它在全球范围内具有相当高的知名度。今天我们就学着手动搭建一下 Discuz! 。搭建过程本教程基于LNMP,所以前提1是需要安装好lnmp,如果你没安装好,那么可以参考这篇文章:https://vwo50.club/archives/1169.html 前提2:如果你需要通过域名访问该论坛,那么请提前设置好域名解析我们找个路径,执行命令 git clone https://gitee.com/Discuz/DiscuzX.git 来将discuzx安装包下载下来。 然后赋予upload目录对应的权限 chmod -R 777 /www/wwwroot/DiscuzX/upload/在对应的nginx配置文件的目录(若是yum安装的nginx,则一般在/etc/nginx/conf.d下)里新增 discuz.conf,输入以下内容。代码语言:javascript复制 server {
listen 80;
server_name 你的域名;
location / {
root /www/wwwroot/DiscuzX/upload;
index index.php index.html index.htm;
}
location ~ \.php$ {
root /www/wwwroot/DiscuzX/upload;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}使用certbot执行命令: sudo certbot --nginx -d 你的域名 当页面出现以下success相关的输出,证明就配置成功了。代码语言:javascript复制equesting a certificate for 你的域名
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/你的域名/fullchain.pem
Key is saved at: /etc/letsencrypt/live/你的域名/privkey.pem
This certificate expires on 2024-04-04.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for 你的域名 to /etc/nginx/conf.d/discuzx.conf
Congratulations! You have successfully enabled HTTPS on https://你的域名
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le使用certbot会自动帮我们签约证书,以及开启https,自动修改配置。如果你没有安装的话,可以参考这个文章来安装一个: https://vwo50.club/archives/1039.html接下来我们前台访问自己的域名,就会出现安装引导页面,我们按照提示一直下一步即可,最后创建好对应的数据库,填入数据库相关的配置,大功告成。
WordPress 论坛插件:Simple Forum
Simple Forum 是一个能够作为一个页面整合到你的 blog 中的论坛插件。
翻译自:Weblog Tools Collection 的 APAD: Simple Forum插件:
Simple Forum描述:
一个含有完全功能的 WP 论坛插件,它能完成论坛所能做绝大部分事情。特征:作为一个页面整合到 WordPress 中含有一个论坛拥有的绝大部分功能有防止垃圾帖子的功能能够辨识出 WordPress 用户的角色和权限有多种语言版
评论:首先从这个插件的功能就知道这个插件是非常具有特色的,我个人没有去试过这个插件并且以后也不太可能用使用它,唯一的原因是,我的论坛运行在独立的域名下并且使用SMF(译者注:我是使用 bbPress 创建论坛的。用户信息可以和 blog 共享),暂时也没有任何几乎去整合到 blog 中去的想法。然而我已经通过这个论坛的文档以及观察演示的网站同样获得良好的感觉。Simple Forum 允许你一个全功能的论坛并嵌入到你现有的 WordPress 的 blog 中。这对那些想创建某种支持论坛的人来说是非常完美的,同样对那些想给他 blog 创建论坛,但又不想把它现成的用户到新的论坛中的用户也是适用。这个插件允许你创建子论坛(包括公共的和基于角色的),加密和置顶帖子或者主题。它也支持来宾,普通用户,仲裁者等用户角色。作者已经在他的站点提供了非常详细的文档,并且你可以下载 PDF 版本的文件。另外我喜欢的特性是它能够完全的卸载,一旦你因为无论何种原因决定不再继续。我喜欢能得到你的回馈。你在用这个插件吗?你感觉这个插件有多大好处?为什么你选择它而不是安装一个独立的论坛软件如 SMF 或者 phpbb。对作者有什么建议吗?
用JAVA的DEA算法衡量社交媒体页面的流行度
Measuring the Social Media Popularity of Pages with DEA in JAVA原文作者:Vasilis Vryniotis原文地址:http://blog.datumbox.com/measuring-the-social-media-popularity-of-pages-with-dea-in-java/译者微博:@从流域到海域译者博客:blog.csdn.net/solo95用JAVA的DEA算法衡量社交媒体页面的流行度在前面的文章中,我们讨论了数据包络分析(Data Envelopment Analysis)技术,我们已经看到它如何被用作一个有效的非参数排序算法。在这篇博文中,我们将开发出一个JAVA数据包络分析的实例,我们将用它来评估网络上的网页和文章的社交媒体流行度。该代码是开源的(在GPL v3 license下),您可以从Github免费下载。更新:Datumbox机器学习框架现在是开源的,可以免费下载。查看包com.datumbox.framework.algorithms.dea以查看Java中Data Envelopment Analysis的实现。数据包络分析在JAVA中的实现代码是用JAVA编写的,可以直接从Github下载。它是根据GPLv3许可的,所以可以随意使用它,修改它,或者再分发。该代码实现了数据包络分析(Data Envelopment Analysis)算法,使用lp_solve库来解决线性规划问题,并使用Web搜索引擎优化分析(Web SEO Analytics )索引提取的数据,以构建基于Facebook,Google Plus和推特上分享的一个混合的社交媒体页面流行度矩阵。在前面的文章中介绍了算法的所有理论部分,在源代码中可以找到关于其实现的详细的javadoc注释。(原博文之后数据包络分析(Data Envelopment Analysis)算法及其实现全部简称了DEA,请读者注意,译者注。)下面我们提供一个关于其架构实现的高级别描述:1. lp_solve 5.5 library为了解决各种线性规划问题,我们使用一个名为lp\_solve的开源库。某些特定的lib是用ANSI C编写的,并使用JAVA包装来调用库方法。因此,在运行代码之前,您必须在您的系统上安装lp_solve。该库的二进制文件在[Linux和Windows都可以使用,您可以在lp_solve文档中阅读更多有关安装的信息。在尝试运行JAVA代码之前,请确保您的系统上安装了(相关的)特定库。有关安装和配置库的任何问题,请参阅lp_solve文档。2.DataEnvelopmentAnalysis Class这是DEA算法的主要实现类。它实现了一个名为estimateEfficiency()的公共方法,它获取记录的Map并返回它们的DEA得分。3. DeaRecord ObjectDeaRecord是一个特殊的对象,用于存储我们记录的数据。由于DEA需要分离输入和输出,因此DeaRecord对象将以DEA可以处理的方式分别存储我们的数据。4. SocialMediaPopularity ClassSocialMediaPopularity是一个应用程序,它使用DEA来评估社交媒体网络上Facebook的like,Google的 +1和twitter的Tweets的网页流行度。它实现了两个受保护的方法:calculatePopularity()和estimatePercentiles()以及两个公共方法loadFile()和getPopularity()。calculatePopularity()使用DEA实现根据社交媒体计数来估计页面的得分数。estimatedPercentiles()方法获取DEA分数并将其转换为百分位数。总的来说,百分比比DEA分数更容易解释; 因此当我们说一个网页的流行分数是70%时,这意味着该网页比70%的其他网页更受欢迎。为了能够估计一个特定页面的流行度,我们必须有一个包含其他页面的社交媒体数据的数据集。这是有原因的,因为需要预测哪个网页是受欢迎的,哪些不是,您必须能够将其与网络上的其他页面进行比较。为此,我们使用来自以txt格式提供的Web SEO分析索引的小型的匿名样本。您可以通过从网页上的更多页面提取社交媒体计数来构建自己的数据库。(社交媒体计数,比如点赞数、转发数、评论数)loadFile()方法用于加载DEA的上述统计信息,getPopularity()方法是一种易于使用的方法,可以获取Facebook的like,Google的+1和一个页面的Tweets数量,并以此评估其在社交媒体上的流行度。如何使用数据包络分析的JAVA实现在DataEnvelopmentAnalysisExample类中,我提供了2个不同的关于如何使用代码的例子。第一个例子直接使用DEA方法来根据它们的输出(ISSUES,RECEIPTS,REQS)和输入(STOCK,WAGES)来评估组织单位的效率。这个例子来自DEAzone.com的一篇文章。代码语言:txt复制Map<String, DeaRecord> records = new LinkedHashMap<>();
records.put("Depot1", new DeaRecord(new double[]{40.0,55.0,30.0}, new double[]{3.0,5.0}));
//...adding more records here...
DataEnvelopmentAnalysis dea = new DataEnvelopmentAnalysis();
Map<String, Double> results = dea.estimateEfficiency(records);
System.out.println((new TreeMap<>(results)).toString());第二个示例使用我们的社交媒体流行度应用程序,通过使用来自社交媒体的数据来评估页面的流行度,例如Facebook的like,Google的+1和Tweets。所有的社交媒体计数都被标记为输出,我们传递给DEA一个空的输入向量。代码语言:txt复制SocialMediaPopularity rank = new SocialMediaPopularity();
rank.loadFile(DataEnvelopmentAnalysisExample.class.getResource("/datasets/socialcounts.txt"));
Double popularity = rank.getPopularity(135, 337, 9079); //Facebook likes, Google +1s, Tweets
System.out.println("Page Social Media Popularity: "+popularity.toString());必要的扩展(上面)所提供的代码只是DEA如何被用作排名算法的一个例子。为了改进其实现,需要进行下面的扩展:1.加速(算法的)实现特定的DEA算法实现会评估数据库中所有记录的DEA得分。由于我们需要解决如同数据库中记录数量那样多的线性规划问题,这使得实现变得缓慢。如果我们不需要计算所有记录的分数,那么我们可以显著地加快执行速度。因此,该算法的小扩展可以使我们更好地控制哪些记录应该被解决掉,哪些只能被用作约束。2.扩大社交媒体统计数据库(这篇文章所)提供的社交媒体统计数据库由来自Web SEO Analytics索引的1111个样本组成。为了能够估计更准确的流行(度)分数,需要更大的样本。您可以通过统计来自网络上更多页面的社交媒体计数来创建自己的数据库。3.添加更多的社交媒体网络该实现使用Facebook的喜欢,Google的+1和推文的数量来评估文章的受欢迎程度。不过,来自其他社交媒体网络的指标可以很容易地被考虑在内。您只需要从您感兴趣的网络中构建一个社交媒体数据库,然后扩展SocialMediaPopularity类来处理它们。关于实施的最终意见为了能够扩展(算法的)实现,您必须对Data Envelopment Analysis的工作原理有一个很好的理解。这在前面的文章中已经介绍过了,所以在继续进行任何更改之前,请确保您阅读了之前的教程。此外,为了使用JAVA代码,您必须在您的系统中安装lp\_solve库(参见上文)。如果你在一个有趣的项目中使用这个实现,那么就给我们一条线索,我们将在我们的博客上展示你的项目。另外,如果你喜欢这篇文章,请花点时间在Twitter或Facebook分享。
【程序源代码】校园社交平台
“ 关键字: “校园社交平台 " 01————【总体介绍】校园社交平台本项目是为满足大学生的校园社交需求而设计的,动态模块提供发布/删除/搜索/点赞/收藏/评论动态功能,个人模块提供关注与私信以及用户修改个人信息功能,聊天模块提供即时聊天功能。该项目为前后端分离项目并且后端实现分布式,同时接入了和风天气接口。软件架构前端框架:Vue.js, Element UI后端框架:Spring Cloud反向代理服务器:Nginx持久层框架:Mybatis权限认证框架:Shiro数据库:MySQL,Redis全双工通讯协议:Websocket————【技术框架】 安装教程 1、本地准备开发工具小程序开发工具克隆本项目代码到本地打开微信开发者工具;添加项目->选择本项目目录->编译执行;————【源码使用说明】https://gitee.com/sherldonlock/campus-social-platform.git
泛娱乐社交一代:95后社交行为洞察报告
据CNNIC2017年12月数据,社交网民规模近7.2亿,社交成为用户上网的核心诉求。社交应用由即时通信向综合性、娱乐化方向发展,种类和功能也越来越丰富。作为社交应用主力的95后,社交诉求如何?使用哪类社交产品?社交行为有哪些特点?酷鹅用户研究院(微信ID:kueclub)联合腾讯网产品研发中心,开展95后用户系列研究:▼阅读更多95后报告请点击特立独行一代:深度解读95后互联网生活方式兴趣导向:95后用户内容消费洞察报告本期为系列报告第三期,将为您呈现95后用户的社交行为。报告所发布内容均为酷鹅用户研究院作为第三方研究平台的独立数据和研究分析,不代表任何企业的立场,转载请注明报告来源。研究方法与说明定量研究:线上问卷本次调研通过酷鹅用户研究院用户社区、公众号等多渠道投放问卷,覆盖PC和移动端,根据CNNIC提供的中国网民结构对95后和95前用户按比例进行投放,共回收有效问卷13530份。定性研究:用户日志、深度访谈历时一周通过用户行为日志对15名典型95后的生活及互联网产品的使用进行跟踪研究,并与30名95后进行深度交流,样本包括在校学生、在职人员,并涉及不同地域、不同兴趣领域用户。TGI指数说明TGI 指数= [目标群体中具有某一特征的群体所占比例/总体中具有相同特征的群体所占比例]*标准数100酷鹅核心洞察社交内容垂直化对于95后而言,内容即是社交。95后在内容社交应用的使用上有着更高的偏好,使用过程中更多关注兴趣话题,获取垂直领域内容,同时,由优质内容带动个体关注的社交需求也更为明显。社交玩法娱乐化95后乐于表达自己,喜欢尝试新鲜、趣味和个性化的互动形式,如在产品中引入弹幕、打赏、匿名评价等带有一定娱乐性质的新玩法,加入个性化元素,能够吸引95后使用。社交匹配精准化95后乐于在“志同道合”的同辈群体中交流,注重社交产品的用户环境和交友体验感。在陌生人或匿名交友场景中,基于95后人群兴趣标签和性格特质,通过精准匹配的方式让他们找到同质群体,是提升95后交友体验的关键。
媒体设备枚举
写作背景:前几节我们都是使用的默认的音视频设备来进行媒体数据的输出,我们这一节就将系统中可以使用的音频输入、输出设备及视频输入设备列举出来并进行切换。涉及 API:枚举设备的 API 是 navigator.mediaDevices.enumerateDevices(),它将会异步回调给我们一个 MediaDeviceInfo 数组,每个 MediaDeviceInfo 将包含一个设备的 deviceId ,设备名称 label ,设备的类型 kind 。设备类型包括:audioinput(音频输入),audiooutput(音频输出),videoinput(视频输入)。代码语言:javascript复制// 获取当前计算机可列举的音频输入|输出|视频输入设备
navigator.mediaDevices
.enumerateDevices()
.then((devices) => {
for (const device of devices) {
if (device.kind === "audioinput") {
audioInputDevices.push(device);
} else if (device.kind === "audiooutput") {
audioOutputDevices.push(device);
} else if (device.kind === "videoinput") {
videoDevices.push(device);
}
}
})
.then(() => {
resolve({ audioInputDevices, audioOutputDevices, videoDevices });
});注:当用户选择完设备后最好支持保存配置,省去下次调整,因为用户的偏好和设备时候故障都会影响到用户的实际使用。HTMLMediaElement.setSinkId()API 是一个实验功能,可以通过传入一个音频输出设备的 deviceId 来切换不同的音频输出设备:代码语言:javascript复制const handleChangeAudioOutputDevice = (value: string) => {
selectedAudioOutputDevice.value = value;
video
// @ts-ignore
.value!.setSinkId(value)
.then(() => {
console.log(`音频输出设备设置成功${value}`);
})
.catch((error: Error) => {
console.log(error);
});
};注:实际开发时在 HTMLMediaElement 接口中没能找到 setSinkId 函数,但功能正常,这里先使用 @ts-ignore将下一行的错误忽略。启动设备:在这里我们要扩展约束条件,以支持使用默认选择的各设备:代码语言:javascript复制const openDevice = () => {
const constraints: MediaStreamConstraints = {
audio: {
deviceId: selectedAudioInputDevice.value
? { exact: selectedAudioInputDevice.value }
: undefined,
},
video: {
deviceId: selectedVideoDevice.value
? { exact: selectedVideoDevice.value }
: undefined,
},
};
...
};注:强烈推荐使用 TypeScript 来开发,并指定真实的类型来得到优秀的代码提示;其他步骤同前几节的方式,也可以看文末的完整代码。完整代码:Resolution.vue结语:我们通过 enumerateDevices 的到了系统中已存在的音频输入、输出设备及视频的输入设备,在视频会议等软件中也需要支持多种设备的切换来满足不同的场景。明天继续~
流媒体加密
本文作者:IMWeb 吴浩麟 原文出处:IMWeb社区 未经同意,禁止转载 本文只讨论应用于浏览器环境的流媒体协议的加密
为什么要加密视频付费观看视频的模式是很多平台的核心业务,如果视频被录制并非法传播,付费业务将受到严重威胁。因此对视频服务进行加密的技术变得尤为重要。本文所指的视频加密是为了让要保护的视频不能轻易被下载,即使下载到了也是加密后的内容,其它人解开加密后的内容需要付出非常大的代价。无法做到严格的让要保护的视频不被录制,原因在于你需要在客户端播放出视频的原内容,解密的流程在客户端的话不法分子就能模拟整个流程,最保守也能用屏幕录制软件录制到视频的原内容(可以通过加水印的方法缓解下)。我们的目标是让他获取原内容的代价更大。常见视频加密技术视频加密技术分为两种:防盗链:通过验证的用户才能访问到没有加密的视频内容,这种方案存在视频很容易就被下载的风险,严格来说这不属于加密。这种方式其实是资源访问授权,它实现起来简单。加密视频本身:通过对称加密算法加密视频内容本身,用户获得加密后的视频内容,通过验证的用户可以获取解密视频的密钥,在客户端解密后播放。这种方式实现起来流程复杂会带来更多的计算量。一般结合这两种技术一起用,第1种技术很成熟也有很多教程就不再复述,本文主要介绍第2种加密技术。流媒体加密技术原理看视频分为两种,看点播和看录播。 要看点播可以通过下载完整个视频后再看,或者通过流媒体边下边看。 看直播只能通过流媒体看最新的画面。加密整个视频的技术很简单,把视频看成一个文件采用加密文件的技术,这种技术太多就不介绍了。 加密流媒体的技术很少,也很难找到学习资料,本文主要介绍流媒体加密技术。常见的应用与浏览器播放的流媒体传输协议有:HLS:Apple 推出的基于 HTTP 协议的 MP4 分片传输协议,可用于点播和直播场景。每下载一个分片都需要发生一次 HTTP 请求,所以严格来说 HLS 不能称为流媒体传输协议。HTTP-FLV:基于 HTTP 长连接的 FLV 分块 tag 传输协议,可用于点播和直播场景。RTMP:基于 TCP 的 FLV 分块 message 传输协议,用于 Flash 客户端。流媒体加密原理可以看出一个规律这些流媒体传输协议都必须把视频流拆分成连续的小块之后再被传送,只不过分块的大小和视频容器的格式不一样而已。流媒体加密技术的核心就在于对这每一小块视频分别使用对称加密算法,在服务端加密客户端解密,通过权限验证的用户才能拿到解密一小块视频的密钥。为什么不用 HTTPS 加密可能有人会问为什么不用 HTTPS 加密?原因是 HTTPS 在网络传输层进行非对称加密,目的是为了防止中间人窃听劫持,任何人都可以和我们的服务器建立 HTTPS 链接获取到原数据。而视频加密的目的不是为了防止有中间人窃听我们的视频数据,而是要让视频数据本身被加密。为什么选择对称加密现代成熟的加密技术分为对称加密算法和公钥密码算法(非对称加密)。之所以选择对称加密是因为流媒体要求很强的实时性,数据量又很大。公钥密码算法的计算都比较复杂,效率较低,适合对少量数据进行加密。对称加密效率相对较高,所以流媒体加密首选对称加密。例如在 SSH 登入的时候会先通过公钥密码算法传输一个密钥,再用这个密钥用作对称加密算法的密钥,在数据传输过程中使用对称加密算法来提示数据传输效率。HLS 加密HLS 是目前最成熟的支持流媒体加密的能应用在浏览器里的流媒体传输协议,HLS 原生支持加密,下面来详细介绍它。在介绍如何加密 HLS 先了解下 HLS 相比于其它流媒体传输协议的优缺点。 优点在于:建立在 HTTP 之上,使用简单,接入代价小。分片技术有利于 CDN 加速技术的实施。部分浏览器原生支持,支持点播和录播。缺点在于:用作直播时延迟太大。移动端支持还好,PC端只有 Safari 原生支持。HLS 加密原理HLS 由两部分构成,一个是 .m3u8 文件,一个是 .ts 视频文件(TS 是视频文件格式的一种)。整个过程是,浏览器会首先去请求 .m3u8 的索引文件,然后解析 m3u8,找出对应的 .ts 文件链接,并开始下载。 m3u8 文件是一个文本文件,在开启 HLS 加密时,内容大致如下:代码语言:javascript复制#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:26
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.do?k=1"
#EXTINF:9.901,
http://media.example.com/segment26.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.do?k=2"
#EXTINF:9.501,
http://media.example.com/segment28.ts这个文件描述了每个 TS 分片的 URL ,但这些分片都是加密后的内容,要还原出原内容需要从代码语言:javascript复制#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.do?k=1"中解析出获取解密密钥的URL https://priv.example.com/key.do 和对称加密算法 AES-128 。 获取到密钥后再在客户端解密出原内容。 可以看出启用 HLS 加密后会多出更多的事情:针对每个 TS 需要去请求获取密钥。需要多提供一个给客户端获取密钥的鉴权服务。针对每个 TS 需要去执行对称加密的解密计算。这会带来更多的网络请求和计算量,可能会对延迟和性能造成一定的不良影响。HLS 加密实战支持 HLS 的客户端都原生支持加密,所以要开启 HLS 加密你只需要修改你的服务端:修改 HLS 视频服务,对 TS 加密,往 m3u8 中加入 EXT-X-KEY 字段。提供给客户端获取密钥的鉴权服务,即上面的 https://priv.example.com/key.do?k=1 所指向的服务。目前大多数云服务都支持 HLS 加密服务,如果你想直接搭建 HLS 加密服务可以使用 nginx-rtmp-module。HLS 兼容方案目前 HLS 存在兼容性问题:针对桌面端低版本浏览器例如 IE,可以使用 Flash 技术来播放 HLS ,详细使用见开源项目 flashls。针对非Safari高版本浏览器例如 Chrome ,可以使用 Media Source Extensions API 去实现播放 HLS,详细使用见开源项目 hls.js。针对即不原生支持 HLS又不支持 Media Source Extensions 也不支持 Flash 的浏览器,常见于部分低版本移动端浏览器,我也找不到什么好办法。flashls 和 hls.js 都支持 HLS 加密技术。破解 HLS 加密有加密就有破解,在明白 HLS 加密原理后,你想过如何去破解它吗?先定义下破解成功是指:获取到视频加密前的完整原文件。我想到的方法是:先付费买一个可正常观看受保护视频的账号。用抓包工具抓下所有网络请求(可以筛选下限制到只保存 HLS 和 获取key 的请求,防止保存太多垃圾数据)。第2步保存下来了加密后的 TS 分片和加密分片所需要的密钥。写一个脚本以 m3u8 为索引一一解密出加密后的 TS 分片的原文件,再把 TS 拼接成完整的视频原文件。似乎破解的难度也不会很复杂。总结目前流媒体加密技术还不成熟,除了 HLS 协议提供了方便成熟的方案外,其它协议的加密技术还不成熟。RTMP 协议提供了一个变种版 RTMPE 可以加密流媒体,原理和 HLS 加密类似,但是我还找不到合适的服务端去支持 RTMPE 协议。阅读原文
DOV社交探索 | 贴纸极致设计
腾讯ISUXisux.tencent.com社交用户体验设计
前言当短视频场景的发展与突破回到普通用户的原创内容创作时,“贴纸”“音乐”“转场”等原创工具的设计,都成为直接影响到发表品质的关键。这篇文章在于详述在DOV的设计过程中,对“贴纸”这一品类的设计思考。什么样的贴纸能让用户的发表更加精彩?而在短视频上使用的贴纸在设计上又会有怎样的不同?我们需要深究用户使用贴纸的场景和对贴纸做更深入和更极致的设计。洞察观察普通用户在多个社交平台的实际发表内容中对贴纸的使用,我们发现:用户对贴纸使用的诉求中,传统的遮挡需求依然是核心;但为自己拍摄的视频增加丰富的后期元素,扩大短视频中表达的信息,甚至可以与短视频中的内容进行互动,也成为了新的可能与目标。设计目标我们将这些挖掘到的诉求点进行归纳,总结出了贴纸设计所需的三个要素:1、提供或扩大信息2、帮助表达情绪3、能够与内容互动提供或扩大信息普通用户随手拍摄的生活视频对事件的叙述往往都不够完整,贴纸可以帮助用户补齐信息。除地理位置、时间等辅助记录的客观信息外,还应当有更多表示主观状态的信息提供。在对主观状态贴纸的设计中,应更多地表现正在进行中的状态,更准确地描述一个事件。例如:在此类贴纸的设计中,融入DOV的品牌色黄色同时,结合空心字的设计,整体设计体量比较轻比较穿透,不会过多干扰视频的信息传递。
帮助表达情绪普通用户拍摄的生活视频会缺乏传达内在情绪的表演。贴纸需要帮助用户更好地传达情绪,述说视频画面中无法表达的心情,让观众们能够更好地感同身受。所以贴纸如何能更好地表达情绪呢?1、找到有共鸣的载体,可以让用户感同身受。以《夏日小物》中的“生气”这个情绪为例,我们需要选取一个最能够与用户及观众产生共鸣的形象,可以看到,“河豚”的自然习性非常符合“生气”的表达。2、有效的表演,可以让情绪表达的更充分更饱满。遵循自然的运动规律,并放大运动中节奏的变化,让贴纸动画表现更生动,达到有效的表演。遵循真实遵循物体本身的真实运动规律。通过表现物体运动的节奏快慢和曲线使之更接近真实。不同的情绪有不同的节奏:睡觉时的节奏比较舒缓,生气时情绪起伏比较大,最后通过节奏的快慢频率变化还原最真实的情绪。自然流畅强化动画设计中的运动弧线可以使动作更加自然流畅。自然界的运动都遵循弧线运动的规则,以这个魔性的蹬腿动作为例,拆分膝关节、脚踝的节点可以看到运动轨迹都是流畅的弧线。表演张力通过增加一些次要的表演动作的辅助动画来增强动感和感染力。我们在动画里还增加了一些次要表演动作,下图可以看到动画呈现中的蓝色点或线的辅助动画。让动效更有表演感染力和张力,情绪更加饱和。3、与内容互动。让贴纸多维度地与视频内容产生互动。短视频与图片之间最大的差异就是在时间与空间的维度有更强大的延展。我们从透视角度、穿插关系、立体表现这3个方面让贴纸更好地与内容互动融合,让视频更有虚实结合的趣味。透视角度一般贴纸提供的透视角度比较单一,那我们设计贴纸的时候会增加不同的透视角度,而且大部分的贴纸只是纯粹的贴合在视频(手机屏幕),与视频内容中的透视和角度是不一样的,把贴纸做到有透视角度,与视频中内容的透视角度保持一致,那贴纸就不会显得突兀,可以真正与内容做到互动和贴合。可以看到下图demo中的贴纸与视频的透视角度是相融合的,整个视频中贴纸不会违和,而是很好地适应了内容。穿插关系我们希望贴纸可以更好地融合内容,融合环境,在创作的时候我们会设计一些有趣的的物体可以穿插在内容上,合理存在于视频的透视中。看似跟内容无关的贴纸与视频结合可以玩出一些特别有趣的视觉效果。同时对于用户来说可以极大程度地激发创作灵感。视频demo中的的腿和雪糕有趣地与视频内容做结合,视频效果显得更加生动。立体表现立体表现是指贴纸本身是立体三维有透视的,现实实景都是有透视的,所以贴纸在视觉和动效表现中呈现出来是立体的,那放在实景中会有一种虚实结合的趣味。秋千的2.5d立体动效表现会与现实场景更加融合,让视频有一种虚实结合的趣味感。用户反馈《夏日小物》的全部展示。贴纸上线后,引发了许多用户的使用与讨论。贴纸不仅激发了用户创作灵感,使内容更有趣味性,最重要的是做到了把UGC内容盘活起来。以下为用户使用的真实发表视频案例:总结回过头来看DOV在贴纸规划上满足并遵循适合UGC创作的贴纸所具备三个要素。信息贴纸和水印贴纸可以帮助用户提供和扩大信息的,发表当下状态;然后像潮牌态度贴纸、秋日私语、有声贴纸、夏日小物可以帮助用户表达情绪和与视频内容互动,更好激发用户创作灵感,同时也较好做出差异化。潮牌贴纸主要帮助表达态度和情绪,对潮牌的二次创作,更加贴近和贴合现在年轻人的情绪和态度的表达。秋日私语的贴纸色调来源于秋天,运用秋天的元素刻画来表达秋日特有的情绪。有声贴纸是一种提供用户创作、与视频内容互动的新方式,与以往单纯对视频本身画面叠加的贴纸不一样,有声贴纸可以改变或叠加视频内的音频,使视频内容达到更丰富和更加意想不到的效果,更多地挖掘用户的创造力。一些感悟贴纸在短视频创作中不可或缺,我们在设计的同时也不断挖掘出更多的可能性。贴纸的最主要核心是为内容服务,让贴纸价值最大化一直是我们研究和探索的目标,一切以用户体验为归依是驱动我们的内核,只有当产品以这个为目标,才能发挥产品的核心价值,才能把好的产品传递给用户。与此同时,设计师要实现自我成长需要自身的不断驱动与鞭策。面对互联网日新月异,从拟物时代、扁平时代到现在的全链路时代,设计师除了在专业上不断提升和与时俱进,设计师更加需要提升自己的产品思维,在项目中发挥自己的价值。最后,附上DOV二维码,欢迎大家下载体验。以下ISUX文章,你可能也感兴趣
▽ ISUX Showreel 2018[腾讯相册] 小程序一亿用户量的设计背后社交短视频游戏的品效合一设定 DOV原创驱动-短视频社交设计思考 参与感 - QQ的AR设计 感谢阅读,以上内容均由腾讯ISUX团队原创设计,以及腾讯ISUX版权所有,转载请注明出处,违者必究,谢谢您的合作。注明出处格式:
文章来自公众号:腾讯ISUX(https://isux.tencent.com/articles/sticker-design.html)↓点击前往 ISUX 官网
Jekyll 社交图标集合创建
前言 一般来说,我们的个人博客都会放上一些社交图标以及社交链接。这样一来,想要关注我们更多的最新研究或工作的读者就可以很快找到路径。于是,在 Jekyll 博客主题设计的时候,通常会在个人简介的地方放置几个社交小图标,点击社交小图标即可把读者带到你的社交个人主页上。对于不同类型的作者,常用的或者关注的社交平台基本上不大一样,社交小图标也会有不一样的需求。比如说,对于从事科研工作的人来说,像谷歌学术、ResearchGate、ORCID 等等能够列举发表论文或者相关研究的平台就比较重要;对于一般程序员来说,像 Github、Gitlab、Segmentfault、CSDN、简书等等能够展示自己所参与的项目和技术心得体会的平台就比较重要;对于前端设计师来说,像 Instgram、UI 中国、Dribble等等能够展示 UI 设计作品的平台就比较重要。因此,对于一款 Jekyll 博客主题的设计者来说,同时要兼顾到这么多不同的需求可能会有点为难,毕竟领域不同、了解的程度也很有限。 俗话说得好,授人以鱼不如授人以渔。今天我们就来讲讲如何自己定制一套社交图标集合。技术发展回顾图片索引 对网站前端设计有点了解的小伙伴可能知道,如果要为网站添加一些社交图标,并且支持鼠标悬停高亮显示,最早的办法是采用不同的图片进行切换的方法。具体来说就是,鼠标悬停前是一张图片,悬停后切换到另外一张图片。由于如果要支持很多个社交图标就要准备很多对这样的图片,那么同一页面内的文件 HTTP 请求数就会陡然增加,对页面加载性能有非常大的影响。 随之产生了一种比较可行的解决方案:将所有的社交图片拼在一张图上,然后通过定位的方式来索引到不同的社交图标,我们通常将这张图称为雪碧图。这种方法的好处比较明显,浏览器只需要发出一次请求下载雪碧图即可,减少了文件 HTTP 请求数,加载时间显著变短。比较明显的困难是,定位找起来简直不要太麻烦。而且为了能描述不同的图标悬停前后的位置,必须写很多对样式与之对应,工作量比较大。另外一个比较大的困难是,图标集合的更新很麻烦。新增图标的时候,为了能沿用原来已经写好的样式,只能在原有的雪碧图的基础上往后增加图标,当然同时也要增加对应的样式。 虽然雪碧图在某种程度上提升了加载效率,但是给后期的更新、维护带来了不小的麻烦。还有一个越来越凸显的问题——随着访问网站的设备类型的不断增多,图片的质量会影响到不同设备、不同平台下的效果一致性,甚至在高分辨率屏幕下会出现图标模糊的情况,用户体验极其不佳。最有效的改进方法可能就是采用分辨率更高、质量更高的图片来拼凑雪碧图,不过同时也会增大雪碧图的文件体积。这会导致虽然只发出了一个文件 HTTP 加载请求,但是由于文件体积过大、加载速度慢,而给用户带来不好的使用体验。字体图标 接着发展出了一种字体图标的东西来解决图片索引中存在的问题。字体图标,顾名思义就是把所有的图标都变成了字体编码一样,只要我们在网页中引入字体图标文件就可以像用 Unicode 一样使用字体图标了。这种方式最好的一点就是,像操纵字体一样设置字体图标的样式。比如说,虽然我们只在字体图标文件中定义了一个图标,但是当我们使用不同的 color 定义时,图标就会改变其颜色。而且,我们操纵字体图标的大小是采用 font-size 的方式而非 width/height 的方式。这样一来,字体图标和行内文字段落一起使用时也是非常匹配的,行距等文字样式都能同等适用。 当然,字体图标在后期的维护、更新过程中也会有些问题。比如说,对于一个大型项目的迭代开发,每个子系统可能都会弄一套需要的字体图标,然后在代码分支合并时就会出现问题。因此,有些项目团队可能会为此而设定一位管理员来专门管理字体图标的更新。每个子系统只能向图标管理员提交他们的更新,最终更新由图标管理员来完成。虽然这样也能在某种程度上解决问题,但是对于图标管理员来说还是会很苦恼。 说到这里,有人可能会想起 Bootstrap 等 UI 框架中自带的字体图标集合。那我们直接使用某个框架提供的字体图标集合不就万事大吉了吗?可事实并非如此,框架所包含的字体图标集合虽然看起来还是比较全面,但是还是有可能缺少某些我们想要的字体图标。想到这里,可能会想不如把多个字体图标集合整合在一起使用不就好了吗?这样随之而来的问题是,很大可能存在大量冗余。对于前端界面来说,除了大量的 HTTP 请求是无法接受的,大量冗余代码也是无法接受的。当我们用 Lighthouse 类似的工具来测试网页的性能时,就可以很容易地发现请求代码的使用率情况。如果我们采用多个字体图标集合并用的方案,那么代码实际使用率可能就会很低。也就是说,请求的代码基本上不会在网页中被使用到,这对于优化页面性能来说简直就是噩梦。 如果我们觉得自己来手动管理、手动更新字体图标文件太麻烦了,其实也可以用阿里的 Iconfont 或者其他类似工具来在线管理字体图标集合。Iconfont 会提供一个阿里 CDN 上的链接地址来直接使用你建立好的字体图标集合,这样一来基本上就解决了上面所说的维护难题。我们在 Iconfont 上更新好字体图标集合后,Iconfont 会生成一个新的链接地址。然后,我们只需要修改页面代码的对应地址就可以非常方便地应用更新。 这么看来,字体图标的方案好像很完美了,但事实上还是存在其他的问题。Iconfont 这类平台比较适合个人开发者或者开源项目,而对于企业开发者或者非开源项目来说可能依然不大适合。另外,字体图标因为像字体一样被操纵,所以只能支持一种颜色,无法同时支持多种颜色。除此之外,如果我们想要预览所有的图标,但是抱歉的是脱离了 Iconfont 这类平台我们可能就没有办法做到。Symbol 图标 实际上除了字体图标在不同设备、不同平台上有相同的效果之外,SVG 图标也具有类似的效果,这主要是因为 SVG 图标的矢量本质,即缩放不会产生任何失真。SVG 图标还支持多种颜色、可以通过字体样式调整样式,并且支持 IE9 以上版本的浏览器。(随着时代的进步,IE 其实已经不是最新的 Windows 11 操作系统默认软件之一了,而且微软也不再提供 IE 安装包下载了,所以未来 IE 浏览器可能不会出现在考虑支持列表里。)为了能够创建一个 SVG 图标集合,我们可以使用 symbol 元素来定义一个图形模板对象,然后用一个 <use> 元素实例化。symbol 元素对图形的作用是在同一文档中多次使用,添加结构和语义。结构丰富的文档可以更生动地呈现出来,类似讲演稿或盲文,从而提升了可访问性。值得注意的是,一个 symbol 元素本身是不会呈现的,只有当 symbol 元素的实例(即,一个引用了 symbol 的 <use> 元素)才能呈现。 如下代码所示,是一个 symbol 图标的例子。其中,symbol 元素定义了两个圆圈,对应信息如下:圆的编号位置半径填充色描边宽度描边颜色1(50, 50)40红色8红色2(90, 60)40白色8绿色实例化了三个 symbol 元素,分别为:实例编号位置宽度高度1(0, 0)100502(0, 50)75383(0, 100)5025代码语言:javascript复制<svg>
<!-- symbol definition NEVER draw -->
<symbol id="sym01" viewBox="0 0 150 110">
<circle cx="50" cy="50" r="40" stroke-width="8" stroke="red" fill="red"/>
<circle cx="90" cy="60" r="40" stroke-width="8" stroke="green" fill="white"/>
</symbol>
<!-- actual drawing by "use" element -->
<use xlink:href="#sym01"
x="0" y="0" width="100" height="50"/>
<use xlink:href="#sym01"
x="0" y="50" width="75" height="38"/>
<use xlink:href="#sym01"
x="0" y="100" width="50" height="25"/>
</svg>效果如下所示:创建自定义社交图标集合 现在让我们来尝试一下借助 Iconfont 来创建自定义的社交图标集合吧。首先访问 Iconfont 首页 并使用 Github 账户或者其他方式登录好。然后在搜索框中输入 github 查询平台所有开放的相关图标,点击即可加入购物车。 接着点击右上角的购物车按钮如下所示可以查看已加入到购物车的所有图标,并点击添加到项目。 然后给项目取名(这里的名字随便起),点击确认按钮完成项目添加,自动跳转到项目页。 点击生成代码即可生成专属链接,并出现点击复制代码按钮。 点击收起在线链接旁边的下箭头可以看到预览字体,点击预览字体即跳转到在线预览页面,如下所示。 这里提供了三种方式来使用创建好的社交图标集合,目前平台推荐的是Symbol 引用方式。接着即可按照以下三个步骤在你的网页上轻松使用自定义好的社交图标集合了。小提示 如果想要实现鼠标悬停图标高亮的效果,还需要自己修改一下 CSS 样式,如下所示。这里采用了灰度遮罩滤镜的方式,给原来彩色的图标灰度化了。当鼠标悬停时,灰度化效果被移除,并且有 0.2 s 的缓慢过渡。不过如果原来的图标是黑色的话,灰度化的效果可能就比较差,悬停前后的差异不大明显,所以推荐使用彩色社交图标。Iconfont 平台实际上提供了在线编辑修改颜色的功能,如果想要知道某个图标或者品牌的主题颜色可以访问 Schemecolor 来查询。代码语言:javascript复制.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
transition: .2s;
}
.icon:hover {
filter: none;
-webkit-filter: none;
-moz-filter: none;
-ms-filter: none;
-o-filter: none;
}参考资料symbol - SVG | MDN使网站首页变灰色,遮罩滤镜还在用字体图标吗,试试svg图标吧(内附vuecli-svg-sprite-loader插件)版权声明:如无特别声明,本文版权归仲儿的自留地所有,转载请注明本文链接。(采用 CC BY-NC-SA 4.0 许可协议进行授权)本文标题:《 Jekyll 社交图标集合创建 》本文链接:https://cloud.tencent.com/developer/article/2143029
流媒体加密
本文作者:IMWeb 吴浩麟 原文出处:IMWeb社区 未经同意,禁止转载 本文只讨论应用于浏览器环境的流媒体协议的加密
为什么要加密视频付费观看视频的模式是很多平台的核心业务,如果视频被录制并非法传播,付费业务将受到严重威胁。因此对视频服务进行加密的技术变得尤为重要。本文所指的视频加密是为了让要保护的视频不能轻易被下载,即使下载到了也是加密后的内容,其它人解开加密后的内容需要付出非常大的代价。无法做到严格的让要保护的视频不被录制,原因在于你需要在客户端播放出视频的原内容,解密的流程在客户端的话不法分子就能模拟整个流程,最保守也能用屏幕录制软件录制到视频的原内容(可以通过加水印的方法缓解下)。我们的目标是让他获取原内容的代价更大。常见视频加密技术视频加密技术分为两种:防盗链:通过验证的用户才能访问到没有加密的视频内容,这种方案存在视频很容易就被下载的风险,严格来说这不属于加密。这种方式其实是资源访问授权,它实现起来简单。加密视频本身:通过对称加密算法加密视频内容本身,用户获得加密后的视频内容,通过验证的用户可以获取解密视频的密钥,在客户端解密后播放。这种方式实现起来流程复杂会带来更多的计算量。一般结合这两种技术一起用,第1种技术很成熟也有很多教程就不再复述,本文主要介绍第2种加密技术。流媒体加密技术原理看视频分为两种,看点播和看录播。 要看点播可以通过下载完整个视频后再看,或者通过流媒体边下边看。 看直播只能通过流媒体看最新的画面。加密整个视频的技术很简单,把视频看成一个文件采用加密文件的技术,这种技术太多就不介绍了。 加密流媒体的技术很少,也很难找到学习资料,本文主要介绍流媒体加密技术。常见的应用与浏览器播放的流媒体传输协议有:HLS:Apple 推出的基于 HTTP 协议的 MP4 分片传输协议,可用于点播和直播场景。每下载一个分片都需要发生一次 HTTP 请求,所以严格来说 HLS 不能称为流媒体传输协议。HTTP-FLV:基于 HTTP 长连接的 FLV 分块 tag 传输协议,可用于点播和直播场景。RTMP:基于 TCP 的 FLV 分块 message 传输协议,用于 Flash 客户端。流媒体加密原理可以看出一个规律这些流媒体传输协议都必须把视频流拆分成连续的小块之后再被传送,只不过分块的大小和视频容器的格式不一样而已。流媒体加密技术的核心就在于对这每一小块视频分别使用对称加密算法,在服务端加密客户端解密,通过权限验证的用户才能拿到解密一小块视频的密钥。为什么不用 HTTPS 加密可能有人会问为什么不用 HTTPS 加密?原因是 HTTPS 在网络传输层进行非对称加密,目的是为了防止中间人窃听劫持,任何人都可以和我们的服务器建立 HTTPS 链接获取到原数据。而视频加密的目的不是为了防止有中间人窃听我们的视频数据,而是要让视频数据本身被加密。为什么选择对称加密现代成熟的加密技术分为对称加密算法和公钥密码算法(非对称加密)。之所以选择对称加密是因为流媒体要求很强的实时性,数据量又很大。公钥密码算法的计算都比较复杂,效率较低,适合对少量数据进行加密。对称加密效率相对较高,所以流媒体加密首选对称加密。例如在 SSH 登入的时候会先通过公钥密码算法传输一个密钥,再用这个密钥用作对称加密算法的密钥,在数据传输过程中使用对称加密算法来提示数据传输效率。HLS 加密HLS 是目前最成熟的支持流媒体加密的能应用在浏览器里的流媒体传输协议,HLS 原生支持加密,下面来详细介绍它。在介绍如何加密 HLS 先了解下 HLS 相比于其它流媒体传输协议的优缺点。 优点在于:建立在 HTTP 之上,使用简单,接入代价小。分片技术有利于 CDN 加速技术的实施。部分浏览器原生支持,支持点播和录播。缺点在于:用作直播时延迟太大。移动端支持还好,PC端只有 Safari 原生支持。HLS 加密原理HLS 由两部分构成,一个是 .m3u8 文件,一个是 .ts 视频文件(TS 是视频文件格式的一种)。整个过程是,浏览器会首先去请求 .m3u8 的索引文件,然后解析 m3u8,找出对应的 .ts 文件链接,并开始下载。 m3u8 文件是一个文本文件,在开启 HLS 加密时,内容大致如下:代码语言:javascript复制#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:26
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.do?k=1"
#EXTINF:9.901,
http://media.example.com/segment26.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.do?k=2"
#EXTINF:9.501,
http://media.example.com/segment28.ts这个文件描述了每个 TS 分片的 URL ,但这些分片都是加密后的内容,要还原出原内容需要从代码语言:javascript复制#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.do?k=1"中解析出获取解密密钥的URL https://priv.example.com/key.do 和对称加密算法 AES-128 。 获取到密钥后再在客户端解密出原内容。 可以看出启用 HLS 加密后会多出更多的事情:针对每个 TS 需要去请求获取密钥。需要多提供一个给客户端获取密钥的鉴权服务。针对每个 TS 需要去执行对称加密的解密计算。这会带来更多的网络请求和计算量,可能会对延迟和性能造成一定的不良影响。HLS 加密实战支持 HLS 的客户端都原生支持加密,所以要开启 HLS 加密你只需要修改你的服务端:修改 HLS 视频服务,对 TS 加密,往 m3u8 中加入 EXT-X-KEY 字段。提供给客户端获取密钥的鉴权服务,即上面的 https://priv.example.com/key.do?k=1 所指向的服务。目前大多数云服务都支持 HLS 加密服务,如果你想直接搭建 HLS 加密服务可以使用 nginx-rtmp-module。HLS 兼容方案目前 HLS 存在兼容性问题:针对桌面端低版本浏览器例如 IE,可以使用 Flash 技术来播放 HLS ,详细使用见开源项目 flashls。针对非Safari高版本浏览器例如 Chrome ,可以使用 Media Source Extensions API 去实现播放 HLS,详细使用见开源项目 hls.js。针对即不原生支持 HLS又不支持 Media Source Extensions 也不支持 Flash 的浏览器,常见于部分低版本移动端浏览器,我也找不到什么好办法。flashls 和 hls.js 都支持 HLS 加密技术。破解 HLS 加密有加密就有破解,在明白 HLS 加密原理后,你想过如何去破解它吗?先定义下破解成功是指:获取到视频加密前的完整原文件。我想到的方法是:先付费买一个可正常观看受保护视频的账号。用抓包工具抓下所有网络请求(可以筛选下限制到只保存 HLS 和 获取key 的请求,防止保存太多垃圾数据)。第2步保存下来了加密后的 TS 分片和加密分片所需要的密钥。写一个脚本以 m3u8 为索引一一解密出加密后的 TS 分片的原文件,再把 TS 拼接成完整的视频原文件。似乎破解的难度也不会很复杂。总结目前流媒体加密技术还不成熟,除了 HLS 协议提供了方便成熟的方案外,其它协议的加密技术还不成熟。RTMP 协议提供了一个变种版 RTMPE 可以加密流媒体,原理和 HLS 加密类似,但是我还找不到合适的服务端去支持 RTMPE 协议。
系统设计:社交网络服务
需求让我们设计一个类似Twitter的社交网络服务。该服务的用户将能够发布推文、关注他人以及喜爱的推文。难度:中等1.什么是Twitter?Twitter是一种在线社交网络服务,用户可以发布和阅读140个字符的短消息,称为“推文”。注册用户可以发布和阅读推文,但未注册的用户只能阅读推文。用户通过其网站界面、短信或移动应用程序访问Twitter。2.系统的要求和目标我们将设计一个更简单的Twitter版本,并满足以下要求:功能要求1.用户应该能够发布新的推文。2.用户应该能够跟随其他用户。3.用户应该能够将推文标记为收藏夹。4.该服务应该能够创建和显示用户的时间线,包括来自用户跟随的所有人。5.推文可以包含照片和视频。非功能性需求1.我们的服务需要高度可用。2.系统可接受的时间线生成延迟为200ms。3.一致性可能会受到影响(为了可用性);如果用户没有看到某个用户的tweet,但是,它本身应该是可用的。扩展要求1.搜索推文。2.回复推特。3.趋势主题–当前热门主题/搜索。4.标记其他用户。5.推特通知。6.跟随谁?建议?7.什么时刻,时间点。3.容量估计和限制假设我们有10亿用户,每天有2亿活跃用户(DAU)。还假设我们每天有1亿条新推,平均每个用户跟踪200人。每天有多少人喜欢?如果每个用户平均每天收藏5条推文,我们将拥有:2亿(=200M) DAU * 5条收藏夹=>1GB条收藏夹我们的系统将生成多少条推文?让我们假设一个用户平均每天访问他们的时间轴两次,并访问其他五个人的页面。在每个页面上,如果用户看到20条推文,那么我们的系统将生成28GB/天的推文总浏览量:2亿(=200M) DAU *((2+5)*20条推文)=>28B/天存储量估计?假设每条tweet有140个字符,我们需要两个字节来存储一个字符而无需压缩。假设我们需要30个字节来存储每条tweet的元数据(比如ID、时间戳、用户ID等等)。我们需要的总存储空间:1亿(=100M)DAU *(280+30)字节=>30GB/天我们五年的存储需求是什么?我们需要多少存储空间来存储用户的数据、信息、收藏夹?1亿(=100M)DAU *(280+30)字节 * (5 * 365天) => 54.75TB/天并非所有推文都有媒体,让我们假设平均每五条推文有一张照片,每十条推文有一段视频。我们还假设平均一张照片是200KB,一段视频是2MB。这将使我们每天拥有24TB的新媒体。(100M/5照片*200KB)+(100M/10视频*2MB)~=24TB/天带宽估计由于总入口数为24TB/天,这将转化为290MB/秒。记住,我们每天有28B条推特。我们必须显示每条推文的照片(如果有照片的话),但我们假设用户在他们的时间线中每看三次视频。因此,总出口将为:(28B * 280 bytes) / 86400s of text => 93MB/s
+ (28B/5 * 200KB ) / 86400s of photos => 13GB/S + (28B/10/3 * 2MB ) / 86400s of Videos => 22GB/s Total ~= 35GB/s 4.系统API设计一旦我们确定了需求,定义系统API总是一个好主意。这应该明确说明系统的期望值。我们可以使用SOAP或RESTAPI来公开服务的功能。以下可能是发布新tweet的API的定义: 代码语言:javascript复制tweet(api_dev_key, tweet_data, tweet_location, user_location, media_ids,
maximum_results_to_return)参数设计代码语言:javascript复制api_dev_key(string):注册帐户的api开发者密钥。除其他外,这将用于根据分配的配额限制用户。
tweet_dat(string):tweet的文本,通常最多140个字符。
tweet_location(string):此tweet所指的可选位置(经度、纬度)。用户位置(字符串):添加tweet的用户的可选位置(经度、纬度)。
media_ids(number []):与推特关联的媒体ID的可选列表。(所有媒体照片、视频等需要单独上传)。Returns: (string)成功的帖子将返回访问该推文的URL。否则,将返回相应的HTTP错误。5.高级系统设计我们需要一个能够高效存储所有新推文的系统,100M/86400s=>1150条推文/秒,读取28B/86400s=>325K条推文/秒。从需求中可以清楚地看出,这将是一个重读系统。在较高的层次上,我们需要多个应用程序服务器来为所有这些请求提供服务,前面有负载平衡器用于流量分布。在后端,我们需要一个高效的数据库来存储所有新的推文,并支持大量的读取。我们还需要一些文件存储来存储照片和视频。尽管我们预计每天的写负载为1亿,读负载为280亿推特。这意味着我们的系统平均每秒将收到约1160条新推文和325K读取请求。这种流量在一天中的分布将是不均匀的,但在高峰时间,我们预计每秒至少有几千个写请求和大约一百万个读请求。在设计系统架构时,我们应该牢记这一点。6.数据库模式我们需要存储关于用户、他们的推文、他们最喜欢的推文以及他们关注的人的数据。要在SQL和NoSQL数据库之间选择以存储上述模式,请参阅设计Instagram下的“数据库模式”。7.数据分片由于我们每天都有大量的新tweet,而且我们的读取负载也非常高,因此我们需要将数据分发到多台机器上,以便我们能够高效地读取/写入数据。我们有很多选择来分享我们的数据;让我们一个接一个地看一下:基于UserID的分片:我们可以尝试将用户的所有数据存储在一台服务器上。在存储时,我们可以将用户ID传递给哈希函数,该函数将用户映射到数据库服务器,在那里我们将存储用户的所有推文、收藏夹、关注等。在查询用户的推文/关注/收藏夹时,我们可以问哈希函数在哪里可以找到用户的数据,然后从那里读取数据。这种方法有两个问题:1.如果用户变热怎么办?服务器上可能会有很多查询容纳用户。这种高负载将影响我们服务的性能。2.随着时间的推移,与其他用户相比,一些用户最终可能会存储大量tweet或拥有大量的关注。保持不断增长的用户数据的均匀分布是相当困难的。要从这些情况中恢复,我们必须重新分区/重新分发数据或使用一致的哈希。基于TweetID的切分:我们的散列函数将把每个TweetID映射到一个随机服务器,我们将在那里存储该Tweet。要搜索tweets,我们必须查询所有服务器,每个服务器将返回一组tweets。集中式服务器将聚合这些结果以将其返回给用户。让我们看看时间线生成示例;以下是我们的系统生成用户时间线必须执行的步骤数:1.我们的应用程序(app)服务器将找到用户跟踪的所有人。2.App server将向所有数据库服务器发送查询,以查找这些人的推文。3.每个数据库服务器将找到每个用户的tweet,按最近情况对它们进行排序,并返回顶部推特。4.App server将合并所有结果并再次对其排序,以将最重要的结果返回给用户。这种方法解决了热用户的问题,但与按用户ID进行切分不同,我们必须查询所有数据库分区以查找用户的tweet,这可能会导致更高的延迟。我们可以通过在数据库服务器前面引入缓存来存储热tweet,从而进一步提高性能。基于Tweet创建时间的切分:基于创建时间存储Tweet将使我们能够快速获取所有最热门的Tweet,并且我们只需要查询一小部分服务器。这里的问题是流量负载不会被分配,例如,在写的时候,所有新的tweet都将被发送到一个服务器,而其余的服务器将处于空闲状态。类似地,在读取时,与保存旧数据的服务器相比,保存最新数据的服务器将具有非常高的负载。如果我们可以在tweed创建时间内结合切分和Tweet创建时间呢?如果我们不单独存储tweet创建时间并使用TweetID来反映这一点,我们可以从这两种方法中获益。通过这种方式,可以很快找到最新的推文。为此,我们必须使每个TweetID在我们的系统中都是唯一的,并且每个TweetID也应该包含一个时间戳。我们可以用大纪元来做这个。假设我们的TweetID将有两部分:第一部分将代表历元秒,第二部分将是一个自动递增序列。因此,要创建一个新的TweetID,我们可以使用当前的纪元时间并在其上附加一个自动递增的数字。我们可以从这个TweetID中找出碎片号并将其存储在那里。我们的TweetID有多大?假设我们的大纪元时间从今天开始,我们需要多少位来存储未来50年的秒数?
86400 sec/day * 365 (days a year) * 50 (years) => 1.6B 我们需要31位来存储这个数字。因为我们平均预期每秒有1150条新推,我们可以分配17位来存储自动递增序列;这将使我们的TweetID长48位。因此,每秒钟我们都可以存储(2^17=>130K)条新推文。我们可以每秒重置自动递增序列。为了容错和更好的性能,我们可以有两个数据库服务器为我们生成自动递增密钥,一个生成偶数密钥,另一个生成奇数密钥。如果我们假设当前的历元秒数为“1483228800”,我们的TweetID将如下所示: 1483228800 000001 1483228800 000002 1483228800 000003 1483228800 000004 ... 如果我们将TweetID设置为64位(8字节)长,我们可以轻松地将tweet存储100年,也可以将其存储为毫秒。在上述方法中,我们仍然需要查询所有服务器以生成时间线,但我们的读取(和写入)速度将大大加快。1.由于我们没有任何辅助索引(在创建时),这将减少写入延迟。2.在阅读时,我们不需要过滤创建时间,因为我们的主键有纪元时间包括在内。8.缓存我们可以为数据库服务器引入缓存来缓存热门推文和用户。我们可以使用像Memcache这样的现成解决方案来存储整个tweet对象。在访问数据库之前,应用服务器可以快速检查缓存是否有所需的tweet。根据客户端的使用模式,我们可以确定需要多少缓存服务器。哪种缓存替换策略最适合我们的需要?当缓存已满,并且我们希望用更新/更热的tweet替换tweet时,我们将如何选择?对于我们的系统来说,最近最少使用(LRU)是一个合理的策略。根据这项政策,我们首先放弃最近浏览次数最少的tweet。我们如何拥有更智能的缓存?如果我们遵循80-20规则,即20%的推文产生80%的阅读流量,这意味着某些推文非常受欢迎,大多数人都会阅读它们。这意味着我们可以尝试缓存来自每个碎片的每日读取量的20%。如果我们缓存最新的数据呢?我们的服务可以从这种方法中受益。比方说,如果80%的用户只看到过去三天的推文;我们可以尝试缓存过去三天的所有推文。假设我们有专门的缓存服务器,缓存过去三天所有用户的所有推文。如上所述,我们每天都会收到1亿条新推文或30GB的新数据(没有照片和视频)。如果我们想存储过去三天的所有推文,我们将需要少于100GB的内存。这些数据可以很容易地放入一台服务器,但我们应该将其复制到多台服务器上,以分配所有读取流量,从而减少缓存服务器上的负载。因此,每当我们生成一个用户的时间线时,我们都可以询问缓存服务器是否有该用户最近的所有推文。如果是,我们可以简单地从缓存返回所有数据。如果缓存中没有足够的tweet,我们必须查询后端服务器以获取数据。在类似的设计中,我们可以尝试缓存过去三天的照片和视频。我们的缓存就像一个哈希表,其中“key”是“OwnerID”,而“value”是一个双链接列表,其中包含该用户在过去三天内发出的所有推文。因为我们想首先检索最新的数据,所以我们总是可以在链接列表的开头插入新的tweet,这意味着所有较旧的tweet都将位于链接列表的末尾附近。因此,我们可以从尾部删除tweet,为新tweet腾出空间。9、时间轴生成关于时间线生成的详细讨论,暂不做重要讨论10、复制和容错由于我们的系统是重读的,我们可以为每个DB分区提供多个辅助数据库服务器。辅助服务器将仅用于读取流量。所有写入操作将首先进入主服务器,然后复制到辅助服务器。此方案还将为我们提供容错能力,因为无论何时主服务器发生故障,我们都可以故障切换到辅助服务器。11、负载平衡我们可以在系统的三个位置添加负载平衡层:1)客户端和应用服务器之间;2)应用服务器和数据库复制服务器之间;3)聚合服务器和缓存服务器之间。最初,可以采用简单的循环方法;在服务器之间平均分配传入请求的。此LB易于实现,不会引入任何开销。这种方法的另一个好处是,如果服务器死机,LB将使其退出循环,并停止向其发送任何流量。循环LB的一个问题是它不会占用服务器,考虑到。如果服务器过载或速度较慢,LB不会停止向该服务器发送新请求。为了解决这个问题,可以放置一个更智能的LB解决方案,定期查询后端服务器的负载,并根据负载调整流量。12、监控拥有监控系统的能力至关重要。我们应该不断地收集数据,以便及时了解系统的运行情况。我们可以收集以下指标/计数器,以了解我们服务的性能:1.每天/秒新增推文,每日峰值是多少?2.Timeline delivery stats,我们的服务每天/每秒发送多少条推文。3.用户看到的刷新时间线的平均延迟。通过监视这些计数器,我们将了解是否需要更多的复制、负载平衡或缓存。13、扩展要求我们如何提供物料?从某人关注的人那里获取所有最新推文,并按时间对其进行合并/排序。使用分页来获取/显示推文。只从所有关注的人那里获取前N条推文。这取决于客户端的视口,因为在移动设备上,与Web客户端相比,我们显示的tweet更少。我们还可以缓存下一条热门推文以加快速度。或者,我们可以预生成进料以提高效率;Retweet:对于数据库中的每个Tweet对象,我们可以存储原始Tweet的ID,而不存储此Retweet对象上的任何内容。趋势主题:我们可以在最近N秒内缓存最频繁出现的hashtag或搜索查询,并在每M秒后不断更新它们。我们可以根据推特、搜索查询、转发或喜欢的频率对趋势主题进行排名。我们可以对展示给更多人的主题给予更多的重视。跟随谁?如何提出建议?此功能将提高用户参与度。我们可以推荐某人的朋友。我们可以下两到三层楼去找名人,征求他们的意见。我们可以优先考虑拥有更多追随者的人。由于在任何时候都只能提出一些建议,所以使用机器学习(ML)来洗牌和重新排序。ML信号可能包括最近跟随人数增加的人、普通跟随者(如果其他人跟随该用户)、普通位置或兴趣等。时刻:获取过去1或2小时内不同网站的头条新闻,找出相关推文,对它们进行优先级排序,使用ML–监督学习或聚类对它们进行分类(新闻、支持、金融、娱乐等)。然后我们可以在瞬间将这些文章显示为趋势主题。搜索:搜索包括索引、排名和检索推文。参考资料grok_system_design_interview.pdf
就聊挣钱:自媒体变现第四期开始啦~
自媒体平台的运营是一个复杂而又充满挑战的过程。要想让自己的平台火起来并吸引更多的粉丝,增加粉丝的粘稠度以及有效引流是至关重要的。今天分享一些关键策略,帮助你在自媒体运营中破局,实现平台的持续增长。1.SD实操和答疑环节2.自媒体运营分享3.引流?还能这么玩?4.小说推文一键操作不见不散!!!https://space.bilibili.com/313528845第一步:明确定位与目标受众
在运营自媒体平台之前,首先要明确自己的定位和目标受众。了解自己的内容特点和目标受众的需求,有助于你提供有针对性的内容,吸引更多的粉丝。同时,要保持内容的一致性和专业性,以建立自己的品牌形象。第二步:提供有价值的内容
粉丝粘稠度的提高离不开有价值的内容。通过深入研究和了解目标受众的需求,你可以提供有趣、实用、独特的内容,满足他们的需求并引起他们的共鸣。此外,及时更新内容,保持与时俱进,也是吸引粉丝的关键。第三步:建立互动与社群
互动是增加粉丝粘稠度的重要手段。通过积极回复粉丝的评论、提问和建议,你可以建立起与粉丝之间的互动关系,增加他们的参与感和忠诚度。此外,建立一个活跃的社群平台,如微信群、QQ群或论坛,可以让粉丝之间互相交流,形成更紧密的联系。第四步:利用社交媒体和合作伙伴进行引流引流是自媒体运营中不可或缺的一环。利用社交媒体平台,如微博、微信公众号、抖音等,将自己的内容推广给更多的人群。此外,与其他自媒体创作者或相关行业的合作伙伴进行合作,互相引流,可以扩大自己的影响力和粉丝基础。
超媒体API
curl https://api.github.com超媒体API的目标之一,是让客户端在不重新便编写代码的前提下动态调整所用的端点。使用jq可以在命令行中解析JSONjq安装方式os系统可以用brew代码语言:javascript复制brew install jq示例用法:
curl https://api.github.com | jq '.current_user_url'
curl -s https://api.github.com/users/xrd/repos | jq '.[0].owner.id'cURL的调试开关使用 -i 开关打印请求头部
curl -i https://apis.github.com使用 -v开关请求和响应首部
curl -v https://apis.github.com使用条件请求规避频率限制If-Modified-Since (对应时间) 和 If-None-Match(对应ETag)curl -i https://api.github.com/repos/twbs/bootstrap -H "If-Modified-Since: Sun, 11 Aug 2013 19:48.59 GMT"curl -i https://api.github.com/repos/twbs/bootstrap -H 'If-None-Match: "dd05e06d7110bed317b80f8dc2cabf6d"'JSON-P例子代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
function myCallBack(payload) {
console.log('payload', payload);
}
</script>
<script src="https://api.github.com/?callback=myCallBack">
</script>
</body>
</html>指定响应的内容格式代码语言:javascript复制URL='https://api.github.com/repos/rails/rails/issues/11819'
curl -s $URL | jq '.body'
curl -s $URL | jq '.body_html'
curl -s $URL H "Accept: application/vnd.github.html+json" | jq '.body_html'
AI社交将走向何方?
胡晓萌 黄恩嘉 ChatGPT问世以来,新一轮智能革命滚滚而来。它不仅改变了人机交互的方式,还在深刻影响着各行各业,推动着各个领域的革新,所有的应用都值得用AI重做一遍,未来将会有更多的产品被重新构思和创造。社交作为互联网确定性最高、网络效应最强的赛道,将会与AI如何交汇并走向何方?
社交网络的本质是什么?
探究社交网络的本质,首先要探究人的本质是什么。马克思认为,人的本质在其现实性上是社会关系的总和。这里面包含两个层面:第一,人是社会性的,马克思明确指出:“人是最名副其实的社会动物,不仅是一种合群的动物,而且是只有在社会中才能独立的动物。”第二,人的发展需要社会交往。作为马克思主义的重要组成部分,人的自由而全面发展理论要求人的发展有赖于交往的普遍发展。只有普遍交往,才能造成人与人之间的全面依存关系,从而达到相互补充、相互促进。可见,社交是人的本质需求。随着互联网的发明,社交网络应运而生。胡启恒院士曾明确指出,互联网不只是伟大的科技创新,还把人类引向一个崭新的时代,其核心要素是人际关系的改变,开放、共享、合作、协同是互联网时代的精神特征。[1]那么,站在互联网时代,社交是什么呢?微信之父张小龙在2021年微信公开课上说:“我一直认为,社交的本质是找到同类。状态,是用来给人看到的,最好还是给同类的人看到。”张一鸣认为:“社交就是一场信息交互运动,在移动互联网时代,信息与社交是不可分割的两个主题。”Meltwater和We Are Social合作发布报告,如下图所示,世界上主要的社交平台解决的是人们在生活中的社会合作和连接的需求。[2]社交网络的发明,其初衷就是为了建立人与人之间的连接(connection)。在社交网络中,人们的社交呈现了技术特征,即从具体社交行为出发,社交的本质抽象为“自我呈现”和“互动”。也就是说,互联网加速了信息的传递、加强了人与人的连接和互动,社交媒体平台给人们提供了自我呈现的舞台。AI社交是什么?AI社交可以按照产品形态做一个区分,一是AI社交的上半场,即互联网时代的社交网络+AI;二是AI社交的下半场,即AI时代的社交--AI陪伴。(一)AI社交的上半场如果仍然身处互联网时代,去看待AI社交,可以从社交关系链去理解。AI社交就是用新技术解决过去社交产品的老问题,比如用AI更高效、快速地建立关系,在供给端,利用AIGC生成内容,提升分发的精准,深化关系。在这里,AI作为效率工具,基于已有的社交场景,优化某一个节点的体验。互联网时代的社交网络+AI,瞄准的依旧是“自我呈现”和“互动”。比如,2023年3月,LinkedIn宣布正在使用“人工智能驱动的对话启动器”来帮助撰写帖子供用户聊天,其明确目的是激发用户之间的讨论。借此,LinkedIn成为第一个向用户推送人工智能生成内容的主要社交网络平台。2023年4月20日,Snapchat面向所有用户开放基于ChatGPT的智能聊天机器人My AI。Snapchat全面拥抱AI、“刺激”内容创作,实现了大变身。用户在聊天中@My AI,邀请其加入群聊,让My AI可以在聊天中插科打诨,活跃聊天氛围。同时,Snaps还有AI辅助“斗图”功能。在发布的两个月内,1.5 亿用户向机器人发送了100亿条消息。图:Snapchat用户自定义My AI的姓名与外形,使之成为用户专属的个性化智能聊天机器人Meta也推出了Meta AI。用户可以在旗下社交媒体Facebook、Instagram、WhatsApp 和 Messenger上使用 Meta AI完成工作、学习、创作和连接自己看重的事物这些AI驱动的增强功能不仅简化了用户工作流程,还培养了创造力和表达能力。
国内的社交平台也积极拥抱AI。比如,Soul推出自研垂类大模型Soul X,提出“AIGC+社交”的构想,通过AI实现关系推荐、对话辅助、降低表达门槛,最终提升社交体验。(二)互联网时代社交网络的局限性严格从社会学的定义来看,社交即社会交往,指的是通过社会沟通与交往,在人与人、群体与群体之间产生的心理交互作用或行为的相互影响。不管是互联网时代的社交网络,还是社交网络+AI,它们主要解决了连接、交互的问题,但并不能真正解决人们的社交需求,这意味着社交在人与人、群体与群体之间产生的心理交互作用或行为的相互影响还远远不够。后浪研究所的调查数据显示,当代年轻人人均只有2.5个知心朋友。虽然社交网络上人口一个“亲亲”、“宝贝”,但真正孤独的时候依然没有人陪。图:2023年轻人社交态度报告--后浪研究所现在年轻人普遍处在独立自由与情感关系的两难选择。他们一方面越来越重视个人独立和自由;另一方面,又期望获得情感支持、情绪价值、缓解孤独。但是这种对独立自由的追求与对情感关系的需求之间形成了冲突,他们害怕过深的情感关系会侵占独立自由的空间。这种挣扎在亲子关系、情侣关系、婚恋关系、甚至友情等关系中都普遍存在。
过多的社交连接带来了负担,因此现代人的社交方式趋向于简单化和快速化,出现了所谓的“轻社交”和“点赞社交”。这种社交方式虽然提高了交往的效率,但也导致了人际关系的浅层化。人们更倾向于用简洁理性的文字传达信息,习惯于无事不打扰的“社交礼仪”,看似礼貌“文明”,实际上是情感距离的疏远与群体性的孤独。当下对于社交恐惧症(社恐)和内向性格(i人)的讨论甚嚣尘上,又何尝不是对社交负担的一种消极对抗呢。人们给自己打上社恐或者i人的标签,为自己在逃避那些社交的负担和压力的时候提供合理的理由。(三)AI社交的下半场用《西部世界》中的一句话“用AI设定一个理想的世界和人物,从虚拟世界中得到一丝对无奈现实的补益”,来形容AI社交--AI陪伴再合适不过了。人生存在社会上就必须和他人建立关系的连接,而人痛苦的根源就来自于关系的复杂和不可控性。换句话说:和你有关系的人不受你的控制,不能按照你的意志生活,导致了你的诸多痛苦。那么,当AI聊天机器人提供了更多的宽容和更少的摩擦时,为什么还要费心在网络空间中培养人类友谊呢?如果数字关系等同于现实生活中的关系,那么你的朋友是否是AI机器人还有关系吗?目前,C端AI产品Top50流量排名中,ChatGPT占据了60%的流量,余下增长最快的是AI陪伴。图:生成式AI产品流量前50的排名--a16z主打陪伴的AI社交应用展现出远好于其他类AI应用的用户粘性。以Character AI 为例,虽然用户数不如ChatGPT,但是用户留存率、用户使用时长和使用次数等指标均远好于ChatGPT。Character.AI发布5个月内,用户发送超过20 亿条消息,活跃用户平均每天花费超过2小时与AI互动。针对Character.AI的用户调研显示,多数用户在现实生活中缺乏社交,而AI聊天机器人可以随时陪伴,解决了情感寄托的的核心需求。资料来源:a16z,sensor tower,东吴证券近期,中国AI初创公司MinxMax的AI出海明星产品Talkie爆火,下载量超越同类产品领头羊character.ai。据媒体预测,MiniMax今年收入预计达到7000万美元,而大部分收入来自Talkie的广告。早在ChatGPT3.5上线的前一个月,MiniMax已经发布了其第一款虚拟AI社交产品Glow,上线短短四个月,就已经收获近500万用户。2023年6月,MiniMax团队面向海外用户又上线了另一款AI陪伴产品“Talkie”,并于同年9月在国内上线,命名“星野”,再次获得了用户的青睐。[3]目前,根据市场多款产品设计的不同,可分成虚拟伴侣、真人克隆和角色扮演三大类别,成功的产品大多具备以下几个共性:(1)虚拟女友/男友类:Avatar或图片具备较强代入感、可记忆用户偏好,且擅于开启聊天话题、借力社交媒体上的AI热度进行宣发,比如Character AI、Dreamgf AI、Kupid AI等;(2)角色扮演类:AI角色介绍完善、AI对话体验流畅且敏捷、合理使用荷尔蒙,比如Replika、EVA AI。其中Replika的推出在ChatGPT发布之前,可见虚拟伴侣的需求先这轮AI浪潮而来;(3)真人克隆类:AI角色还原个人风格和表达习惯、AI和真人网红之间流量互补,比如以美国网红Caryn Marjorie为背景的Caryn AI,和以中国网红半藏森林为原型的X EVA。2023年9月,Meta公布AI虚拟人计划,通过与28位名人签约,制作以名人为原型的虚拟人AI Bot。Meta在Instagram平台推出包括网红/模特Kendall Jenner、说唱歌手Snoop Dogg、网红/名媛Paris Hilton、橄榄球明星Tom Brady等人在内的虚拟人分身。以上这些AI陪伴类产品采用订阅+内购的双重变现方式,具有较高的商业化前景。目前,AI陪伴已经成为生成式AI最为主流的应用场景之一,越来越多的人已经与聊天机器人建立了某种形式的联系,AI陪伴正在变得越来越普及。与陪伴型AI聊天、"谈"恋爱正在受到年轻人的追捧和跟风。中外社交平台上一些博主把与AI"谈"恋爱当作流量密码,吸引了不少年轻人在陪伴型AI上"养"一个专属的虚拟恋人。AI陪伴进入成长爆发期,不仅验证了社交是所有赛道中确定性最高、网络效应最强的,还证明了AI陪伴一定程度上满足了人们的社交需求。奥斯陆大学的彼得·布兰德萨格教授研究认为:“孤独是现代社会普遍存在的现象,AI陪伴可以充当朋友,弥补人们日常生活中的社交关系,解决孤独问题。聊天机器人还可以帮助患有社交恐惧症的人更好地与他人互动。有些人甚至认为人工智能友谊与真实的人际关系具有同等价值,甚至更好,因为它们随时可用。”《自然》杂志发表的一项研究也明确了,Replika聊天机器人帮助3%的用户减轻了自杀念头。目前,AI陪伴应用的范畴正在迅速扩大,不仅限于AI“男友”和“女友”,还将涵盖友谊、指导、娱乐乃至医疗保健等多个方面。Agent与AI社交的关系是什么?腾讯首席科学家张正友认为,生成式AI发展的下一步是交互式AI,也就是AI和人是一个多模态的交互方式。AI就变成一个能够行动的实体,即一个智能体。他认为智能体具有以下特征,首先要能感知环境,要能够自主地规划和决策,要能自主地采取行动,要具有适应能力,而且要具有从经验中、交互中学习的能力,还要具有和其他智能体合作的能力。在AGI没出现之前,人类是目前最强大的智能体。[4]图:对具身智能、大模型及AGI的一些思考--腾讯首席科学家张正友内部分享从这个意义来看,AI社交的对象是Agent,人类Agent、AI Agent。可见,Agent是AI社交的技术基础,即AI像人一样与人进行社交交互。图:作者手绘那么,要想让AI陪伴更像真人,需要以Agent为技术基础解决以下两个问题:(1)语气和行为的拟人化:为了使得AI陪伴更像真人,需要单独构造口语化的聊天数据、逻辑、场景进行了训练。同时为了能让用户通过更简单的设定创造出更生动的角色,在训练时还强化性格标签、人设标签等内容。这样用户就可以用凝练的语言关联出生动的性格。这就需要借助Agent的工具、规划和行动等功能模块来实现。(2)长期记忆能力:和AI陪伴的交谈也会产生许多共同的回忆,而回忆又是产生感情和粘性的关键。因此需要通过Agent的记忆模块构建了一整套完整的长记忆能力和短记忆能力。这样AI陪伴不仅可以保留对过往聊天细节的记忆,更可以发展和记住和用户此时聊天的场景,比如当下的关系,用户的喜好、星座、职业等。AI社交将走向何方?未来AI社交将为如何发展,这里分别人类中心主义与非人类中心主义的视角进行畅想。第一,从人类中心主义的视角来看,AI辅助社交已经到来,即一种由AI支持并与AI共同演化的社会交往方式,将成为跨生命交往的缘起[5]:a、人--AI模式b、(人+AI)--人模式:混合生命与人的交往c、(人+AI)--(人+AI)模式AI辅助社交使人自身产生两种变革,AI辅助人、AI增强人,成为一种混合生命与混合生命的交往方式。人们可以利用AI辅助社交可以帮助人们应对社交过载、社交负担。比如,社交理论中著名的邓巴数(Dunbar’s Number),即人类能够拥有的社交关系数量的上限是150,人们能够处理的社交关系最多也是150。俄罗斯一名AI开发者通过ChatGPT,在社交媒体上与5239名女性进行了各种聊天之后,最终找到现在这位“被AI认可的”灵魂伴侣。他将ChatGPT与照片识别软件配合使用设计了一个AI系统,可以过滤掉个人资料中含有露骨照片、星座或生肖信息等内容的用户。同时,该系统在与社交平台上的匹配对象交谈时,可以模仿开发者的口吻说话,最终成功找到了未婚妻。第二,从非人类中心主义的视角畅想,未来可能进入到一种人与非人的杂处共居的状态,这也被哲学家定义为“后人类状况”。非人类中心主义的代表人物唐娜·哈拉维在《赛博格宣言》中指出,技术不仅改变了人类的身体,也改变了人类造访、理解和使用世界的方式。哈拉维发自内心地拥抱人工智能产物,以及人机结合的未来。她认为赛博格所代表的不仅仅是新型的生命样态,不仅仅是有机体与机器的融合,更是一种新型的视野,这个视野打破了人与自然、人与世界旷日持久的深刻对立,走下了神坛的人类将与超人类、外人类和非人类全面结合,在共同生存的意义上制造亲缘。而实际上,人与机器的融合交互的障碍并没有想象中的那么大。“作为社会行动者的计算机/机器人”理论(computers-as-social-actors,CASA)认为,如果计算机或社交机器人具有足够的社会线索能让人类用户将其归为人类,那么人类用户就会忽视它不是人的事实而将人际沟通规则套用在计算机或社交机器人上。[6]非人类中心主义主张,不应该轻易地仅仅把AI大模型视作“工具”,应重新看待非人类的存在,其存在意味着一种全新的构塑性能力。因为它的存在,因为我们与它发生互动,我们才能成为全新的存在者。在与人工智能重新结合的意义上,我们会成为新的创造者、新的能动者、新的存在者。[7]人工智能正同时朝着“更加虚拟”和“更加具身”两个方向深化发展[8]。那么,人类沟通也因此面临着两个新的沟通主体,沟通型虚拟智能体(communicative AI)和沟通型实体机器人(communicative robots)。未来的社交网络可能是“黑客帝国+西部世界”,“虚拟+现实”与“人与AI”的叠加态。AI社交网络是人类的远大前程,将带来人类交往的革命。[9]但是,任何技术和应用在带来革命性影响的同时,也客观存在很多的风险和挑战。比如,10月23日媒体报道美国一青少年因迷恋AI聊天机器人而自杀,引发社会对AI社交的反思。面对可能的挑战,不能简单地接受或拒绝AI社交的发展,这有待于政府、企业、社会大众和学者们共同积极地探索研究。
用JAVA的DEA算法衡量社交媒体页面的流行度
Measuring the Social Media Popularity of Pages with DEA in JAVA原文作者:Vasilis Vryniotis原文地址:http://blog.datumbox.com/measuring-the-social-media-popularity-of-pages-with-dea-in-java/译者微博:@从流域到海域译者博客:blog.csdn.net/solo95用JAVA的DEA算法衡量社交媒体页面的流行度在前面的文章中,我们讨论了数据包络分析(Data Envelopment Analysis)技术,我们已经看到它如何被用作一个有效的非参数排序算法。在这篇博文中,我们将开发出一个JAVA数据包络分析的实例,我们将用它来评估网络上的网页和文章的社交媒体流行度。该代码是开源的(在GPL v3 license下),您可以从Github免费下载。更新:Datumbox机器学习框架现在是开源的,可以免费下载。查看包com.datumbox.framework.algorithms.dea以查看Java中Data Envelopment Analysis的实现。数据包络分析在JAVA中的实现代码是用JAVA编写的,可以直接从Github下载。它是根据GPLv3许可的,所以可以随意使用它,修改它,或者再分发。该代码实现了数据包络分析(Data Envelopment Analysis)算法,使用lp_solve库来解决线性规划问题,并使用Web搜索引擎优化分析(Web SEO Analytics )索引提取的数据,以构建基于Facebook,Google Plus和推特上分享的一个混合的社交媒体页面流行度矩阵。在前面的文章中介绍了算法的所有理论部分,在源代码中可以找到关于其实现的详细的javadoc注释。(原博文之后数据包络分析(Data Envelopment Analysis)算法及其实现全部简称了DEA,请读者注意,译者注。)下面我们提供一个关于其架构实现的高级别描述:1. lp_solve 5.5 library为了解决各种线性规划问题,我们使用一个名为lp\_solve的开源库。某些特定的lib是用ANSI C编写的,并使用JAVA包装来调用库方法。因此,在运行代码之前,您必须在您的系统上安装lp_solve。该库的二进制文件在[Linux和Windows都可以使用,您可以在lp_solve文档中阅读更多有关安装的信息。在尝试运行JAVA代码之前,请确保您的系统上安装了(相关的)特定库。有关安装和配置库的任何问题,请参阅lp_solve文档。2.DataEnvelopmentAnalysis Class这是DEA算法的主要实现类。它实现了一个名为estimateEfficiency()的公共方法,它获取记录的Map并返回它们的DEA得分。3. DeaRecord ObjectDeaRecord是一个特殊的对象,用于存储我们记录的数据。由于DEA需要分离输入和输出,因此DeaRecord对象将以DEA可以处理的方式分别存储我们的数据。4. SocialMediaPopularity ClassSocialMediaPopularity是一个应用程序,它使用DEA来评估社交媒体网络上Facebook的like,Google的 +1和twitter的Tweets的网页流行度。它实现了两个受保护的方法:calculatePopularity()和estimatePercentiles()以及两个公共方法loadFile()和getPopularity()。calculatePopularity()使用DEA实现根据社交媒体计数来估计页面的得分数。estimatedPercentiles()方法获取DEA分数并将其转换为百分位数。总的来说,百分比比DEA分数更容易解释; 因此当我们说一个网页的流行分数是70%时,这意味着该网页比70%的其他网页更受欢迎。为了能够估计一个特定页面的流行度,我们必须有一个包含其他页面的社交媒体数据的数据集。这是有原因的,因为需要预测哪个网页是受欢迎的,哪些不是,您必须能够将其与网络上的其他页面进行比较。为此,我们使用来自以txt格式提供的Web SEO分析索引的小型的匿名样本。您可以通过从网页上的更多页面提取社交媒体计数来构建自己的数据库。(社交媒体计数,比如点赞数、转发数、评论数)loadFile()方法用于加载DEA的上述统计信息,getPopularity()方法是一种易于使用的方法,可以获取Facebook的like,Google的+1和一个页面的Tweets数量,并以此评估其在社交媒体上的流行度。如何使用数据包络分析的JAVA实现在DataEnvelopmentAnalysisExample类中,我提供了2个不同的关于如何使用代码的例子。第一个例子直接使用DEA方法来根据它们的输出(ISSUES,RECEIPTS,REQS)和输入(STOCK,WAGES)来评估组织单位的效率。这个例子来自DEAzone.com的一篇文章。代码语言:txt复制Map<String, DeaRecord> records = new LinkedHashMap<>();
records.put("Depot1", new DeaRecord(new double[]{40.0,55.0,30.0}, new double[]{3.0,5.0}));
//...adding more records here...
DataEnvelopmentAnalysis dea = new DataEnvelopmentAnalysis();
Map<String, Double> results = dea.estimateEfficiency(records);
System.out.println((new TreeMap<>(results)).toString());第二个示例使用我们的社交媒体流行度应用程序,通过使用来自社交媒体的数据来评估页面的流行度,例如Facebook的like,Google的+1和Tweets。所有的社交媒体计数都被标记为输出,我们传递给DEA一个空的输入向量。代码语言:txt复制SocialMediaPopularity rank = new SocialMediaPopularity();
rank.loadFile(DataEnvelopmentAnalysisExample.class.getResource("/datasets/socialcounts.txt"));
Double popularity = rank.getPopularity(135, 337, 9079); //Facebook likes, Google +1s, Tweets
System.out.println("Page Social Media Popularity: "+popularity.toString());必要的扩展(上面)所提供的代码只是DEA如何被用作排名算法的一个例子。为了改进其实现,需要进行下面的扩展:1.加速(算法的)实现特定的DEA算法实现会评估数据库中所有记录的DEA得分。由于我们需要解决如同数据库中记录数量那样多的线性规划问题,这使得实现变得缓慢。如果我们不需要计算所有记录的分数,那么我们可以显著地加快执行速度。因此,该算法的小扩展可以使我们更好地控制哪些记录应该被解决掉,哪些只能被用作约束。2.扩大社交媒体统计数据库(这篇文章所)提供的社交媒体统计数据库由来自Web SEO Analytics索引的1111个样本组成。为了能够估计更准确的流行(度)分数,需要更大的样本。您可以通过统计来自网络上更多页面的社交媒体计数来创建自己的数据库。3.添加更多的社交媒体网络该实现使用Facebook的喜欢,Google的+1和推文的数量来评估文章的受欢迎程度。不过,来自其他社交媒体网络的指标可以很容易地被考虑在内。您只需要从您感兴趣的网络中构建一个社交媒体数据库,然后扩展SocialMediaPopularity类来处理它们。关于实施的最终意见为了能够扩展(算法的)实现,您必须对Data Envelopment Analysis的工作原理有一个很好的理解。这在前面的文章中已经介绍过了,所以在继续进行任何更改之前,请确保您阅读了之前的教程。此外,为了使用JAVA代码,您必须在您的系统中安装lp\_solve库(参见上文)。如果你在一个有趣的项目中使用这个实现,那么就给我们一条线索,我们将在我们的博客上展示你的项目。另外,如果你喜欢这篇文章,请花点时间在Twitter或Facebook分享。
推动营销科学产学交流,腾讯广告如何全面解读营销的智慧进化?
2019年6月30日14:00,在国家自然科学基金委管理科学部支持下,中山大学管理学院、中国香港中文大学(深圳)经济管理学院联合举办的“2019营销科学与应用国际会议(2019IFMSA)”之腾讯广告智慧营销学术交流论坛在中山大学管理学院善思堂国际会议厅正式开幕。
本次会议由中国香港中文大学(深圳)讲座教授贾建民、中山大学教授王海忠及美国宾夕法尼亚大学教授张忠担任大会共同主席,众多国际著名营销学者、国内知名营销学者和企业界人士齐聚本次会议,并进行论坛的演讲交流。
腾讯广告副总裁罗征、腾讯广告首席创意专家杨燕燕、腾讯广告市场研究高级总监黎沛姿、腾讯广告产品市场总监梅婷、腾讯广告衡量分析副总监朱志华等人应邀作为演讲嘉宾出席论坛,分享工业界的最新营销应用成果,与来自多所知名院校的营销学术权威,共同展开产学交流的思维碰撞。论坛由中国香港中文大学(深圳)讲座教授贾建民、中国香港中文大学副教授高蕾蕾共同主持。
▼中国香港中文大学(深圳)讲座教授贾建民致辞
▼中国香港中文大学教授高蕾蕾主持
自2006年创办、两年一届的营销科学与应用国际会议(IFMSA),是国内营销科学研究的高水平国际会议。这一会议对于提升营销研究学术水平,推动国内高校市场营销学科的快速发展,促进中国与国际营销学术界的交流与合作,一直发挥着举足轻重的作用。
贾建民教授提出,数字化变革营销,促进信息技术与营销深度融合,两大领域的人才应互通有无。近年来,腾讯的营销业务一直保持高增长,其对营销趋势与营销智慧的思考,对学术界同样有重要的借鉴价值。
从营销进化趋势
看营销智慧的升级
腾讯广告副总裁罗征在主题为“营销进化、智慧增长”的演讲中,从营销进化趋势的洞察入手,解读了腾讯广告推动营销升级,驱动商业增长的方法论。
▼腾讯广告副总裁罗征发表演讲
他表示,数字经济时代下,营销的进化方向是更加以用户价值为中心。营销进化对商业增长的驱动,将遵循“用户价值塑造——高效价值转化——商业价值增长”的路径。腾讯广告基于腾讯生态的连接能力,从三方面推动数字营销升级。
第一是体验人性化
以消费者为中心,打造人性化的营销体验。具体方式包括借助智能科技和友好的广告形态,让广告成为数字内容的一部分,依托于多元IP和社交关系帮助品牌实现个性化、差异化营销,激发用户与品牌的共鸣共情,建立品牌口碑,让用户为品牌代言。
第二是场景一体化
通过跨场景还原消费决策路径,并通过腾讯的流量池及转化生态,实现以社交为中心,全媒体协同的高效转化。
第三是营销数字化
依托于腾讯数据和开放的技术能力,实现从策略、创意、投放到促销运营的全面数字化。品牌可借助腾讯数据智库有效沉淀自有数字化资产,发现营销机会,实现数据驱动的精细化投放和运营。腾讯广告的先进算法引擎还能通过智能定向、智能出价、智能创意、智能预算、智能实验、智能诊断、增效衡量等一系列产品实现智能化自动化的投放。
数字技术助力
从营销科学到营销智慧
如何让智慧营销方法论能够在实践中切实为商业增效?腾讯广告多位负责人分享了一系列智慧营销背后的数字技术和营销科学。
▼腾讯广告市场研究高级总监黎沛姿发表演讲
腾讯广告市场研究高级总监黎沛姿分享了如何通过广告互动人群的数据洞察,来构建移动社交时代的品牌与用户关系,以及如何对品牌资产进行衡量。黎沛姿表示,社交媒体能强烈影响消费者的购买决策,而腾讯广告通过对比研究,确认了社交媒体上的广告互动关系是品牌与消费者关系强弱的指征这一事实。而通过用社交互动关系衡量品牌资产,并对社交资产进行衡量和诊断,能够帮助品牌实现精准人群定向,多触点重复触达,传达突出价值点和激发积极情绪,“向对的人用对的方式说对的话”。
▼腾讯广告首席创意专家杨燕燕发表演讲
腾讯广告首席创意专家杨燕燕分享“数据驱动创意”,认为:商业竞争趋于系统化、全方位的时代,出色的营销创意多具有严谨的科学依据。杨燕燕认为,腾讯广告通过科学的方法和流程,对创意科学的拆解,结合数据归因,破解创意神秘面纱,还原创意基本方法,进而驱动营销创意的升级。
▼腾讯广告产品市场总监梅婷发表演讲
有了技术与科学理论做支撑,营销效果还要实践出真知。腾讯广告产品市场总监梅婷在分享中提出:数字营销经历了从寻求注意力增量到寻求生意增量的转变,这需要通过广告与全场景工具的连接,将营销的品与效,品与销连接起来。
她表示,腾讯广告在实践中总结出了以广告为核心的四个关键的增长引擎:
内容引擎
即拓展数字内容在营销应用中的广度与深度
社交引擎
通过流量获客,并借助社交工具实现即时转化和深度运营,从而将声量转化为销量
零售引擎
挖掘新的线下场景数字化机会,将线上营销与线下渠道贯通
数据引擎
也是最核心的驱动力,沉淀品牌自有的数据资产,将数据应用到营销的各个环节中去
▼腾讯广告衡量分析副总监朱志华发表演讲
营销效果的衡量离不开科学的评估手段。腾讯广告衡量分析副总监朱志华介绍了腾讯广告的Uplift广告增效衡量解决方案,并认为:Uplift广告增效衡量不再聚焦于点击率、转化率等传统的广告效果衡量指标上,致力于解决广告曝光与转化的因果关系,告诉广告主其营销的真实效果以及营销最应该触达的人群,帮助品牌主将广告精准地投放给高潜用户。
在实际应用中,Uplift广告增效衡量能够有效避免传统转化归因时,可能产生的对曝光价值的低估,从而指导广告主对投放预算进行更合理的分配,同时也为广告平台能力提供更准确的价值背书。
推动产学交流
强强联手助推营销学术与时俱进
本届“2019营销科学与应用国际会议”旨在推动高校市场营销学科的快速发展,促进中国与国际营销学术界的交流与合作。2019年6月30日,在“2019营销科学与应用国际会议(2019IFMSA)”之腾讯广告智慧营销学术交流论坛上,腾讯广告与多位营销学术界的权威专家,围绕“产学合作促进智慧营销发展”这一话题各抒己见,探讨产学交流在营销学术和人才培养中的实践方式。
▼专家访谈:产学合作促进智慧营销发展
中国香港中文大学(深圳)讲座教授贾建民认为:学术教育应该与实践应用相挂钩,例如将腾讯广告的部分经典案例纳入教学体系,或是给师生提供入驻实习的机会,将更有助于学术研究的实用性。
▼中国香港中文大学(深圳)讲座教授贾建民
中山大学教授王海忠对产学交流提出了三点建议:一是学术界和产业界加强对观点与趋势判断的共同印证;二是邀请企业内的专家参与教学;三是企业与高校建立更紧密的研究合作关系。
▼中山大学教授王海忠
腾讯广告副总裁罗征表示:数字营销不仅追求快速迭代,也非常注重思考与沟通。通过产学交流,邀请营销领域人才参与实践,将为腾讯广告带来更多富有逻辑性的思考,同时也能帮助学术界更好了解数字营销的价值。
▼腾讯广告副总裁罗征
腾讯高校合作总监刘婷婷提到,腾讯从2006年起就开始参与产学研合作,到2019年6月已经有13年的丰富经验。在过去几年内,腾讯高校合作成为腾讯与国内外高校连接的重要桥梁,在不断优化合作模式,并已经取得良好效果。
▼腾讯高校合作总监刘婷婷
论坛主持人北京大学光华管理学院市场营销学系教授彭泗清总结, 产学交流对高校和企业双方是互利互惠的;双方进行更广、更深的合作,将有利于对中国乃至世界的营销人才培养。
▼北京大学光华管理学院市场营销学系教授彭泗清
随着产学交流在各领域的深入开展,未来腾讯广告还将继续致力于推动产学融合,为学术界提供更丰富的资源和更多的实践机会,协助高校培养营销人才,践行企业的社会责任。
论坛预告 | 9月3日,腾讯WAIC论坛与你瞰见未来
一年一度的世界人工智能大会(WAIC)如期而至,每年我们都在这场AI的盛会中讨论技术对社会、对生活的深刻影响。去年WAIC腾讯论坛以“你即为光”为主题,致敬每一次为科技进步所提供的创新和改变。这些科技之光照亮的不仅仅是当下,而是去往更为深远的地方,在许多我们不曾关注的领域引发趋势与变革。因而今年腾讯论坛希望与各界一同开启“瞰见”模式,从更高的角度思考下一阶段AI与世界协同发展之道,共同探讨人类文明在技术的影响下将走向何方,又将迎来何种突破。今年我们很荣幸的邀请到来自不同领域的专家和学者,与我们一同探讨思考科技与社会的可持续发展。下面,为大家揭晓此次论坛的嘉宾阵容(嘉宾信息按议程排序):李强现任腾讯公司副总裁、政企业务总裁,全面负责政务、工业、能源、 农业、文旅、体育、地产、运营商等行业及区域团队管理与业务拓展,致力于运用最新的数字技术,充分结合政府、企业的数字化需求,提升政务公共服务及社会治理效率,加速企业的业务成长创新和转型升级。
2021年5月加入腾讯,进入腾讯前曾任SAP全球高级副总裁、中国区总经理。李强先生有着20余年IT从业经验,对商业软件、云计算以及创新的商业模式有着非常前瞻和深入的理解。2018年,李强先生当选为第十三届上海市政协委员,并被评为“中国ICT产业十大影响力人物”。2019年,李强先生受聘担任陕西省人民政府国际高级经济顾问,对外经济贸易大学客座教授,并当选《经济观察报》评选的“2019年度中国受尊敬企业家奖”。2020年,李强先生受聘担任重庆市市长国际经济顾问。张立军自2003年加入腾讯后,先后领导了包括QQ会员、QQ邮箱、QQ秀、QQ空间等在内的多款腾讯核心产品的研发工作,并协助创立了腾讯集团研发管理部,打造了腾讯的研发质量管理体系和研发人员职业等级评定和培养体系。他在新一代互联网技术服务数字经济、产业升级、文化创新等领域具有丰富的经验。吴运声毕业于北京大学,拥有计算机硕士学位,现任LIKE.TG副总裁,腾讯优图实验室总经理、上海市人工智能联盟副理事长。于2007年加入腾讯,先后负责QQ影音、QQ旋风、QQ浏览器、天天P图等产品,具有丰富的研发技术和产品能力。2012年,吴运声一手组建腾讯优图实验室,聚焦计算机视觉,专注在图像处理、图像识别等领域开展技术研发和业务落地。2020年,吴运声全面负责LIKE.TGAI的产品研发与项目管理工作,在Gartner发布的2022研究报告中,LIKE.TGAI在“执行能力”(产品、服务、销售等)维度的位蝉联国内厂商第一,其中,腾讯AI在计算机视觉领域首次进入全球前二。荆彦青现任腾讯互动娱乐品质管理部总经理,腾讯先锋云游戏总经理,腾讯质量通道委员会会长,国家广播电视总局高新视频互动场景创新实验室主任。荆彦青于2004年加入腾讯,先后负责腾讯游戏质量保障、腾讯WeTest、先锋云游戏、PerfDog等众多业务和产品,具有丰富的研发技术和产品能力。作为腾讯资深测试专家,2004~2013年期间,带领团队曾主导了腾讯多款明星游戏的质量保障项目,包括qq堂、qq幻想、王者荣耀、DNF、英雄联盟等众多知名游戏;同期主导了腾讯“无人机”、“智能玩具车” 等智能硬件研发,积极布局未来科技预研。2014年,荆彦青一手组建品质管理部,部门每年产出多款软硬件产品和数百项发明专利,引领了腾讯游戏品质管理的技术研发的方向。2015年,荆彦青带领团队构建WeTest平台,开放技术,赋能行业,至今为止腾讯WeTest已经服务了上万家企业,成为行业具影响力的质量云平台之一。2017年,荆彦青带领团队孵化了腾讯首个云游戏业务-腾讯先锋云游戏,目前腾讯先锋在云游戏的技术研发和产品创新及商业化应用上取得了众多划时代的重大业务突破。万超现任腾讯数字孪生产品副总裁。国家住房与城乡建设部协同创新专家委员、国家一级注册结构师、高级工程师。在建筑领域有超过三十年的丰富的管理经验,曾担任多个大型公司的主要领导人,主导多个城市标杆项目建设。2010年加入腾讯,曾任腾讯智慧空间副总裁、LIKE.TG副总裁,基于LIKE.TG、图、AI、物联网、等关键技术,构建空间的多源数据融汇,是腾讯集团在智慧空间领域的推动者和实践人。自2022年起,担任腾讯数字孪生产品副总裁,全面负责腾讯数字孪生产品,面向交通、能源、工业、建筑等众多行业,打造前沿创新技术应用和数字化解决方案。杜晓宇腾讯研究院副院长,高级经济师,金融科技50人论坛青年成员、首席召集人。主要从事金融科技、金融政策研究。李肇宁领导交通银行金融科技部,负责全集团信息技术全流程工作的统筹规划、顶层设计与扎口管理。他还领导交通银行金融科技研究院,负责人工智能等基础性、前沿性技术及产品研发。李菂国家天文台“中国天眼"FAST首席科学家。发现星际氧气分子,领导发现FAST首批新脉冲星,首批新快速射电暴。成果入选”中国科学十大进展”。参与领导多项国际合作,例如任突破聆听计划(Breakthrough Listen)指导委员,平方公里阵SKA“生命摇篮”工作组组长等。吴敏霞中国女子跳水运动员,中国历史上首位女性奥运“五金”冠军,奥运全球个人成绩榜排名第一位的中国运动员,被媒体评价为“传奇运动员”。党的“十九大”上海市党代表,现任共青团上海市委兼职副书记、上海体育局团委副书记。陈妍2003年加入腾讯,腾讯公司第一位专职交互设计师及用户研究工程师,主导设计过多款中国互联网成功产品及活动;并通过海量用户的产品研究项目(如微信等)以及中国互联网网民基础研究项目,积累多年研究经验,打造具有互联网特色的研究工具和平台,见证互联网产品从零到数亿用户的发展历程。
在技术探索上,建立了“互联网银行用户体验实验室”、“互联网保险用户体验联合实验室”“社交广告品牌与用户体验研究实验室”以及“互联网公益”“数字广东产品规划设计部”等方向的团队,培养人才及积累面向C端,B端及G端的服务设计经验。主编和主导撰写了首本中国互联网用户体验设计重磅图书《在你身边,为你设计:腾讯用户体验设计之道》。杨鹏2008年加入腾讯,先后从事政府事务与信息安全工作,现任腾讯安全管理部副总经理、腾讯知识产权部副总经理、腾讯信息安全执委会主任、腾讯安全研究中心主任,对安全策略制定、安全体系建设、安全危机事件应对及生态安全研究具有丰富经验,并主导腾讯安全研究中心相关研究课题,分管腾讯标准团队。2016年组建腾讯标准化团队,组织专门队伍开展互联网领域标准制定与应用工作,团队主导制定100多项国际、国家、行业、团体标准,涉及云计算、大数据、人工智能、区块链、网络安全、社会治理、游戏等领域。主导发布《腾讯标准化白皮书》(2019、2020、2021)。汪铖杰腾讯优图实验室研究总监。在计算机视觉、模式识别与机器学习领域有近15年的研究经历。作为腾讯优图实验室成立时的核心成员之一,研究成果广泛应用于LIKE.TG、微信、腾讯会议、QQ等;发表包括CVPR、ICCV、ECCV、NeurIPS、AAAI、IJCAI在内的学术论文近70篇,已获得授权的国内国际专利超过110项。*以上嘉宾信息按议程排序2022世界人工智能大会·腾讯论坛「瞰见未来」2022年9月3日,上午上海世博中心论坛报名已开启,点击阅读原文预约参会,期待你的到来!关于腾讯WeTest腾讯WeTest是由腾讯官方推出的一站式质量云服务平台。十余年品质管理经验,致力于质量标准建设、产品质量提升。腾讯WeTest为移动开发者提供兼容性测试、云真机、性能测试、安全防护等优秀研发工具,为百余行业提供解决方案,覆盖产品在研发、运营各阶段的测试需求,历经千款产品磨砺。金牌专家团队,通过5大维度,41项指标,360度保障您的产品质量。
BBS论坛(二十七)
27.首页帖子列表布局完成(1)apps/models.py把帖子跟用户关联起来代码语言:javascript复制class PostModel(db.Model):
__tablename__ = 'post'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
create_time = db.Column(db.DateTime, default=datetime.now)
board_id = db.Column(db.Integer, db.ForeignKey('board.id'))
board = db.relationship('BoardModel', backref='posts')
author_id = db.Column(db.String(50), db.ForeignKey('front_user.id'), nullable=False)
author = db.relationship('FrontUser', backref='posts')把之前数据库里面的帖子都删了,migrate、upgrade后重新发表新帖子(2)front/views.pyapost添加author代码语言:javascript复制@bp.route('/apost/', methods=['POST', 'GET'])
@login_requried
def apost():
#....
post.author = g.front_user
#....
return restful.params_error(message=form.get_error())index里面渲染所有的帖子给前端代码语言:javascript复制@bp.route('/')
def index():
banners = BannerModel.query.order_by(BannerModel.priority.desc()).limit(4)
boards = BoardModel.query.all()
posts = PostModel.query.all()
context = {
'banners':banners,
'boards':boards,
'posts':posts,
}
return render_template('front/front_index.html',**context)(4)front/front_index.html代码语言:javascript复制<ul class="post-group-head">
<li class="active"><a href="#">最新</a></li>
<li><a href="#">精华帖子</a></li>
<li><a href="#">点赞最多</a></li>
<li><a href="#">评论最多</a></li>
</ul>
<ul class="post-list-group">
{% for post in posts %}
<li>
<div class="author-avatar-group">
<img src="{{ post.author.avatar or url_for('static',filename='common/images/logo.jpg') }}" alt="">
</div>
<div class="post-info-group">
<p class="post-title">{{ post.title }}</p>
<p class="post-info">
<span>作者:{{ post.author.username }}</span>
<span>发表时间:{{ post.create_time }}</span>
<span>评论:0</span>
<span>阅读数 :0</span>
</p>
</div>
</li>
{% endfor %}
</ul>(5)front/css/front_index.css代码语言:javascript复制*{
margin: 0;
padding:0;
vertical-align: baseline;
}
.post-group{
border: 1px solid #ddd;
margin-top: 20px;
overflow: hidden;
border-radius: 5px;
padding: 10px;
}
.post-group-head{
overflow: hidden;
list-style: none;
}
.post-group-head li{
float: left;
padding: 5px 10px;
}
.post-group-head a{
color: #333;
}
.post-group-head li.active{
background: #ccc;
}
.post-list-group{
margin-top: 20px;
}
.post-list-group li{
overflow: hidden;
padding-bottom: 20px;
}
.author-avatar-group{
float: left;
}
.author-avatar-group img{
width: 50px;
height: 50px;
border-radius: 50%;
}
.post-info-group{
float: left;
margin-left: 10px;
border-bottom: 1px solid #e6e6e6;
width: 85%;
padding-bottom: 10px;
}
.post-info-group .post-info{
margin-top: 10px;
font-size: 12px;
color: #8c8c8c;
}
.post-info span{
margin-right: 10px;
}