【說明】服務器的開發:主要是接口的開發和管理(怎麼和數據庫交互)java
【建立應用和綁定服務】mysql
【發佈管理】android
【創建第一張表】git
【創建第二張表】sql
【開發環境】使用的是javaEE工具包;數據庫
【說明】首先建立的是CreateRoom,建立房間號,其餘的服務器接口與此大同小異;api
【建立項目】服務器
【增長severlet包】網絡
【新建類】併發
【參數說明】
【HttpServletRequest req】請求的參數包含在此參數中
【HttpServletResponse resp】返回的數據的在此參數中
【請求參數的獲取】
【數據庫插入數據】
【查詢語句併發送結果】
發佈成功以後就能夠訪問了;
【bug】增長調試信息可使用system.out.print();在網頁的控制檯存在後臺運行的信息;
【說明】由於如今的字段的值爲空,所以須要增長雙引號;兩個逗號之間都存在「反斜槓」
【說明】
【狀況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的檢查報錯】
【測試】再次打包,發佈,而後運行測試;返回的錯誤的信息爲亂碼,須要對編碼作兼容的修改;
【編碼的兼容】
【測試】
【數據的返回】
【說明】以前返回的只是一個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 }
【修改邏輯中返回的對象】
【網絡使用的庫】網絡直接使用okhttp庫;七牛庫中已經存在了;
【說明】優化分爲兩處;
【拷貝的內容是一大段的內容】
【調用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 }
【封裝】
【‘最後的源碼】/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 }
【修改調用】
【獲取直播列表的邏輯】
【新建獲取列表的類】
【完善直播列表】
【封裝doGet】doget方法是全部的action須要的方法,所以須要抽象接口;
【接口的實現】
【servlet的修改】
【說明】【與數據庫交互】【進行下拉刷新和上拉加載的功能】【分頁的功能】【須要設置多個參數】
【分頁頁面的獲取】
【錯誤碼的增長】
【錯誤信息的返回】
【從數據庫中獲取直播列表】
【異常的捕獲】
【測試】
【數據庫添加數據】
【數據包的導出】
、
【數據獲取】
【佈局源碼】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>
【源碼】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 }
【說明】用到了主播直播的騰訊雲中的主播開直播的功能
【佈局】
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>