1. 介紹及第三方資料庫的搭建
【說明】伺服器的開發:主要是接口的開發和管理(怎麼和資料庫互動)
1.1 服務端的接口清單
1.2 資料庫的表
1.3 第三方資料庫的使用
【建立應用和綁定服務】
【釋出管理】
【建立第一張表】
【建立第二張表】
2. 服務端接口api、部署測試、測試
【開發環境】使用的是javaEE工具包;
【說明】首先建立的是CreateRoom,建立房間号,其他的伺服器接口與此大同小異;
2.1 資料庫資料的建表、查詢
【建立項目】
【增加severlet包】
【建立類】
【參數說明】
【HttpServletRequest req】請求的參數包含在此參數中
【HttpServletResponse resp】傳回的資料的在此參數中
【請求參數的擷取】
【資料庫插入資料】
【查詢語句并發送結果】
2.2 配置xml
2.3 釋出
釋出成功之後就可以通路了;
2.4【bug】
【bug】增加調試資訊可以使用system.out.print();在網頁的控制台存在背景運作的資訊;
【說明】因為現在的字段的值為空,是以需要增加雙引号;兩個逗号之間都存在“反斜杠”
2.5 容錯的添加
2.6 伺服器錯誤資訊的傳回
【說明】
【情況1】如果是錯誤的狀态和資訊,則errCode和errMsg中存在相應的内容;data字段為null;
【情況2】如果是正常的狀體,則errCode和errMsg中為null;data字段傳回相應的内容;
【源碼】/ImoocBearLive/src/imooc/bear/live/ResponseObject.java
1 package imooc.bear.live;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5
6 import javax.servlet.http.HttpServletResponse;
7
8 import com.google.gson.Gson;
9
10 public class ResponseObject {
11
12 public static final String CODE_SUCCESS = "1";
13 public static final String CODE_FAIL = "0";
14
15 private static Gson GsonInstance = new Gson();
16
17 public String code;
18 public String errCode;
19 public String errMsg;
20 public Object data;
21
22
23 public static ResponseObject getSuccessResponse(Object data) {
24 ResponseObject responseObject = new ResponseObject();
25 responseObject.code = CODE_SUCCESS;
26 responseObject.errCode = "";
27 responseObject.errMsg = "";
28 responseObject.data = data;
29 return responseObject;
30 }
31
32 public static ResponseObject getFailResponse(String errCode, String errMsg) {
33 ResponseObject responseObject = new ResponseObject();
34 responseObject.code = CODE_FAIL;
35 responseObject.errCode = errCode;
36 responseObject.errMsg = errMsg;
37 responseObject.data = null;
38 return responseObject;
39 }
40
41 }
【錯誤資訊的封裝】/ImoocBearLive/src/imooc/bear/live/Error.java
1 package imooc.bear.live;
2
3 public class Error {
4
5 public static final String errorCode_Exception = "500";
6 private static final String errorMsg_Exception = "伺服器異常";
7
8 public static String getExceptionMsg(String e) {
9 return errorMsg_Exception + ":" + e;
10 }
11 }
【錯誤資訊的擷取】
【userId的檢查報錯】
【測試】再次打包,釋出,然後運作測試;傳回的錯誤的資訊為亂碼,需要對編碼做相容的修改;
【編碼的相容】
【測試】
【資料的傳回】
2.7 完善roomId的資訊
【說明】之前傳回的隻是一個id的int值,應該完善為一個對象;
【修改之前傳回的數值】
【修改】将所有的資料全部傳回;
【建立該類】封裝好要封裝的對象;
[源碼]/ImoocBearLive/src/imooc/bear/live/RoomInfo.java
1 package imooc.bear.live;
2
3 public class RoomInfo {
4 public int roomId;
5 public String userId;
6 public String userName;
7 public String userAvatar;
8 public String liveCover;
9 public String liveTitle;
10 public int watcherNums;
11 }
【修改邏輯中傳回的對象】
3. 建立直播接口的調用--缺少内容
【網絡使用的庫】網絡直接使用okhttp庫;七牛庫中已經存在了;
4.服務端接口優化
【說明】優化分為兩處;
4.1 servlet的分發優化
【拷貝的内容是一大段的内容】
【調用action處理對象】
【源碼】/ImoocBearLive/src/imooc/bear/live/RoomServlet.java
1 package imooc.bear.live;
2
3 import imooc.bear.live.action.CreateRoomAction;
4 import imooc.bear.live.action.GetRoomListAction;
5 import imooc.bear.live.action.GetWatchersAction;
6 import imooc.bear.live.action.HeartBeatAction;
7 import imooc.bear.live.action.JoinRoomAction;
8 import imooc.bear.live.action.QuitRoomAction;
9
10 import java.io.IOException;
11 import java.sql.SQLException;
12
13 import javax.servlet.ServletException;
14 import javax.servlet.http.HttpServlet;
15 import javax.servlet.http.HttpServletRequest;
16 import javax.servlet.http.HttpServletResponse;
17
18 public class RoomServlet extends HttpServlet {
19
20 private static final long serialVersionUID = 1L;
21
22 private static final String RequestParamKey_Action = "action";
23 private static final String RequestAction_Create = "create";
24 private static final String RequestAction_Join = "join";
25 private static final String RequestAction_Quit = "quit";
26 private static final String RequestAction_GetList = "getList";
27 private static final String RequestAction_GetWatcher = "getWatcher";
28 private static final String RequestAction_HeartBeat = "heartBeat";
29
30 // http://XXXX.com?action=create&userId=xxx&userAvatar=xxx&...
31 @Override
32 protected void doPost(HttpServletRequest req, HttpServletResponse resp)
33 throws ServletException, IOException {
34 doGet(req, resp);
35 }
36
37 @Override
38 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
39 throws ServletException, IOException {
40 // super.doGet(req, resp);
41 // 處理使用者的請求
42 String action = req.getParameter(RequestParamKey_Action);
43 if (action == null || "".equals(action)) {
44 ResponseObject responseObject = ResponseObject.getFailResponse(
45 Error.errorCode_NoAction, Error.getNoActionMsg());
46 responseObject.send(resp);
47 return;
48 }
49 try {
50 if (RequestAction_Create.equals(action)) {
51 // 建立一個直播房間。
52 new CreateRoomAction().doAction(req, resp);
53 } else if (RequestAction_Join.equals(action)) {
54 // 加入一個直播房間。
55 new JoinRoomAction().doAction(req, resp);
56 } else if (RequestAction_Quit.equals(action)) {
57 // 退出一個直播房間。
58 new QuitRoomAction().doAction(req, resp);
59 } else if (RequestAction_GetList.equals(action)) {
60 // 擷取直播房間清單。
61 new GetRoomListAction().doAction(req, resp);
62 } else if (RequestAction_GetWatcher.equals(action)) {
63 // 擷取房間中的觀衆
64 new GetWatchersAction().doAction(req, resp);
65 } else if (RequestAction_HeartBeat.equals(action)) {
66 // 心跳包
67 new HeartBeatAction().doAction(req, resp);
68 } else {
69 ResponseObject responseObject = ResponseObject.getFailResponse(
70 Error.errorCode_NoRequestParam,
71 Error.getNoRequestParamMsg(RequestParamKey_Action));
72 responseObject.send(resp);
73 }
74 } catch (SQLException | IOException e) {
75 e.printStackTrace();
76 // 資料庫異常,傳回錯誤資訊
77 ResponseObject responseObject = ResponseObject.getFailResponse(
78 Error.errorCode_Exception,
79 Error.getExceptionMsg(e.getMessage()));
80 try {
81 responseObject.send(resp);
82 } catch (IOException e1) {
83 e1.printStackTrace();
84 }
85 }
86 }
87
88 }
4.2 優化2-sql的優化
【封裝】
【‘最後的源碼】/ImoocBearLive/src/imooc/bear/live/SqlManager.java
1 package imooc.bear.live;
2
3 import java.sql.Connection;
4 import java.sql.DriverManager;
5 import java.sql.SQLException;
6
7 //擷取資料庫連接配接的類
8 public class SqlManager {
9
10 private static final String dbPro = "jdbc:mysql://";
11 private static final String host = "192.168.1.234";// ip位址
12 private static final String port = "30112";// 端口号
13 private static final String dbName = "5d4b8dd3c5be4";// 資料庫名字
14 private static final String charset = "?useUnicode=true&charactsetEncoding=utf-8";// 字元集
15
16 private static final String url = dbPro + host + ":" + port + "/" + dbName
17 + charset;
18 private static final String user = "b3c15e03e3df4";
19 private static final String password = "d10ce1a25ae64";
20
21 public static Connection getConnection() throws SQLException {
22 return DriverManager.getConnection(url, user, password);
23 }
24
25 }
【修改調用】
5.擷取直播清單
5.1 架構的封裝
【擷取直播清單的邏輯】
【建立擷取清單的類】
【完善直播清單】
【封裝doGet】doget方法是所有的action需要的方法,是以需要抽象接口;
【接口的實作】
【servlet的修改】
5.2 直播清單的具體實作
【說明】【與資料庫互動】【進行下拉重新整理和上拉加載的功能】【分頁的功能】【需要設定多個參數】
【分頁頁面的擷取】
【錯誤碼的增加】
【錯誤資訊的傳回】
【從資料庫中擷取直播清單】
【異常的捕獲】
【測試】
【資料庫添加資料】
【資料包的導出】
、
【資料擷取】
6.直播房間清單界面實作--此段講解沒有完成
6.1 直播清單的布局
【布局源碼】layout/fragment_live_list.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:id="@+id/activity_live_list"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent">
7
8 <android.support.v7.widget.Toolbar
9 android:id="@+id/titlebar"
10 android:layout_width="match_parent"
11 android:layout_height="?attr/actionBarSize"
12 android:background="@color/colorPrimaryDark" />
13
14 <android.support.v4.widget.SwipeRefreshLayout
15 android:id="@+id/swipe_refresh_layout_list"
16 android:layout_width="match_parent"
17 android:layout_height="match_parent"
18 android:layout_below="@id/titlebar"
19 android:layout_weight="1">
20
21 <ListView
22 android:id="@+id/live_list"
23 android:layout_width="match_parent"
24 android:layout_height="match_parent"
25 android:clickable="true"
26 android:divider="@null" />
27 </android.support.v4.widget.SwipeRefreshLayout>
28
29 </RelativeLayout>
【item的源碼】
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:orientation="vertical">
6
7 <TextView
8 android:id="@+id/live_title"
9 android:layout_width="match_parent"
10 android:layout_height="wrap_content"
11 android:padding="15sp"
12 android:text="标題"
13 android:textColor="#333"
14 android:textSize="18sp" />
15
16 <View
17 android:layout_width="match_parent"
18 android:layout_height="1px"
19 android:background="#3000" />
20
21 <RelativeLayout
22 android:id="@+id/live"
23 android:layout_width="match_parent"
24 android:layout_height="wrap_content">
25
26 <ImageView
27 android:id="@+id/live_cover"
28 android:layout_width="match_parent"
29 android:layout_height="210dp"
30 android:scaleType="fitXY"
31 android:src="@drawable/cover_background" />
32
33 <ImageView
34 android:id="@+id/live_logo"
35 android:layout_width="wrap_content"
36 android:layout_height="wrap_content"
37 android:layout_alignParentRight="true"
38 android:layout_alignParentTop="true"
39 android:layout_marginRight="12dp"
40 android:layout_marginTop="10dp"
41 android:src="@drawable/icon_livewhite" />
42
43 <LinearLayout
44 android:id="@+id/host_bar"
45 android:layout_width="match_parent"
46 android:layout_height="55dp"
47 android:background="#99efefef"
48 android:layout_alignBottom="@+id/live_cover"
49 android:orientation="horizontal">
50
51 <ImageView
52 android:id="@+id/host_avatar"
53 android:layout_width="45dp"
54 android:layout_height="45dp"
55 android:layout_gravity="center_vertical"
56 android:layout_marginBottom="5dp"
57 android:layout_marginLeft="15dp"
58 android:layout_marginTop="5dp" />
59
60 <LinearLayout
61 android:layout_width="0dp"
62 android:layout_height="wrap_content"
63 android:layout_gravity="center_vertical"
64 android:layout_marginLeft="15dp"
65 android:layout_weight="1"
66 android:orientation="vertical">
67
68 <TextView
69 android:id="@+id/host_name"
70 android:layout_width="wrap_content"
71 android:layout_height="wrap_content"
72 android:maxLines="1"
73 android:text="主播名字"
74 android:textColor="#333"
75 android:textSize="18sp" />
76
77 </LinearLayout>
78
79
80 <TextView
81 android:id="@+id/watch_nums"
82 android:layout_width="wrap_content"
83 android:layout_height="wrap_content"
84 android:layout_gravity="center"
85 android:layout_marginLeft="15dp"
86 android:layout_marginRight="15dp"
87 android:drawablePadding="5dp"
88 android:gravity="right"
89 android:text="1000\n在看"
90 android:textSize="14sp" />
91
92 </LinearLayout>
93
94 </RelativeLayout>
95
96 <View
97 android:layout_width="match_parent"
98 android:layout_height="1dp"
99 android:background="#3000" />
100 </LinearLayout>
6.2 擴充卡的建立
【源碼】imooc.com.bearlive.livelist.LiveListFragment
1 package imooc.com.bearlive.livelist;
2
3 import android.content.Context;
4 import android.content.Intent;
5 import android.graphics.Color;
6 import android.os.Bundle;
7 import android.support.annotation.Nullable;
8 import android.support.v4.app.Fragment;
9 import android.support.v4.widget.SwipeRefreshLayout;
10 import android.support.v7.app.AppCompatActivity;
11 import android.support.v7.widget.Toolbar;
12 import android.text.TextUtils;
13 import android.view.LayoutInflater;
14 import android.view.View;
15 import android.view.ViewGroup;
16 import android.widget.BaseAdapter;
17 import android.widget.ImageView;
18 import android.widget.ListView;
19 import android.widget.TextView;
20 import android.widget.Toast;
21
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import imooc.com.bearlive.R;
26 import imooc.com.bearlive.model.RoomInfo;
27 import imooc.com.bearlive.utils.ImgUtils;
28 import imooc.com.bearlive.utils.request.BaseRequest;
29 import imooc.com.bearlive.watcher.WatcherActivity;
30
31 /**
32 * Created by Administrator.
33 */
34
35 public class LiveListFragment extends Fragment {
36
37 private ListView mLiveListView;
38 private LiveListAdapter mLiveListAdapter;
39 private SwipeRefreshLayout mSwipeRefreshLayout;
40
41 @Nullable
42 @Override
43 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
44 super.onCreateView(inflater, container, savedInstanceState);
45 View view = inflater.inflate(R.layout.fragment_live_list, container, false);
46
47 findAllViews(view);
48 requestLiveList();
49
50 return view;
51 }
52
53 private void requestLiveList() {
54 //請求前20個資料
55 GetLiveListRequest liveListRequest = new GetLiveListRequest();
56 liveListRequest.setOnResultListener(new BaseRequest.OnResultListener<List<RoomInfo>>() {
57 @Override
58 public void onSuccess(List<RoomInfo> roomInfos) {
59
60 mLiveListAdapter.removeAllRoomInfos();//下拉重新整理,先移除掉之前的room資訊
61 mLiveListAdapter.addRoomInfos(roomInfos);//再添加新的資訊
62
63 mSwipeRefreshLayout.setRefreshing(false);
64 }
65
66 @Override
67 public void onFail(int code, String msg) {
68 Toast.makeText(getActivity(), "請求清單失敗:" + msg, Toast.LENGTH_SHORT).show();
69 mSwipeRefreshLayout.setRefreshing(false);
70 }
71 });
72 GetLiveListRequest.LiveListParam param = new GetLiveListRequest.LiveListParam();
73 param.pageIndex = 0;//從0開始,也就是第一頁。
74 String url = liveListRequest.getUrl(param);
75 liveListRequest.request(url);
76 }
77
78 private void findAllViews(View view) {
79
80 Toolbar titlebar = (Toolbar) view.findViewById(R.id.titlebar);
81 titlebar.setTitle("熱播清單");
82 titlebar.setTitleTextColor(Color.WHITE);
83 ((AppCompatActivity) getActivity()).setSupportActionBar(titlebar);
84
85 mLiveListView = (ListView) view.findViewById(R.id.live_list);
86 mLiveListAdapter = new LiveListAdapter(getContext());
87 mLiveListView.setAdapter(mLiveListAdapter);
88
89 mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout_list);
90 mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
91 @Override
92 public void onRefresh() {
93 //請求伺服器,擷取直播清單
94 requestLiveList();
95 }
96 });
97 }
98
99 private class LiveListAdapter extends BaseAdapter {
100
101 private Context mContext;
102 private List<RoomInfo> liveRooms = new ArrayList<RoomInfo>();
103
104 public LiveListAdapter(Context context) {
105 this.mContext = context;
106 }
107
108 public void removeAllRoomInfos() {
109 liveRooms.clear();
110 }
111
112 public void addRoomInfos(List<RoomInfo> roomInfos) {
113 if (roomInfos != null) {
114 liveRooms.clear();
115 liveRooms.addAll(roomInfos);
116 notifyDataSetChanged();
117 }
118 }
119
120 @Override
121 public int getCount() {
122 return liveRooms.size();
123 }
124
125 @Override
126 public RoomInfo getItem(int position) {
127 return liveRooms.get(position);
128 }
129
130 @Override
131 public long getItemId(int position) {
132 return position;
133 }
134
135 @Override
136 public View getView(int position, View convertView, ViewGroup parent) {
137 RoomHolder holder = null;
138 if (convertView == null) {
139 convertView = LayoutInflater.from(mContext).inflate(R.layout.item_live_list, null);
140 holder = new RoomHolder(convertView);
141 convertView.setTag(holder);
142 } else {
143 holder = (RoomHolder) convertView.getTag();
144 }
145
146 holder.bindData(liveRooms.get(position));
147
148 return convertView;
149 }
150
151
152 private class RoomHolder {
153
154 View itemView;
155 TextView liveTitle;
156 ImageView liveCover;
157 ImageView hostAvatar;
158 TextView hostName;
159 TextView watchNums;
160
161 public RoomHolder(View view) {
162 itemView = view;
163 liveTitle = (TextView) view.findViewById(R.id.live_title);
164 liveCover = (ImageView) view.findViewById(R.id.live_cover);
165 hostName = (TextView) view.findViewById(R.id.host_name);
166 hostAvatar = (ImageView) view.findViewById(R.id.host_avatar);
167 watchNums = (TextView) view.findViewById(R.id.watch_nums);
168 }
169
170 public void bindData(final RoomInfo roomInfo) {
171
172 String userName = roomInfo.userName;
173 if (TextUtils.isEmpty(userName)) {
174 userName = roomInfo.userId;
175 }
176 hostName.setText(userName);
177
178 String liveTitleStr = roomInfo.liveTitle;
179 if (TextUtils.isEmpty(liveTitleStr)) {
180 this.liveTitle.setText(userName + "的直播");
181 } else {
182 this.liveTitle.setText(liveTitleStr);
183 }
184 String url = roomInfo.liveCover;
185 if (TextUtils.isEmpty(url)) {
186 ImgUtils.load(R.drawable.default_cover, liveCover);
187 } else {
188 ImgUtils.load(url, liveCover);
189 }
190
191 String avatar = roomInfo.userAvatar;
192 if (TextUtils.isEmpty(avatar)) {
193 ImgUtils.loadRound(R.drawable.default_avatar, hostAvatar);
194 } else {
195 ImgUtils.loadRound(avatar, hostAvatar);
196 }
197
198 int watchers = roomInfo.watcherNums;
199 String watchText = watchers + "人\r\n正在看";
200 watchNums.setText(watchText);
201
202 itemView.setOnClickListener(new View.OnClickListener() {
203 @Override
204 public void onClick(View view) {
205 Intent intent = new Intent();
206 intent.setClass(mContext, WatcherActivity.class);
207 intent.putExtra("roomId", roomInfo.roomId);
208 intent.putExtra("hostId", roomInfo.userId);
209 startActivity(intent);
210 }
211 });
212 }
213 }
214 }
215 }
7.主播直播的基本界面
【說明】用到了主播直播的騰訊雲中的主播開直播的功能
【布局】
1 <?xml version="1.0" encoding="utf-8"?>
2 <imooc.com.bearlive.widget.SizeChangeRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:id="@+id/size_change_layout"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 tools:context="imooc.com.bearlive.hostlive.HostLiveActivity">
8
9
10 <com.tencent.ilivesdk.view.AVRootView
11 android:id="@+id/live_view"
12 android:layout_width="match_parent"
13 android:layout_height="match_parent"
14 android:background="@android:color/white" />
15
16 <imooc.com.bearlive.view.TitleView
17 android:id="@+id/title_view"
18 android:layout_width="match_parent"
19 android:layout_height="wrap_content" />
20
21 <FrameLayout
22 android:id="@+id/bottom_view"
23 android:layout_width="match_parent"
24 android:layout_height="wrap_content"
25 android:layout_alignParentBottom="true">
26
27 <imooc.com.bearlive.view.BottomControlView
28 android:id="@+id/control_view"
29 android:layout_width="match_parent"
30 android:layout_height="wrap_content" />
31
32 <imooc.com.bearlive.view.ChatView
33 android:id="@+id/chat_view"
34 android:layout_width="match_parent"
35 android:layout_height="wrap_content" />
36 </FrameLayout>
37
38 <LinearLayout
39 android:id="@+id/chat_list_view"
40 android:layout_width="match_parent"
41 android:layout_height="wrap_content"
42 android:layout_above="@id/bottom_view"
43 android:orientation="horizontal">
44
45 <imooc.com.bearlive.view.ChatMsgListView
46 android:id="@+id/chat_list"
47 android:layout_width="0dp"
48 android:layout_height="180dp"
49 android:layout_weight="2" />
50
51 <View
52 android:layout_width="0dp"
53 android:layout_height="200dp"
54 android:layout_weight="1" />
55 </LinearLayout>
56 <imooc.com.bearlive.view.VipEnterView
57 android:id="@+id/vip_enter"
58 android:layout_width="wrap_content"
59 android:layout_height="wrap_content"
60 android:layout_above="@id/chat_list_view" />
61 <tyrantgit.widget.HeartLayout
62 android:id="@+id/heart_layout"
63 android:layout_width="100dp"
64 android:layout_height="match_parent"
65 android:layout_alignParentRight="true" />
66
67 <imooc.com.bearlive.view.DanmuView
68 android:id="@+id/danmu_view"
69 android:layout_width="match_parent"
70 android:layout_height="wrap_content"
71 android:layout_above="@id/vip_enter" />
72
73 <imooc.com.bearlive.view.GiftRepeatView
74 android:id="@+id/gift_repeat_view"
75 android:layout_width="wrap_content"
76 android:layout_height="match_parent"
77 android:layout_above="@id/chat_list_view" />
78
79 <imooc.com.bearlive.view.GiftFullView
80 android:id="@+id/gift_full_view"
81 android:layout_width="match_parent"
82 android:layout_height="match_parent" />
83 </imooc.com.bearlive.widget.SizeChangeRelativeLayout>