Android--从零开始开发一款文章阅读APP
前言
本案例已经开源!如果你想免费下载,可以访问我的Github,所有案例均在上面,只求给个star。当然愿意支付小小金额请我喝茶也行(大学穷狗-.-)
一、准备工作
- 使用Android Studio开发
- 微信和QQ第三方sdk,需要自行申请(这个简单)
- 本案例使用干活集中营提供的api,使用MVp+Material Design作为主体架构进行开发
- 体验完整功能,点击下载APK
二、程序实现
目录结构
目录结构如下,我按照功能分包:
实现思路
整体架构--MVP+Material
重点代码分析
如果讲述整个App,估计一篇文章说不清楚。那我干脆取其中一条线来分析。
下面主要分析文章列表--文章详情--文章分享
主页文章列表
这里只选择Android文章模块进行介绍:
GankContract
public interface GankContract {
interface View extends BaseView<Presenter>{
//错误
void showError();
//正在加载
void showLoading();
//停止加载
void Stoploading();
//显示数据列表
void showResult(ArrayList<GankNews.Question> list);
//网络错误
void showNotNetError();
}
interface Presenter extends BasePresenter{
// 请求数据
void loadPosts(int PagerNum, boolean cleaing);
//刷新数据
void reflush();
//加载更多
void loadMore(int PagerNum);
//显示详情
void StartReading(int positon);
//随便看看
void LookAround();
}
}
GankFragment
Fragment的内容主要是文章列表,我们只分享重点:
//下拉刷新实现
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
boolean isScrollState=false;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
LinearLayoutManager manager= (LinearLayoutManager) recyclerView.getLayoutManager();
//没有滚动时候
if (newState==RecyclerView.SCROLL_STATE_IDLE){
//获的最后一个可见的item
int lastVisibilityItem=manager.findLastCompletelyVisibleItemPosition();
int totalItemCount=manager.getItemCount();
//判断是否滚动到底部并且是向下滑动
if (lastVisibilityItem==(totalItemCount-1)&&isScrollState){
presenter.loadMore(1);
}
}
}
//通知Presenter加载数据和设置item点击事件
@Override
public void showResult(ArrayList<GankNews.Question> list) {
if (adapter==null){
Log.i(TAG, "showResult: "+list.size());
adapter=new GankNewsAdapter(list,getContext());
adapter.setItemOnClickListener(new OnRecyclerViewOnClickListener() {
@Override
public void onItemClick(View v, int position) {
presenter.StartReading(position);
}
@Override
public void onItemLongClick(View v, int position) {
}
});
recyclerView.setAdapter(adapter);
}else {
adapter.notifyDataSetChanged();
}
}
GankPresenter
同样只分析重点代码:
//根据当前页数加载列表数据
@Override
public void loadPosts(int PagerNum, final boolean cleaing) {
CurrentPagerNum=PagerNum;
if (cleaing) {
view.showLoading();
}
if (Network.networkConnected(context)) {
model.load(Api.Gank_Android + PagerNum, new OnStringListener() {
@Override
public void onSuccess(String result) {
try {
// Log.i(TAG, "gankpresenter.model.load.result"+result);
GankNews news = gson.fromJson(result, GankNews.class);
//contenvalues只能存储基本类型的数据,像string,int之类的,不能存储对象这种东西,而HashTable却可以存储对象。
// ContentValues values = new ContentValues();
if (cleaing) {
list.clear();
}
for (GankNews.Question item : news.getResults()) {
/**
* 1.数据库查重:首先检测数据库中是否已经储存过该条数据
* 2:因为每次重启后都是在网络上重新下载数据 如果是数据库已经存在的数据则不会重新加载,也导致了这些数据当前id值为空
* ,所有要绑定队友的id值.
*/
if (!queryIfIdExists(item.get_id())){
DbLiteOrm.insert(item, ConflictAlgorithm.Replace);
}else {
ArrayList<GankNews.Question> ganklist=App.DbLiteOrm.query(new QueryBuilder<GankNews.Question>(GankNews.Question.class)
.where(GankNews.Question.COL_ID+"=?",new String[]{item.get_id()}));
GankNews.Question gankitem=ganklist.get(0);
item.setId(gankitem.getId());
}
list.add(item);
}
view.showResult(list);
}catch (JsonSyntaxException e){
view.showError();
}
view.Stoploading();
}
@Override
public void onError(VolleyError error) {
view.Stoploading();
view.showError();
}
});
} else {
//更新列表缓存 因为详情页都是用webView呈现 所以缓存content为空
if (cleaing){
QueryBuilder query=new QueryBuilder(GankNews.Question.class);
query.appendOrderDescBy("id");
query.limit(0,10*CurrentPagerNum);
list.addAll(DbLiteOrm.<GankNews.Question>query(query));
view.showResult(list);
}else {
view.showNotNetError();
}
}
}
//判断数据库是否已经存在
public boolean queryIfIdExists(String _id){
ArrayList<GankNews.Question> questionArrayList=App.DbLiteOrm.query(new QueryBuilder(GankNews.Question.class)
.where(GankNews.Question.COL_ID+"=?",new String[]{_id}));
if (questionArrayList.size()==0){
return false;
}
return true;
}
//传递当前点击item的信息,进入详情阅读
@Override
public void StartReading(int positon) {
//每个item就是一组数据
GankNews.Question item=list.get(positon);
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra("type", BeanTeype.TYPE_Gank);
intent.putExtra("id",list.get(positon).getId());
int id=list.get(positon).getId();
Log.i(TAG, "StartReading: "+id);
intent.putExtra("_id", list.get(positon).get_id());
intent.putExtra("url",list.get(positon).getUrl());
intent.putExtra("title", list.get(positon).getDesc());
if (item.getImages()==null){
intent.putExtra("imgUrl", "");
}else {
intent.putExtra("imgUrl", list.get(positon).getImages().get(0));
}
/**
* Content的startActivity方法,需要开启一个新的task。如果使用 Activity的startActivity方法,
* 不会有任何限制,因为Activity继承自Context,重载了startActivity方法。
*/
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
//随便看看 随机选取
@Override
public void LookAround() {
if (list.isEmpty()){
view.showError();
return;
}
StartReading(new Random().nextInt(list.size()));
}
GankNewsAdapter
因为文章分两种:有图和无图。所有要进行分类加载
//判断是否有图和是否是底部加载item
@Override
public int getItemViewType(int position) {
if (position==getItemCount()-1){
return TYPE_FOOTER;
}if (list.get(position).getImages()==null){
return TYPE_NO_IMG;
}
return TYPE_NORMTAL;
}
//根据type加载不同ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case TYPE_NORMTAL:
return new NormalViewHolder(inflater.inflate(R.layout.home_list_item_layout,parent,false),listener);
case TYPE_FOOTER:
return new FooterViewHolder(inflater.inflate(R.layout.list_footer,parent,false));
case TYPE_NO_IMG:
return new NoImageViewHolder(inflater.inflate(R.layout.home_list_item_without_image,parent,false),listener);
}
return null;
}
//使用Glide加载图片。无图则不加载
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!(holder instanceof FooterViewHolder)){
GankNews.Question item=list.get(position);
if (item!=null){
if (holder instanceof NormalViewHolder){
Glide.with(context)
.load(item.getImages().get(0))
.asBitmap()
.placeholder(R.mipmap.loading)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.error(R.mipmap.loading)
.centerCrop()
.into(((NormalViewHolder) holder).imageView);
((NormalViewHolder) holder).textView.setText(item.getDesc());
}else if (holder instanceof NoImageViewHolder){
((NoImageViewHolder) holder).textViewNoImg.setText(item.getDesc());
}
}
}
}
详情页
DetailActivity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frame);
if (savedInstanceState!=null){
detailFragment= (DetailFragment) getSupportFragmentManager().getFragment(savedInstanceState,"detailFragment");
}else {
detailFragment=DetailFragment.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.container,detailFragment).commit();
}
//获取列表传过来的具体item数据
Intent intent=getIntent();
DetailPresenter presenter=new DetailPresenter(detailFragment,DetailActivity.this);
presenter.setType((BeanTeype) intent.getSerializableExtra("type"));
presenter.setId(intent.getIntExtra("id",1));
presenter.set_id(intent.getStringExtra("_id"));
presenter.setTitle(intent.getStringExtra("title"));
presenter.setUrl(intent.getStringExtra("url"));
presenter.setImgUrl(intent.getStringExtra("imgUrl"));
}
DetailContract
public class DetailContract {
interface Presenter extends BasePresenter{
/**
* 流浪器中打开
* 复制文本
* 复制连接
* 添加收藏或取消收藏
* 查询是否收藏
* 请求数据
* 分享到QQ
* 分享到微信
* 分享到朋友圈
* 分享到微信收藏
*/
void openInBrower();
void copyText();
void copyLink();
void addToOrDeleteFromBookMarks();
boolean queryIsBooksMarks();
void requestData();
void shareArticleToQQ(final MyQQListener listener);
void shareArticleToWx();
void shareArticleToWxCommunity();
void shareArticleToWxCollect();
}
interface View extends BaseView<Presenter> {
// 显示正在加载
void showLoading();
// 停止加载
void stopLoading();
// 显示加载错误
void showLoadingError();
// 显示分享时错误
void showSharingError();
// 正确获取数据后显示内容
// void showResult(String result);
// // 对于body字段的消息,直接接在url的内容
void showResultWithoutBody(String url);
// 设置顶部大图
void showCover(String url);
// 设置标题
void setTitle(String title);
// 设置是否显示图片
void setImageMode(boolean showImage);
// 用户选择在浏览器中打开时,如果没有安装浏览器,显示没有找到浏览器错误
void showBrowserNotFoundError();
// 显示已复制文字内容
void showTextCopied();
// 显示文字复制失败
void showCopyTextError();
// 显示已添加至收藏夹
void showAddedToBookmarks();
// 显示已从收藏夹中移除
void showDeletedFromBookmarks();
void showNotNetError();
void shareSuccess();
void shareError();
void shareCancel();
}
}
DetailFragment
详情页主题是使用WebView显示,重点注意好设置属性和正确销毁:
@Override
public void initView(View view) {
......
//webview设置属性
webview.getSettings().setJavaScriptEnabled(true);
//缩放,设置为不能缩放可以防止页面上出现放大和缩小的图标
webview.getSettings().setBuiltInZoomControls(false);
//缓存
webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
//开启DOM storage API功能
webview.getSettings().setDomStorageEnabled(true);
//开启application Cache功能
webview.getSettings().setAppCacheEnabled(false);
.....
}
//早onDestroy中销毁WebView的对象
@Override
public void onDestroyView() {
super.onDestroyView();
webview.removeAllViews();
webview.destroy();
webview=null;
}
DetailPresenter
//复制链接地址
@Override
public void copyLink() {
ClipboardManager manager= (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData data=null;
switch (type){
case TYPE_Gank:
data=ClipData.newPlainText("text",url);
}
manager.setPrimaryClip(data);
view.showTextCopied();
}
//添加到收藏或者移除收藏
@Override
public void addToOrDeleteFromBookMarks() {
switch (type){
case TYPE_Gank:
GankNews.Question gank= App.DbLiteOrm.queryById(id,GankNews.Question.class);
if (queryIsBooksMarks()){
view.showDeletedFromBookmarks();
gank.mark=false;
}else {
view.showAddedToBookmarks();
gank.mark=true;
}
App.DbLiteOrm.update(gank);
break;
case TYPE_Front:
FrontNews.Question front=App.DbLiteOrm.queryById(id,FrontNews.Question.class);
if (queryIsBooksMarks()){
view.showDeletedFromBookmarks();
front.mark=false;
}else {
view.showAddedToBookmarks();
front.mark=true;
}
App.DbLiteOrm.update(front);
break;
case TYPE_IOS:
IosNews.Question ios=App.DbLiteOrm.queryById(id,IosNews.Question.class);
if (queryIsBooksMarks()){
view.showDeletedFromBookmarks();
ios.mark=false;
}else {
view.showAddedToBookmarks();
ios.mark=true;
}
App.DbLiteOrm.update(ios);
}
}
//查询是否已经收藏
@Override
public boolean queryIsBooksMarks() {
if (_id ==null || type==null){
view.showLoadingError();
return false;
}
//true为已经收藏 false未收藏
switch (type){
case TYPE_Gank:
GankNews.Question gank= App.DbLiteOrm.queryById(id,GankNews.Question.class);
OrmLog.i(TAG,gank);
boolean isMark=gank.mark;
if (isMark){
return true;
}else {
return false;
}
case TYPE_Front:
FrontNews.Question front=App.DbLiteOrm.queryById(id,FrontNews.Question.class);
if (front.mark){
return true;
}else {
return false;
}
case TYPE_IOS:
Log.i(TAG, "queryIsBooksMarks: "+id);
IosNews.Question ios=App.DbLiteOrm.queryById(id,IosNews.Question.class);
OrmLog.i(TAG,ios);
if (ios.mark){
return true;
}else {
return false;
}
}
return false;
}
//分享到QQ
@Override
public void shareArticleToQQ(MyQQListener listener) {
//title == desc
if (TextUtils.isEmpty(imgUrl)){
ShareSingleton.getInstance().shareToQQ((Activity) context,url,"推荐给你一篇文章",title, R.string.app_name, QQShare.SHARE_TO_QQ_FLAG_QZONE_ITEM_HIDE,listener);
}else {
ShareSingleton.getInstance().shareToQQ((Activity) context,url,"推荐给你一篇文章",title,imgUrl,R.string.app_name, QQShare.SHARE_TO_QQ_FLAG_QZONE_ITEM_HIDE,listener);
}
}
//分享到微信
@Override
public void shareArticleToWx() {
//title == desc
ShareSingleton.getInstance().shareWebToWx(url,"",title,true);
}
//分享到朋友圈
@Override
public void shareArticleToWxCommunity() {
//title == desc
ShareSingleton.getInstance().shareWebToWx(url,"",title,false);
}
//分享到微信收藏
@Override
public void shareArticleToWxCollect() {
//title == desc
ShareSingleton.getInstance().shareWebToWxCollect(url,"干货",title);
}
ShareSingleton
关于微信和QQ分享的具体方法还得参考官方文章,我这里提出我自己写好的分享单例类
public class ShareSingleton {
private Tencent mTencent;
public static IWXAPI api;
private static final int THUMB_SIZE = 150;
//单例模式
private ShareSingleton() {
}
public static final ShareSingleton getInstance(){
return Singleton.INSTANCE;
}
private static class Singleton{
private static final ShareSingleton INSTANCE=new ShareSingleton();
}
/**
* 图文分享 图片来源网络
* !! 分享操作要在主线程中完成
* @param activity
* @param targetUrl 这条分享消息被好友点击后的跳转URL。
* @param shareTitle 分享的标题, 最长30个字符。
* @param shareSummary 分享的消息摘要,最长40个字。
* @param netImgUrl 可填 分享图片的URL或者本地路径
* @param appName 手Q客户端顶部,替换“返回”按钮文字,如果为空,用返回代替
* @param shareToQQExtInt 额外选项 是否自动打开分享到QZone的对话框
* @param listener 分享回调接口
*/
public void shareToQQ(Activity activity,String targetUrl,String shareTitle,String shareSummary,
@Nullable String netImgUrl,@StringRes int appName,int shareToQQExtInt,MyQQListener listener){
if (mTencent==null){
mTencent=Tencent.createInstance(Constants.QQ_APP_ID,activity.getApplicationContext());
}
final Bundle params = new Bundle();
params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_DEFAULT);
params.putString(QQShare.SHARE_TO_QQ_TARGET_URL,targetUrl);
params.putString(QQShare.SHARE_TO_QQ_TITLE, shareTitle);
params.putString(QQShare.SHARE_TO_QQ_SUMMARY, shareSummary );
params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, netImgUrl);
params.putString(QQShare.SHARE_TO_QQ_APP_NAME,activity.getString(appName));
params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, shareToQQExtInt);
mTencent.shareToQQ(activity, params, listener);
}
/**
* 文章分享 无图
* !! 分享操作要在主线程中完成
* @param activity
* @param targetUrl 这条分享消息被好友点击后的跳转URL。
* @param shareTitle 分享的标题, 最长30个字符。
* @param shareSummary 分享的消息摘要,最长40个字。
* @param appName 手Q客户端顶部,替换“返回”按钮文字,如果为空,用返回代替
* @param shareToQQExtInt 额外选项 是否自动打开分享到QZone的对话框
* @param listener 分享回调接口
*/
public void shareToQQ(Activity activity,String targetUrl,String shareTitle,String shareSummary
,@StringRes int appName,int shareToQQExtInt,MyQQListener listener){
if (mTencent==null){
mTencent=Tencent.createInstance(Constants.QQ_APP_ID,activity.getApplicationContext());
}
final Bundle params = new Bundle();
params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_DEFAULT);
params.putString(QQShare.SHARE_TO_QQ_TARGET_URL,targetUrl);
params.putString(QQShare.SHARE_TO_QQ_TITLE, shareTitle);
params.putString(QQShare.SHARE_TO_QQ_SUMMARY, shareSummary );
params.putString(QQShare.SHARE_TO_QQ_APP_NAME,activity.getString(appName));
params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, shareToQQExtInt);
mTencent.shareToQQ(activity, params, listener);
}
/**
* 分享文章到微信/朋友圈
* @param webUrl
* @param webTitle
* @param webDesc
* @param isShareFriend
*/
public void shareWebToWx(@NonNull String webUrl,String webTitle,String webDesc,boolean isShareFriend){
// 注册操作也可以写死在Application中
// 通过WXAPIFactory工厂,获取IWXAPI的实例
api=WXAPIFactory.createWXAPI(App.getContext(),Constants.WX_APP_ID,true);
// 将该app注册到微信
api.registerApp(Constants.WX_APP_ID);
//初始化一个WXWebpageObject对象,填写url
WXWebpageObject webpag=new WXWebpageObject();
webpag.webpageUrl=webUrl;
//用WXWebpageObject对象初始化一个WXMediaMessage对象 填写标题和描述
WXMediaMessage msg=new WXMediaMessage(webpag);
msg.title=webTitle;
msg.description=webDesc;
//构造一个Req
SendMessageToWX.Req req=new SendMessageToWX.Req();
req.transaction=buildTransaction("webpage");//transaction 字段用于唯一标识一个请求
req.message= msg;
req.scene=isShareFriend ? SendMessageToWX.Req.WXSceneSession : SendMessageToWX.Req.WXSceneTimeline;
api.sendReq(req);
}
/**
* 分享文章到微信收藏
* @param webUrl
* @param webTitle
* @param webDesc
*/
public void shareWebToWxCollect(@NonNull String webUrl, String webTitle, String webDesc){
// 注册操作也可以写死在Application中
// 通过WXAPIFactory工厂,获取IWXAPI的实例
api=WXAPIFactory.createWXAPI(App.getContext(),Constants.WX_APP_ID,true);
// 将该app注册到微信
api.registerApp(Constants.WX_APP_ID);
//初始化一个WXWebpageObject对象,填写url
WXWebpageObject webpag=new WXWebpageObject();
webpag.webpageUrl=webUrl;
//用WXWebpageObject对象初始化一个WXMediaMessage对象 填写标题和描述
WXMediaMessage msg=new WXMediaMessage(webpag);
msg.title=webTitle;
msg.description=webDesc;
//构造一个Req
SendMessageToWX.Req req=new SendMessageToWX.Req();
req.transaction=buildTransaction("webpage");//transaction 字段用于唯一标识一个请求
req.message= msg;
req.scene=SendMessageToWX.Req.WXSceneFavorite;
api.sendReq(req);
}
这篇文章就分析这么多,如果你想了解跟多,欢迎下载源码。主要部分源码都有注释
三、部分运行效果
四、其他补充
如果你有问题可以提交到Github的issue上,也可以给我发邮件。我的邮件是yeshuwei.swy@gmail.com
Android--从零开始开发一款文章阅读APP
注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权
Android--从零开始开发一款文章阅读APP的更多相关文章
- Android开源实战:使用MVP+Retrofit开发一款文字阅读APP
文字控 使用MVP+Retrofit开发的一款文艺APP,它是一个非常优美的文字阅读应用,界面基本上符合material design设计规范. 在该项目中,我采用的是MVP架构,该架构目前在Andr ...
- 基于WanAndroid开放API实现的文章阅读APP
简介 基于WanAndroid开放API开发的技术文章阅读App.主要功能包括:首页.体系.项目.公众号.搜索.登录.收藏.夜间模式等. 用到的第三方框架 RxJava RxAndroid Retro ...
- 从零开始开发一款app,所想到的
我在知乎上看到这个问题http://www.zhihu.com/question/27645587.我在阅读了各位大牛的答案后,再加上自己的思考,就有了这篇文章的内容. 从零开始开发一款app ...
- 开发一款即时通讯App,从这几步开始
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云视频发表于云+社区专栏 关注公众号"腾讯云视频",一键获取 技术干货 | 优惠活动 | 视频方案 " ...
- 从零开始开发一款H5小游戏(二) 创造游戏世界,启动发条
本系列文章对应游戏代码已开源 Sinuous game 上一节介绍了canvas的基础用法,了解了游戏开发所要用到的API.这篇文章开始,我将介绍怎么运用这些API来完成各种各样的游戏效果.这个过程更 ...
- 从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命
本系列文章对应游戏代码已开源 Sinuous game. 每个游戏都会包含场景和角色.要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来. 场景坐标 canv ...
- 如何开发一款堪比APP的微信小程序(腾讯内部团队分享)
一夜之间,微信小程序刷爆了行业网站和朋友圈,小程序真的能如张小龙所说让用户"即用即走"吗? 其功能能和动辄几十兆安装文件的APP相比吗? 开发小程序,是不是意味着移动应用开发的一次 ...
- [Android游戏开发]八款开源 Android 游戏引擎 (巨好的资源)
初学Android游戏开发的朋友,往往会显得有些无所适从,他们常常不知道该从何处入手,每当遇到自己无法解决的难题时,又往往会一边羡慕于 iPhone下有诸如Cocos2d-iphone之类的免费游戏引 ...
- Android商城开发系列(二)——App启动欢迎页面制作
商城APP一般都会在应用启动时有一个欢迎界面,下面我们来实现一个最简单的欢迎页开发:就是打开商城App,先出现欢迎界面,停留几秒钟,自动进入应用程序的主界面. 首先先定义WelcomeActivity ...
随机推荐
- 【计算几何】【极角排序】Gym - 101174B - Bribing Eve
把每件物品当成平面上一个点,将第一件物品放在原点.那个权重值相当于一条直线,于是相当于直线绕原点转一圈,统计上侧点的数量. 队友的代码: #include <cmath> #include ...
- SOCKET类型定义及应用
读代码时看到此处,摘记下来. 流套接字(SOCK_STREAM):流套接字用于提供面向连接.可靠的数据传输服务.该服务将保证数据能够实现无差错.无重复发送,并按顺序接收.流套接字之所以能够实现可靠的数 ...
- 兼容各种浏览器下调用iframe里面的函数
<script type="text/javascript"> var o = $(window.frames["menu"])[0].conten ...
- Problem Z: 百鸡问题
#include <stdio.h> int main() { int i, j, k; ; i <= ; i++ ) ; j <= ; j++ ) ; k <= ; k ...
- Very Good!——C语言初学者百题大战之二
#include<stdio.h> int main() { printf("***************\n\n Very Good!\n\n***************& ...
- linux shell 自定义函数(定义、返回值、变量作用域)介绍
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.下面说说它的定义方法,以及调用需要注意那些事项. 一.定义shell函数(define function) 语法: [ f ...
- iptables的框架
参考 : https://segmentfault.com/a/1190000002540601 SNAT: 网关上,POSTROUTING链上,报文出去的时候处理,适用:内网多台 ...
- Nand Flash与Nor
转:http://www.360doc.com/content/11/1215/15/1299815_172458274.shtml Flash经常在一些地方被提到,一直没认真去理解它们的区别,因此, ...
- boost.asio包装类st_asio_wrapper开发教程(转)
一:什么是st_asio_wrapper它是一个c/s网络编程框架,基于对boost.asio的包装(最低在boost-1.49.0上调试过),目的是快速的构建一个c/s系统: 二:st_asio_w ...
- LINUX下渗透提权之嗅探技术
内网渗透在攻击层面,其实更趋向于社工和常规漏洞检测的结合,为了了解网内防护措施的设置是通过一步步的刺探和经验积累,有时判断出错,也能进入误 区.但是如果能在网内进行嗅探,则能事半功倍,处于一个对网内设 ...