商业级项目——基金客户端的架构设计与开发(上)

最后更新于:2022-04-01 14:32:52

本项目是通力基金的商业级项目,声明:在此仅用于交流学习。该项目主要有三个主要功能模块:1、基金模块:包括基金的查询、展示等功能;2、账户模块:包括登录、充值、提现、收藏等功能;3、辅助模块:消息中心、帮助、意见反馈等。 项目运行首先会有一个闪屏页,然后进入的是一个新手引导页(只显示一次),可以向右滑动,共4页图片,当滑到最后一张时,图片中会有一个进入的按钮,点击这个按钮进可以进入到App的主页。  主页中:下面是4个导航,根据用户点击的不同,会切换至不同的界面。 ok,我们现在开始来分享一下这个软件的实现过程。新手引导页的话很简单,很多人都做过。总之就是使用一个viewPager来实现,使用一个pagerAdapter来填充viewpager的内容,如果直接继承pagerAdapter这个抽象类,需要重写getCount()、isViewFromObject()、destroyItem()、instantiateItem()等4个方法才可以为viewpager填充内容。我以前的博客中详细介绍了新手引导页的开发,传送门:[http://blog.csdn.net/sdksdk0/article/details/50043843 ](http://blog.csdn.net/sdksdk0/article/details/50043843)     [点击打开链接](http://blog.csdn.net/sdksdk0/article/details/50043843)。所以这里就不再重复写了。 下面来分享一个这个主界面的设计,首先使用的是一个非常经典的方法:TabHost。TabHost允许将多个控件放在同一个区域内,然后通过点击按钮来替换,这样就不用调用FragmentManager来进行替换的操作。 下面来看一下这个主界面的布局文件: ~~~ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="visible"> <RelativeLayout android:id="@+id/content_view" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/bottom_view"> <fragment android:id="@+id/jijin_view" android:name="com.zhilinghui.fund.fragment.JijinFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> <fragment android:id="@+id/member_view" android:name="com.zhilinghui.fund.fragment.MemberFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:id="@+id/fs_view" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.zhilinghui.fund.fragment.FSFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> <fragment android:id="@+id/settings_view" android:name="com.zhilinghui.fund.fragment.SettingsFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <RelativeLayout android:id="@+id/bottom_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@drawable/tab_bar_bg" android:gravity="center_vertical"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/tab_selected" android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" android:background="@drawable/tab_scroll" android:contentDescription="@null" android:visibility="gone" /> <ImageView android:id="@+id/notice_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="12dp" android:layout_marginTop="10dp" android:adjustViewBounds="true" android:background="@drawable/notice_logo" android:contentDescription="@null" android:visibility="gone" /> </RelativeLayout> </RelativeLayout> </TabHost> <RelativeLayout android:id="@+id/index_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/transparent" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/viewpager_index" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.zhilinghui.fund.widget.PageControlView android:id="@+id/pageControlView_index" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="30dp" /> </RelativeLayout> ~~~ 在Activity中要找到这些按钮: ~~~ setContentView(R.layout.a_main); bottom_view = (RelativeLayout) findViewById(R.id.bottom_view); mTabHost = (TabHost) findViewById(android.R.id.tabhost); mTabs = (TabWidget) findViewById(android.R.id.tabs); mTabs.setDividerDrawable(null); ~~~ 从上面的布局文件中,我们可以看到,这个TabHost里面是加了图片和文字的,这样看起来会更加美观一点,添加图片的方法如下: ~~~ tab01 = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.item_tab_view, null); tab01.setBackgroundResource(R.drawable.tab_jijin_drawable); tab02 = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.item_tab_view, null); tab02.setBackgroundResource(R.drawable.tab_member_drawable); tab03 = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.item_tab_view, null); tab03.setBackgroundResource(R.drawable.tab_fs_drawable); tab04 = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.item_tab_view, null); tab04.setBackgroundResource(R.drawable.tab_settings_drawable); mTabHost.addTab(mTabHost.newTabSpec("0") .setIndicator(tab01) .setContent(R.id.jijin_view)); mTabHost.addTab(mTabHost.newTabSpec("1") .setIndicator(tab02) .setContent(R.id.member_view)); mTabHost.addTab(mTabHost.newTabSpec("2") .setIndicator(tab03) .setContent(R.id.fs_view)); mTabHost.addTab(mTabHost.newTabSpec("3") .setIndicator(tab04) .setContent(R.id.settings_view)); mTabHost.setCurrentTab(0); //设当前 Tab 为 0 ~~~ 添加点击事件: ~~~ mTabHost.setOnTabChangedListener(mOnTabChangeListener); ~~~ 设置适配器: ~~~ // 导航栏适配器 adapter = new NavigatePagerAdapter(this, ids, mIndexOverListener, page_control); viewpager.setAdapter(adapter); viewpager.setOnPageChangeListener(mOnPageChangeListener); ~~~ (因为我虚拟机的分辨率没设置好,貌似图片有点失真了) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d709f0c1.jpg) 在第一页中上面那个可以滑动的自然也是一个viewpager了,下面就是一个listview第一个页面还具有下列刷新(包括上下箭头和时间)和加载更多的功能。基金是有一个后台的,从服务器传输数据显示在这个轮播条上面。下面的数据条目也是一样,因为这里没有过多的图片,所以不需要设置图片缓存(而某些项目的listview条目中有图片的话,我们就应该要考虑增加图片缓存功能了,从而避免太耗用户流量或内存溢出等问题)。当我们点击这个基金进去的时候,例如第一个“活期通力宝",就会跳转到另一个界面,这里详细的描述了该基金的信息,例如基金经理、基金代码、收益率、同类排名等内容,然后还会加上一个折线图,从而更加清晰的描述这个项目,给用户带来更好的体验。还有就是当数据与后台传输的时候,在这个界面还会有一个圆圈在转动,若网络无法连接,则会有一个我们自定义好了的提示界面,说网络无法连接。(在这里不提供公司的后台接口,所以这里的数据虚拟化了显示)。 ~~~ @Override public void onRefresh() { mController.execute(new UIAsyncTask(getFavHandler, mDatabaseAdapter, BaseHandlerUI.REQUEST_GET_FAV)); } @Override public void onLoadMore() { onLoad(); } private void onLoad() { mListView.stopRefresh(); mListView.stopLoadMore(); } ~~~ ~~~ @SuppressLint("SetJavaScriptEnabled") private void loadUrl() { showProgressDialog(context, context.getString(R.string.dlg_loading)); thread = new Thread(new Runnable() { @SuppressWarnings("deprecation") @Override public void run() { webview.getSettings().setJavaScriptEnabled(true); // webview.getSettings().setPluginsEnabled(true); webview.getSettings().setSupportZoom(true); webview.getSettings().setBuiltInZoomControls(true); //加载url前,设置图片阻塞 webview.getSettings().setBlockNetworkImage(true); webview.loadUrl(bean.url); MyWebViewClient myWebView = new MyWebViewClient(); webview.setWebViewClient(myWebView); } }); thread.start(); } ~~~ 全部基金listview的适配: ~~~ @SuppressLint("ResourceAsColor") public class Text_Adapter extends BaseAdapter { List<FundTitleBean> items = new ArrayList<FundTitleBean>(); private LayoutInflater mInflater; private OnClickListener mOnClick; Context context; private boolean left = true; public Text_Adapter(Context context, List<FundTitleBean> items, OnClickListener mOnClick, boolean left) { super(); this.context = context; this.mInflater = LayoutInflater.from(context); this.items = items; this.mOnClick = mOnClick; this.left = left; } @Override public int getCount() { return items.size(); } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; // ViewHolder 是内部类, 只有一个成员(name) TextView if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate(R.layout.item_list_txt_view, null); holder.name = (TextView) convertView .findViewById(R.id.name); // 这里的 name 是一个 TextView convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); //getTag 返回: 一个 Object 引用 } holder.name.setOnClickListener(mOnClick); holder.name.setTag(position); holder.name.setText(items.get(position).name); if (items.get(position).isSelected) { if (left) { convertView.setBackgroundResource(R.drawable.choose_s_left); } else { convertView.setBackgroundResource(R.drawable.choose_s_right); } } else { convertView.setBackgroundResource(R.drawable.choose_list_selector); } return convertView; } private class ViewHolder { TextView name; } } ~~~ 无网络时的错误提示Activity代码: ~~~ public void initView() { title = (TextView) findViewById(R.id.title_txt); title.setText(getString(R.string.dlg_net_err)); left_btn = (TextView) findViewById(R.id.left_btn); left_btn.setVisibility(View.VISIBLE); left_btn.setOnClickListener(this); left_btn.setVisibility(View.VISIBLE); right_btn = (TextView) findViewById(R.id.right_btn); right_btn.setVisibility(View.GONE); } ~~~ 说到这里,还需要分享一个知识就是关于Application的使用:在很多大型项目中都会有 ~~~ Application和Actovotu,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来 * 存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简 * 单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己 * 的 Application的名字定入即可)。 * android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且 * application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局 的单例的,所以在不同的Activity,Service中 * 获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享 等,数据缓存等操作。 * <p/> * 二.程序的入口 * Android使用Google Dalvik VM,相对于传统Java VM而言有着很大的不同,在Sun的Java体系中入口点和标准c语言一样是main(), * 而每个Android程序都包含着一个Application实例,一个Application实例中有多个Activity、 Service、ContentProvider或 * Broadcast Receiver。 * 其实在android.app.Application这个包的onCreate才是真正的Android入口点,只不过大多数开发者无需重写该类。 * <p/> * 第一步、写一个全局的单例模式的MyApplication继承自Application 重写onCreate * 第二步、配置全局的Context * <application android:name="com.zhilinghui.fund.app.GFFApp"></application> ~~~ ok,今天主要是分享了一下这个商业级基金项目的导航设置和第一个界面(主界面)的设计与功能介绍。下一次将分享”个人中心“、”交易界面"、“帮助中心”的设计与实现,当然,在下一篇博客中我会贴出这个项目的源码哦!今天的就暂不提供源码,欢迎关注! ### 记住:下一次博客会贴出整个项目的源码。可免费下载使用!欢迎关注!
';

猜拳游戏案例

最后更新于:2022-04-01 14:32:50

猜拳是由石头、剪刀、布组成,可以选择你的对手是谁,这里设定了3个对手(总裁、副总、总监),然后选择你的出拳,通过使用数字1、2、3来分别代表着3种状态。然后我们还需要加上计分的功能。这里我们建立4个类,人、机、主方法、测试类。 当然啦,我们都知道人类和机类的基本方法都差不多,因为我们要获取到键盘的输入内容,所以我们要使用这个Scanner ,这个在util包下面,我们直接导入就可以了。这里直接贴出的代码: ~~~ import java.util.Scanner; public class Person{ private String name; private int score; public Person(){ name="sdksdk0"; score=0; } public Person(String n){ name=n; score=0; } public int showFist(){ Scanner input=new Scanner(System.in); System.out.println("\n请出拳:1 剪刀 2 石头 3 布(输入相应数字):"); int show=input.nextInt(); switch(show){ case 1: System.out.println("你出拳:剪刀"); break; case 2: System.out.println("你出拳:石头"); break; case 3: System.out.println("你出拳:布"); break; } return show; } public String getName(){ return name; } public void setName(String name){ this.name=name; } public int getScore(){ return score; } public void setScore(int score){ this.score=score; } } ~~~ 机类的话,自然需要一个随机数了,这样才可以随机比较。 ~~~ import java.util.Random; public class Android{ private String name; private int score; public Android(String n){ name=n; score=0; } public Android(){ name="Android"; score=0; } public int showFist(){ Random r=new Random(); int show=r.nextInt(3)+1; switch(show){ case 1: System.out.println("你出拳:剪刀"); break; case 2: System.out.println("你出拳:石头"); break; case 3: System.out.println("你出拳:布"); break; } return show; } public String getName(){ return name; } public void setName(String name){ this.name=name; } public int getScore(){ return score; } public void setScore(int score){ this.score=score; } } ~~~ 下面最重要的是我们的主方法类了。 我们需要来定义人、机、计分方法。 private Person person; private Android android; private int count; 功能初始化 public void initial(){ person=new Person(); android=new Android(); count=0; } 定义显示结果: ~~~ if(result==1){ System.out.println("结果:平分秋色"); } else if(result==2){ System.out.println("结果:"+person.getName()+"你赢了"); } else if(result==3){ System.out.println("结果:"+person.getName()+"你输了"+android.getName()+"赢了"); } ~~~ 在这几种情况下是玩家赢了: ~~~ 1 剪刀 2 石头  3  布   ~~~ 用户1对机器3,用户2对机器1,用户3对机器2. 如果用户输入的和机器产生的一样就是平局了,其余情况就是机器赢了。 至于计算分数的话就这样就可以了android.setScore(android.getScore()+1); 完整代码如下: ~~~ import java.util.Scanner; public class Referee{ private Person person; private Android android; private int count; //初始化功能 public void initial(){ person=new Person(); android=new Android(); count=0; } //计算 总分 public int calcResult(){ if(person.getScore()==android.getScore()){ return 1; } else if(person.getScore()>android.getScore()){ return 2; } else{ return 3; } } //显示总结果 public void showResult(){ System.out.println("======================"); System.out.println(person.getName()+"VS"+android.getName()); System.out.println("总对战次数:"+count); int result=calcResult(); if(result==1){ System.out.println("结果:平分秋色"); } else if(result==2){ System.out.println("结果:"+person.getName()+"你赢了"); } else if(result==3){ System.out.println("结果:"+person.getName()+"你输了"+android.getName()+"赢了"); } System.out.println("======================"); } public void startGame(){ System.out.println("======================"); System.out.println("\n\t************************"); System.out.println("请选择你的对手 1 总裁 2 副总 3 总监"); Scanner input=new Scanner(System.in); int role=input.nextInt(); switch(role){ case 1: android.setName("总裁"); break; case 2: android.setName("副总"); break; case 3: android.setName("总监"); break; } System.out.println("\n要开始吗?[Y/N]"); String con=input.next(); int perFist; int androidFist; while(con.equals("Y")){ // perFist = person.showFist(); perFist=person.showFist(); androidFist=android.showFist(); if(perFist==androidFist){ System.out.println("结果:平局"); } else if((perFist==1&&androidFist==3) ||(perFist==2&&androidFist==1) ||(perFist==3&&androidFist==2)){ System.out.println("结果:"+person.getName()+"赢了!"); person.setScore(person.getScore()+1); } else{ System.out.println("结果:"+person.getName()+"输了"); android.setScore(android.getScore()+1); } count++; System.out.print("\n是否开始下一轮(Y/N)"); con=input.next(); } showResult(); } } ~~~ 最后,我们只需要一个测试类就可以了 ~~~ public class StartGame{ public static void main(String[] args){ Referee r=new Referee(); r.initial(); r.startGame(); } } ~~~ 当然咯,如果你想要把测试类都写在一起也是可以正常编译出来的,不过不建议这样做,这样的话对代码的重构性就不好了。使用命令javac StartGame.java进行编译,然后 java StartGame 运行。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d708117d.jpg)
';

动态数组的实现案例

最后更新于:2022-04-01 14:32:48

Java动态数组是一种可以任意伸缩数组长度的对象,在Java中比较常用的是List。下面介绍一下List作为Java动态数组的用法。 我们可以首先编写两个类List.java  和一个测试类Test1.java。将主类和测试类分开写,更有利于扩展性,这是一个非常好的编程思想。下面来看一下我们如何来实现List类。注释已经写得很清楚了,如果有不懂的地方欢迎留言。 ~~~ //定义一个容器类 public class List{ final int INIT_LENGTH=10; int[] array=new int[INIT_LENGTH]; int index=-1; int add(int [] newArray){ int temp=index; if(newArray==null){ System.out.println("数组不能为空"); return -1; } if(index+1+newArray.length>array.length){ int newlength=(index+1+newArray.length)*2; expand(newlength); } System.arraycopy(newArray,0,array,index+1,newArray.length); index=index+newArray.length; return temp+1; } //方法的重载 void expand(int newlength){ //新创建一个数组,这个新数组的长度两倍大 int[] x=new int[newlength]; //复制array中的数据到x中 System.arraycopy(array,0,x,0,array.length); array=x; } void expand(){ //新创建一个数组,这个新数组的长度两倍大 int[] x=new int[array.length*2]; //复制array中的数据到x中 System.arraycopy(array,0,x,0,array.length); array=x; } //输出指定位置的元素 int remove(int i){ //判断i是否存在 if(i>=0&&i<index){ System.arraycopy(array,i+1,array,i,index-i); index--; }else{ System.out.println("集合中没有这个索引"+i); return -1; } return -1; } //添加元素到指定位置 //value:要添加的值 //i:添加到数组的位置 //返回值:添加元素的索引 int add(int value,int i){ if(i<0||i>index+1){ System.out.println("索引越界"+i); return -1; } if(index>=array.length-1){ expand(); } index++; System.arraycopy(array,i,array,i+1,index-i); array[i]=value; return i; } //清空 void clear(){ array=new int[INIT_LENGTH]; index=-1; } //求出容器的真实大小 int size(){ return index+1; } void add(int value){ if( index>=array.length-1){ expand(); } index++; array[index]=value; } int[] getArray(){ //新创建一个数组 int[] result=new int[index+1]; System.arraycopy(array,0,result,0,index+1); return result; } } ~~~ 接下来,我们把测试类写好。 ~~~ public class Test1{ public static void main(String[] args){ List list=new List(); int[] r=list.getArray(); for(int t:r){ System.out.println(t); } //开始存元素 list.add(1); list.add(2); r=list.getArray(); for(int t:r){ System.out.println(t); } System.out.println("list容器中有"+list.size()); for(int i=0;i<10;i++){ list.add(i); } //存完后 r=list.getArray(); for(int t:r){ System.out.println(t); } System.out.println("list容器中有"+list.size()); System.out.println("清空==================="); list.clear(); System.out.println("清空后,list容器中有"+list.size()); System.out.println("移除==================="); for(int i=0;i<10;i++){ list.add(i); } list.remove(3); r=list.getArray(); for(int t:r){ System.out.println(t); } System.out.println("在指定位置添加元素"); list.add(888,1000); r=list.getArray(); } } ~~~ 然后呢,我们使用javac Test1.java      java Test1 就可以顺利编译运行了。
';

随机验证码实现案例

最后更新于:2022-04-01 14:32:45

众所周知,验证码在我们的生活中都是非常常见的,很多公司都在各种折腾各种各样的验证码,这里简要的用一个小案例来实现验证码的功能(ps:其实我挺讨厌验证码这个东西的)。 建立一个javaweb工程,新建login.html,在里面主要是写界面,代码实现如下,写在body区就可以了,来一个表单验证 ~~~ <body> <form action=""> username:<input/><br/> password:<input/><br/> 验证码:<input name="code"/><img id="image1" src="/day08_response/servlet/responseDemo4"/> <input type="button" value="看不清,换一张" onclick="change()"/> <br/> <input type="submit" value="登陆"/> </form> <script type="text/javascript"> function change(){ var imageObj = document.getElementById("image1"); //地址一样,浏览器不会发出请求 imageObj.src="/day08_response/servlet/responseDemo4?time="+new Date().getTime(); } </script> </body> ~~~ 然后我们需要新建一个servlet类,实现验证码我们可以有两种方式,一种是自己写,如下:颜色可以直接配置就好,好吧,鉴于我的审美观有限,貌似这种颜色搭配起来挺丑的。 ~~~ private void test1(HttpServletResponse response) throws IOException { int width=110; int height=25; //1构建一幅内存图片BufferedImage BufferedImage bi=new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); //3.图片上的画笔 Graphics g=bi.getGraphics(); g.setColor(Color.green); g.drawRect(0, 0, width, height); //画矩形边线 //4填充背景色 g.setColor(Color.gray); g.fillRect(1, 1, width, height); //5干扰线 g.setColor(Color.yellow); Random r=new Random(); for(int i=0;i<20;i++) g.drawLine(r.nextInt(width), r.nextInt(height),r.nextInt(width), r.nextInt(height)); //6数字验证码 g.setColor(Color.BLACK); g.setFont(new Font("宋体",Font.BOLD|Font.ITALIC,13));//加粗倾斜 int x=20; for(int i=0;i<4;i++){ g.drawString(r.nextInt(10)+"",x,20); x+=20; } //2输出响应对象的字节流输出流 ImageIO.write(bi, "jpg", response.getOutputStream()); } ~~~ 还有一种方法可以使用,开源框架validate.jar,导入这个jar包,两行代码就可以搞定,当然了,如果你想要更好的效果,你可以自己写或者看里面的.class中的源码,自己进行修改,这里就不在多废话了。 ~~~ //开源框架实现 ValidateCode vc=new ValidateCode(110, 25, 4, 30);//第一个参数是宽,第二个是高,第三个是验证码的数目,第四个是干扰线的条数 vc.write(response.getOutputStream()); ~~~ 最后,我们还需要清除一下浏览器的缓存,我们都知道,清除缓存当然是用那三种方法啦。 ~~~ //清除缓存 response.setIntHeader("Expires", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); ~~~ 哦?你还不满意,好吧,那我们再加一个定时刷新的功能吧! 第一种刷新方式是直接刷新: ~~~ private void test1(HttpServletResponse response) throws IOException { response.setIntHeader("Refresh", 1);//单位是秒 Random r = new Random(); response.getWriter().write(r.nextInt()+""); } ~~~ 当然,我们还可以来那种倒计时几秒钟就跳转到其他页面的这种效果, ~~~ //刷新到别处 private void test2(HttpServletResponse response) throws IOException { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("注册成功!2秒后自动转向登陆页面。"); response.setHeader("Refresh", "2;URL=/day08_response/login.html");//单位是秒 } ~~~ 下面再来说一下控制浏览器缓存时间的实现方法: ~~~ response.setDateHeader("Expires", System.currentTimeMillis()+1*60*60*1000);//取值是一个毫秒值。如果该值小于当前时间,则不缓存。 //如果大于当前时间,缓存的时间是:值-当前时间。 response.getWriter().write("hello"); ~~~ 好吧,基本上说完了,将以上知识整个起来就可以实现你的一个项目的功能了,快去试试吧!
';

win10+ubuntu双系统安装方案

最后更新于:2022-04-01 14:32:43

网上有很多教程,大多是win7,win8的,我折腾了一天,今天终于都安装好了,折腾的够呛,很多人都说挺简单的,嗯其实的确很简单,很多人回复说安装不成功,很有可能就是电脑安全权限的问题,我用的是华硕的电脑,采用u盘做启动盘安装。已经安装好了的是win10,现在加一个ubuntu15\.   (部分图片收集于网络) 一、到官网下载ubuntu15的iso镜像文件,大概1G多,放到电脑盘中 二、下载Ultraiso软碟通工具,下载完成后,插入U盘,使用Ultraiso把刚才下载的ubuntu镜像文件【写入硬盘影像】到U盘 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6f5b1a8.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6f94741.jpg) 写完后会有相应成功提示,成功后点击返回即可。 三、关机后,按电脑左面的小按钮就能进入BIOS,大多数笔记本是F12  F11 F10  F2什么什么的,有的电脑可能需要注意按Fn键。。没事= =在重启界面乱按。我的华硕电脑是多次按del键就进入了。进入后按照这篇教程设置u盘启动。[点击打开链接](http://jingyan.baidu.com/article/ed15cb1b7299061be2698143.html),非常重要。 四、接下来会进行正常的ubuntu的安装过程,这里就不一一介绍安装流程,反正就是那个套路。记住选择win10和ubuntu并存。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6fc42de.jpg) 我想说的是安装完成之后会有一个重启操作,我们会发现,重启之后进入的是我们之前的win10,并没有进入ubuntu15,这个时候真是一个让人疯狂的事情。 五、发现是win10后,我们进入win10中安装一个easybcd2.2,在这里面选择添加新条目,linux/grub自动等,(其实这个设置并没有什么卵用),我只是想通过这个设置来让下次开机时弹出以下界面。进入我用红圈的地方,选择u盘,一路进入之后,会发现有大概4个选项,选择试用ubuntu(我们之前已经安装过了,只是进不去,所以这里要再次试用,而不是安装) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d7027956.jpg) 六、进入试用之后就是一个ubuntu的界面了,这个时候桌面上面会有一个安装的图标,我们选择安装,我们会来到类似下图这样的一个界面(图片来自网络),我的有5个选项,我选择清除并再次安装(大概是第3个选项),这个时候等待安装完成就可以了。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6fc42de.jpg) 七、安装完成之后,我们就可以选择重启,这个时候就不会像我们之前安装的那样直接进入win10,这个时候会有4个选项在引导图这里,第一个是进入ubuntu,第二个是进入ubuntu(高级),这个我们一般不要点,第三个是windows  boot  manager,这个就是我们的win10系统啦,第四个是系统选项,我们也不用点。到这里为止,就可以愉快的使用我们的双系统啦,即选择地1和3进入就是我们的双系统。之后把我们不需要的东西都删除掉就可以了。 说明:方法虽然复杂了一点,但是都是博主亲自测试的安装过程,不同机型可能有细微差别,安装不同操作系统或许有可能不同,我相信有些人的方法肯定非常简单,而且可以很快弄好。 生命就在于折腾,折腾不止,青春犹在,各位如有其他好的解决办法欢迎留言一起交流学习哦!我的安装也参考了很多人的经验,结合起来就弄好了,非常感谢那些分享经验的人。
';

细说Http协议

最后更新于:2022-04-01 14:32:41

## 什么Http协议 HTTP是HyperText Transfer Protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程及数据本身的格式。  版本:1.1(目前使用的)和1.0  和1.1可以在一次TCP/IP链接上,发出多次请求和得到多次响应  1.0每次都必须建立新的TCP/IP链接,比较浪费资源。 基于HTTP协议的客户/服务器模式的信息交换过程,如图所示,它分四个过程,建立连接、发送请求信息、发送响应信息、关闭连接。 三次握手  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6ed1414.jpg) ## 请求部分 请求消息头:客户端给服务器端发送的一些附加信息。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6ef010a.jpg)  请求部分详解:  1、请求行:位于请求内容的第一行。  请求方式 请求的资源的URI地址 使用的协议及版本号  GET /app1/1.html HTTP/1.1 a、请求方式:  请求方式有:GET(默认的)、POST、HEAD、DELETE等  GET方式:/app1/1.html?username=abc&password=123。把表单的数据紧跟在URI地址后面,用?进行分割。问号后面的内容,专业叫法:查询字符串。请求行的长度不能超过1KB。不适合传递太多的数据。  POST方式:把表单的数据放在了请求的正文部分。没有长度限制。  b、请求的URI:  URI:统一资源标识符。[http://localhost:8080/app1/1.html](http://localhost:8080/app1/1.html)(URL),去掉协议、主机、端口剩余的叫做URI。  c、客户端使用的协议及版本。  2、请求消息头:  界限:从第2行开始,到第一个空行结束。   Accept:浏览器可接受的MIME类型 。  Accept:text/html.  MIME类型是区分网络上传输的数据类型的。MIME:大类型/具体类型。MIME类型和文件的扩展名有对应关系(Tomcat\conf\web.xml)  Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集   Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip  Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。 可以在浏览器中进行设置。  Host:初始URL中的主机和端口   Referer:取值是一个URL,该URL表示用户是由哪个页面过来的。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6f12d14.jpg)   Content-Type:内容类型:告知服务器,请求正文的MIME数据类型,默认值:application/x-www-form-urlencoded   if-Modified-Since: Wed, 02 Feb 2011 12:04:56 GMT利用这个头与服务器的文件进行比对,如果一致,则从缓存中直接读取文件。   User-Agent:浏览器类型.   Content-Length:表示请求消息正文的长度   Connection:表示是否需要持久连接。如果服务器看到这里的值为“Keep -Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接   Cookie:这是最重要的请求头信息之一 (会话管理时再讲)   Date:Date: Mon, 22 Aug 2011 01:55:39 GMT请求时间GMT 3、请求正文:只有表单的请求方式是POST方式时才会出现。  username=abc&password=123 参数都是:key=value的形式,多个参数之间用&进行分割。 ## 响应部分 响应消息头:服务器端给客户端发送的一些附加信息。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6f295fe.jpg)  3.1响应消息行:位于第一行  服务器端使用的协议及版本 响应码 响应码描述  HTTP/1.1 200 OK  a、响应码:  1XX~5XX:目前用到了40个左右。  记住常用的几个状态码:  200:一切正常  302/307:请求重定向  304:服务器的资源没有修改。  404:客户端访问的地址不存在  500:服务器端程序遇到异常 3.2响应消息头:(重点)   Location: [http://www.it315.org/index.jsp](http://www.it315.org/index.jsp)指示新的资源的位置  一般和302、307响应码一起使用,完成请求重定向。   Server:apache tomcat指示服务器的类型   Content-Encoding: gzip告知客户端服务器发送的请求正文内容的压缩编码   Content-Length: 80 告诉浏览器正文的长度   Content-Language: zh-cn服务发送的文本的语言   Content-Type: text/html; charset=GB2312服务器发送的内容的MIME类型   Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT文件的最后修改时间   Refresh: 1;url=[http://www.it315.org](http://www.it315.org/)指示客户端刷新频率。单位是秒  Content-Disposition: attachment; filename=aaa.zip指示客户端下载文件   Set-Cookie:SS=Q0=5Lb_nQ; path=/search服务器端发送的Cookie(会话管理)   Expires: -1   Cache-Control: no-cache (1.1)   Pragma: no-cache (1.0) 三个头一般一起使用,告诉客户端不要缓存内容   Connection: close/Keep-Alive   Date: Tue, 11 Jul 2000 18:23:51 GMT 3.3响应正文:  和在页面上点击右键“查看源码”看到的是一样的。 ## 三种禁止浏览器缓存的头字段 ~~~ // 不要 缓存的 三个 头 : //Cache-Control : no-cache // Pragma: no-cache // Expires: Thu, 01 Dec 1994 16:00:00 GMT (时间值 ) // 都是 用于 控制 浏览器 缓存的 , 当前 是要不缓存, 其 值 是 no-cache, no-cache,Thu, 01 Dec 1994 16:00:00 GMT 时间值 // 为什么 有三个 头 啊 ? // 如果 要 通知 浏览器 不缓存, 最好 的方式 这个三个 都设置 response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); // 设置 expires 的时候 需要 调用 这个 方法 : setDateHeader(java.lang.String name, long date) response.setDateHeader("Expires", -1); ~~~ ## 状态码所表示的意义 状态行  格式: HTTP版本号 状态码 原因叙述  举例:HTTP/1.1 200 OK  状态码用于表示服务器对请求的各种不同处理结果和状态,它是一个三位的十进制数。响应状态码分为5类,使用最高位为1到5来进行分类如下所示: **一、200状态码:**  成功2××: 成功处理了请求的状态码。  1、200 :服务器已成功处理了请求并提供了请求的网页。  2、204: 服务器成功处理了请求,但没有返回任何内容。  **二、300状态码:**  重定向3×× :每次请求中使用重定向不要超过 5 次。  1、301: 请求的网页已永久移动到新位置。当URLs发生变化时,使用301代码。搜索引擎索引中保存新的URL。  2、302: 请求的网页临时移动到新位置。搜索引擎索引中保存原来的URL。  3、304: 如果网页自请求者上次请求后没有更新,则用304代码告诉搜索引擎机器人,可节省带宽和开销。  **三、400状态码:**  客户端错误4×× :表示请求可能出错,妨碍了服务器的处理。  1、400: 服务器不理解请求的语法。  2、403: 服务器拒绝请求。  3、404: 服务器找不到请求的网页。服务器上不存在的网页经常会返回此代码。  4、410 :请求的资源永久删除后,服务器返回此响应。该代码与 404(未找到)代码相似,但在资源以前存在而现在不存在的情况下,有时用来替代404 页面代码。如果资源已永久删除,应当使用 301 指定资源的新位置。  **四、500状态码:**  服务器错误5×× :表示服务器在处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。  1、500 :服务器遇到错误,无法完成请求。  2、503: 服务器目前无法使用(由于超载或停机维护)。
';

新闻客户端案例开发

最后更新于:2022-04-01 14:32:38

新闻客户端,顾名思义就是看新闻用的客户端。 该新闻用到的知识模块有:android高级界面设计(Fragment、ViewPager),android网络通信(http通信),开源组件(xutils框架-HttpUtils模块、xutils框架-BitmapUtils模块),开源框架(library)。所需jar包:xUtils、gson、android-support-v4。 主界面滑动标签:library框架用于主界面标签 •主界面ViewPager:ViewPager与上部分的library框架结合做成Fragment动态效果 •ListView中的每个Item: •HttpUtils模块进行是用于进行访问网络,获取json数据 •BitmapUtils模块进行网络图片的加载和显示 •android-support-v4.jar包提供ViewPager控件 •library开源代码框架库,是用来实现简易新闻客户端上端的滑动标签,同时它与ViewPager控件结合最终实现的是[Fragment](http://blog.csdn.net/sdksdk0/article/details/Fragment%E4%BB%8B%E7%BB%8D.doc)的动态实现。 •简易新闻客户端上端滑动标签是用了library开源代码框架中的com.viewpagerindicator.TabPageIndicator控件 •ViewPager控件是android-support-v4.jar包中的android.support.v4.view.ViewPager控件。 •用了com.viewpagerindicator.TabPageIndicator这个控件之后,要对界面主题(Theme)进行修改,在styles.xml文件中创建相关的style 首先导入libary库:千万不要直接把库拷贝进项目文件夹中,这样可能会出现各种其他不知名的错误,建议通过Import Module方式一步步导入库文件。然后在file->project structure->dependencies->3Model dependency中选择library库就可以了,还要在2中导入一个gson.jar和xUtils.jar。此时库已成功导入。 我们首先来写布局文件,activity_main.xml ~~~ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <include android:id="@+id/header" android:layout_width="match_parent" android:layout_height="@dimen/header_height" layout="@layout/header"/> <com.viewpagerindicator.TabPageIndicator android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/header"> </com.viewpagerindicator.TabPageIndicator> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/indicator"> </android.support.v4.view.ViewPager> </RelativeLayout> ~~~ 第2个是头部header.xml ~~~ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/header_height" android:background="@mipmap/setting_iv_bg" android:gravity="center"> <TextView android:text="@string/header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="@dimen/header_text_size" /> </RelativeLayout> ~~~ 第三个是下面的viepage页面,fragment.xml ~~~ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@+id/newsList" android:layout_width="match_parent" android:layout_height="wrap_content"></ListView> </LinearLayout> ~~~ 以及细节部分fragment_list_item.xml ~~~ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/newIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:src="@mipmap/skyblue_logo_whatsapp_checked"/> <LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_toRightOf="@+id/newIcon" > <TextView android:id="@+id/newTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/newTitleTxtSize" /> <TextView android:id="@+id/newTime" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/newTimeTxtSize" /> </LinearLayout> </RelativeLayout> ~~~ 既然是一个新闻客户端那必须要有数据,那么数据当然就需要json解析来从第三方网站获取新闻数据,下面来简要介绍一下json数据的解析过程。 •JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C++、C#、Java、JavaScript、Perl、Python等)。这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。 这个时候我们需要在src中新建一个bean类来存放json的数据 ~~~ public class jsonBean { private String title; private String url; private String listimage; private String pubdate; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getListimage() { return listimage; } public void setListimage(String listimage) { this.listimage = listimage; } public String getPubdate() { return pubdate; } public void setPubdate(String pubdate) { this.pubdate = pubdate; } } ~~~ 然后再bean中再创建一个ListBean.java,用于存取多个新闻的内容,这里面需要两个内部类,通过data获取NewsList,然后通过NewsLis获取到NewsItem中的值。 ~~~ public class listBean { //变量名最好跟json中一样 public NewsList data; public static class NewsList{ //变量名最好跟json中一样 public ArrayList<NewsItem> news; } public static class NewsItem{ public String title; public String url; public String listimage; public String pubdate; }} ~~~ 接下来就需要访问网络了,新建一个utils包,再建一个NetUtils.java类 ~~~ public class NetUtils { //定义我们需要的变量 private HttpUtils httpUtils; //全局变量 private Gson gson=new Gson(); private List<News> newsList=new ArrayList<News>(); private News news; //获取新闻信息 public List<News> getNews(String url){ //访问网络是一个耗时操作,建议开启新线程 GetNewsRunnable getNewsRunnable=new GetNewsRunnable(httpUtils, gson, newsList, news, url); getNewsRunnable.run(); return newsList; } public void addAll(List<News> newsList){ this.newsList.addAll(newsList); } } ~~~ 新建一个GetNewsRunnable.java,请求网络。 ~~~ public class GetNewsRunnable implements Runnable{ private HttpUtils httpUtils; private Gson gson; private List<News> newsList; private News news; private String url; private NetUtils netUtils; public GetNewsRunnable(HttpUtils httpUtils,Gson gson,List<News> newsList,News news,String url){ this.httpUtils=httpUtils; this.gson=gson; this.newsList=newsList; this.news=news; this.url=url; netUtils=new NetUtils(); } //run方法中访问网络并解析json数据 public void run() { if(newsList==null){ newsList=new ArrayList<News>(); } httpUtils=new HttpUtils(); httpUtils.send(HttpMethod.GET, url, new RequestCallBack<String>() { public void onFailure(HttpException arg0, String arg1) { Log.e("failure", "访问失败"); } public void onSuccess(ResponseInfo<String> responseInfo) { Log.e("success", "访问成功"); //访问成功,就进行数据解析 NewsListBean newsListBean=gson.fromJson(responseInfo.result, NewsListBean.class); //for-each循环 for(NewsListBean.NewsItem newsItem:newsListBean.data.news){ news=new News(); news.setTitle(newsItem.title); news.setListimage(newsItem.listimage); news.setUrl(newsItem.url); news.setPubdate(newsItem.pubdate); newsList.add(news); netUtils.addAll(newsList); } } }); } } ~~~ 好了,我们现在需要添加一个适配器了,4个构造方法getCount()、getItem、getItemId、getView可以自动生成就可以了,在适配器中我们首先把变量写好 ~~~ //定义变量 private List<News> newsList; private LayoutInflater mInflater; //获取外部布局用的 private BitmapUtils mBitmapUtils; //加载和显示图片用的 ~~~ ~~~ public NewsListAdapter(Context context,List<News> newsList){ this.newsList=newsList; this.mInflater=LayoutInflater.from(context); mBitmapUtils=new BitmapUtils(context); } ~~~ ~~~ //真正存放东西的方法 @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder holder=null; if(convertView==null){//如果传进来的view为空,就进行创建,并填充内容 holder=new ViewHolder(); convertView=mInflater.inflate(R.layout.fragment_list_item, null); holder.newIcon=(ImageView) convertView.findViewById(R.id.newIcon); holder.newTime=(TextView) convertView.findViewById(R.id.newTime); holder.newTitle=(TextView) convertView.findViewById(R.id.newTitle); convertView.setTag(holder); }else{ holder=(ViewHolder) convertView.getTag(); } //将数据放到我们的控件当中 News news=newsList.get(position); holder.newTitle.setText(news.getTitle()); holder.newTime.setText(news.getPubdate()); mBitmapUtils.display(holder.newIcon, news.getListimage()); return convertView; } //模拟我们传递的控件 class ViewHolder{ ImageView newIcon; TextView newTitle; TextView newTime; } //把所有News加进List中 public void addListItem(List<News> list){ newsList.addAll(list); ~~~ 通过适配器将数据传送到listview中,之后我们要用到fragment, ~~~ //这里是ListView的滚动和点击事件 newsList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { // TODO Auto-generated method stub //获取新闻详细信息的地址 String descriptionUrl=newsDatas.get(position).getUrl(); //定义并实例化Intent Intent intent=new Intent(getActivity().getApplicationContext(),DescriptActivity.class); String name="url"; //将值放到intent当中 intent.putExtra(name, descriptionUrl); //开启Activity startActivity(intent); } }); ~~~ 然后我们添加一些详情页TabIndicatorAdapter 最后描述新闻详情 ~~~ public class DescriptActivity extends Activity { private WebView newsDescription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_descript); String url=this.getIntent().getExtras().get("url").toString(); newsDescription=(WebView) findViewById(R.id.newsDescription); /** * 适配屏幕 */ newsDescription.getSettings().setUseWideViewPort(true); newsDescription.getSettings().setLoadWithOverviewMode(true); newsDescription.setVerticalScrollBarEnabled(true); newsDescription.setHorizontalScrollBarEnabled(false); newsDescription.loadUrl(url); } } ~~~ 记得在清单文件中添加网络权限等,还有一些步骤就不一一细说了,具体的可以下载源码看一下。 ~~~ <uses-permission android:name="android.permission.INTERNET" /> ~~~ 最终效果图为: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6e9a21c.jpg)[点击打开链接](http://download.csdn.net/detail/sdksdk0/9446330) 源码下载地址:[点击打开链接](http://download.csdn.net/detail/sdksdk0/9446330)http://download.csdn.net/detail/sdksdk0/9446330   
';

深入解读XML解析

最后更新于:2022-04-01 14:32:36

## 一、XML是什么?有什么用? XML是指可扩展标记语言(eXtensible MarkupLanguage),它是一种标记语言。它被设计的宗旨是描述数据(XML),而非显示数据(HTML)。 目前遵循的是W3C组织于2000年发布的XML1.0规范 应用场景: 1、描述数据 2、作为配置文件存在 ## 二、XML的基本语法 ### 1、文档声明:很重要 在编写XML文档时,需要先使用文档声明来声明XML文档。且必须出现在文档的第一行。 作用:告知解析器,我是一个XML文档。 最简单的声明语法:     中间不要加空格,后面加?号 当我们写好的一个xml文件写入内存的时候会转换为二进制保存,这个时候会查码表,记事本保存的时候是gbk,而保存的时候默认查码表时用的是utf-8, 这个时候我们就可以用encoding属性:默认是UTF-8    ,这样就可以解决乱码等问题。 standlone属性:该xml文件是否独立存在。 ### 2、元素(标签) XML语法非常严格。不能够省略结束标签。 一个XML文档必须有且仅有一个根标签 XML中不会忽略主体内容中出现的空格和换行 元素(标签)的名称可以包含字母、数字、减号、下划线和英文句点,但必须遵守下面的一些规范: 1 严格区分大小写; 2 只能以字母或下划线开头;abc _abc 3 不能以xml(或XML、Xml等)开头----W3C保留日后使用; 4 名称字符之间不能有空格或制表符;ab 5 名称字符之间不能使用冒号; (有特殊用途)    ### 3、元素的属性 属性值一定要用引号(单引号或双引号)引起来 元素中属性不允许重复 ### 4、注释 XML中的注释语法为:这是注释--> XML声明之前不能有注释       不允许第一行写注释(不同于java) ### 5、CDATA区 Character Data:字符数据。 语法: <![CDATA[ 内容 ]]> 作用: 被CDATA包围的内容,都是普通的文本字符串。 ### 6、特殊字符 特殊字符       替代符号 &                    &amp <                  &lt >                  &gt "                    &quot '                     &apos ### 7、处理指令(PI:ProcessingInstruction)(了解) XML声明就是一种处理指令 处理指令: ~~~ "1.0" encoding="GBK"?>   "text/css" href="main.css"?>       中国       美国       小日本   ~~~ ## 三、XML的约束 XML可以自定义。如果作为配置文件。 格式良好的XML文档:遵循XML语法的。 有效的XML文档:遵守约束的XML文档。 有效的XML文档必定是格式良好的,但良好的不一定是有效的。 ### 1、DTD约束:(能看懂DTD即可) a、DTD(Document Type Definition):文档类型定义 作用:约束XML的书写规范 注意:dtd可以写在单独的文件中,扩展名是dtd,且必须使用UTF-8编码进行保存。 b、XML文档中如何导入DTD约束文档(XML外部) 1  dtd文档在本地: 2  dtd文档在网络上: c、了解:也可以把DTD的内容直接写在XML文档内部。 写在XML文档内部,dtd没有编码要求。(了解) ~~~ <?xml version="1.0" encoding="GBK"?> <!DOCTYPE 书架 [ <!ELEMENT 书架 (书+)> <!ELEMENT 书 (书名,作者,售价)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 售价 (#PCDATA)> <!ATTLIST 书 ISBN ID #REQUIRED COMMENT CDATA #IMPLIED 出版社 CDATA "指令汇公司" > <!ENTITY copyright "指令汇公司"> ]> <书架> <书 ISBN="a" COMMENT="ddd" 出版社="指令汇公司"> <书名>Java就业培训教程</书名> <作者>&copyright;</作者> <售价>39.00元</售价> </书> <书 ISBN="b"> <书名>JavaScript网页开发</书名> <作者>张孝祥</作者> <售价>28.00元</售价> </书> </书架> ~~~ 练习: ~~~ <?xml version="1.0" encoding="GBK"?> <!DOCTYPE TVSCHEDULE [ <!ELEMENT TVSCHEDULE (CHANNEL+)> <!ELEMENT CHANNEL (BANNER,DAY+)> <!ELEMENT BANNER (#PCDATA)> <!ELEMENT DAY (DATE,(HOLIDAY|PROGRAMSLOT+)+)> <!ELEMENT HOLIDAY (#PCDATA)> <!ELEMENT DATE (#PCDATA)> <!ELEMENT PROGRAMSLOT (TIME,TITLE,DESCRIPTION?)> <!ELEMENT TIME (#PCDATA)> <!ELEMENT TITLE (#PCDATA)> <!ELEMENT DESCRIPTION (#PCDATA)> <!ATTLIST TVSCHEDULE NAME CDATA #REQUIRED> <!ATTLIST CHANNEL CHAN CDATA #REQUIRED> <!ATTLIST PROGRAMSLOT VTR CDATA #IMPLIED> <!ATTLIST TITLE RATING CDATA #IMPLIED> <!ATTLIST TITLE LANGUAGE CDATA #IMPLIED> ]> <TVSCHEDULE NAME="NN"> <CHANNEL CHAN="CC"> <BANNER>AAA</BANNER> <DAY> <DATE>2015</DATE> <PROGRAMSLOT> <TIME>ee</TIME> <TITLE>bb</TITLE> <DESCRIPTION>cc</DESCRIPTION> </PROGRAMSLOT> </DAY> </CHANNEL> </TVSCHEDULE> ~~~ ### 2、Schema约束(新,有替换DTD的趋势) ## 四、利用Java代码解析XML文档 ### 1、解析方式 l  DOM:Document Object Model,文档对象模型。这种方式是W3C推荐的处理XML的一种标准方式。 缺点:必须读取整个XML文档,才能构建DOM模型,如果XML文档过大,造成资源的浪费。 优点:适合对XML中的数据进行操作(CRUD)。 l  SAX:Simple API for XML。这种方式不是官方标准,属于开源社区XML-DEV,几乎所有的XML解析器都支持它。 ### 2、解析工具 JAXP: DOM或SAX方式进行解析XML。API在JDK之中。 Dom4J:(推荐) 是开源组织推出的解析开发包。(牛,大家都在用,包括SUN公司的一些技术的实现都在用) ## 五、JAXP进行DOM方式解析XML基本练习 ### 1、JAXP简介: 开发包:(JDK中) DOM:W3C。org.w3c.dom.*   DOM规范。(接口/抽象类) SAX:开源组织。org.xml.sax.*  SAX规范。(接口/抽象类) JAXP:javax.xml.*  ### 2、利用JAXP进行DOM方式解析 ~~~ //JAXP进行DOM方式解析的基本操作 public class JaxpDemo1 { public static void main(String[] args) throws Exception { //得到解析器 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); //通过解析器就可以得到代表整个内存中XML的Document对象 Document document = builder.parse("src/book.xml"); test8(document); } // 1、得到某个具体的节点内容: 刘丰 private static void test1(Document document){ NodeList nl = document.getElementsByTagName("作者"); Node authorNode = nl.item(0); System.out.println(authorNode.getTextContent()); } // 2、遍历所有元素节点:打印元素的名称 private static void test2(Node node){ //确定node的类型 //方式一 // if(node.getNodeType()==Node.ELEMENT_NODE){ // //是元素 // } //方式二 if(node instanceof Element){ //是元素 Element e = (Element)node; System.out.println(e.getNodeName());//打印元素名称 } //判断有没有子节点 NodeList nl = node.getChildNodes(); for(int i=0;i<nl.getLength();i++){ Node n = nl.item(i); test2(n); } } // 3、修改某个元素节点的主体内容:<售价>39.00元</售价>--->10元 private static void test3(Document document) throws Exception{ //得到售价 Node priceNode = document.getElementsByTagName("售价").item(0); priceNode.setTextContent("10元"); //更新XML文件 TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(); //构建输入源: Source source = new DOMSource(document); //构建目标: Result result = new StreamResult("src/book.xml"); t.transform(source, result); } // 4、向指定元素节点中增加子元素节点:第一本书添加子元素 <出版社>黑马程序员</出版社> private static void test4(Document document) throws Exception{ //创建:<出版社>黑马程序员</出版社> Element e = document.createElement("出版社"); e.setTextContent("黑马程序员"); //得到书,把新节点挂上去 Node bookNode = document.getElementsByTagName("书").item(0); bookNode.appendChild(e); //更新XML文件 TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(); //构建输入源: Source source = new DOMSource(document); //构建目标: Result result = new StreamResult("src/book.xml"); t.transform(source, result); } // 5、向指定元素节点上增加同级元素节点:第一本书<售价>前面添加<批发价>30</批发价> private static void test5(Document document) throws Exception{ //创建新节点 Element e = document.createElement("批发价"); e.setTextContent("30元"); //找到<售价> Node priceNode = document.getElementsByTagName("售价").item(0); //父标签:调用insertBefore(新节点,参考节点); Node bookNode = priceNode.getParentNode(); bookNode.insertBefore(e, priceNode); //更新XML文件 TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(); //构建输入源: Source source = new DOMSource(document); //构建目标: Result result = new StreamResult("src/book.xml"); t.transform(source, result); } // 6、删除指定元素节点:删除批发价 private static void test6(Document document) throws Exception{ Node priceNode = document.getElementsByTagName("批发价").item(0); priceNode.getParentNode().removeChild(priceNode); //更新XML文件 TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(); //构建输入源: Source source = new DOMSource(document); //构建目标: Result result = new StreamResult("src/book.xml"); t.transform(source, result); } // 7、操作XML文件属性:书籍添加一个属性:ISBN=“ABC” private static void test7(Document document) throws Exception{ Node bookNode = document.getElementsByTagName("书").item(0); if(bookNode instanceof Element){ Element e = (Element)bookNode; e.setAttribute("ISBN", "ABC"); } //更新XML文件 TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(); //构建输入源: Source source = new DOMSource(document); //构建目标: Result result = new StreamResult("src/book.xml"); t.transform(source, result); } // 8、操作XML文件属性:获取ISBN=“ABC” private static void test8(Document document) throws Exception{ Node bookNode = document.getElementsByTagName("书").item(0); if(bookNode instanceof Element){ Element e = (Element)bookNode; System.out.println(e.getAttribute("ISBN")); } } } ~~~ ### 3、DOM小案例 a、建立xml文件 ~~~ <?xml version="1.0" encoding="UTF-8" standalone="no"?><exam> <student examid="222" idcard="111"> <name>刘丰</name> <location>湖北</location> <grade>100</grade> </student> <student examid="dsf" idcard="2342"><name>dsf</name><location>435</location><grade>654.0</grade></student></exam> ~~~ b、代码要精细。要分层。 DAO:com.zhilinghui.dao VIEW:com.zhilinghui.view JavaBean:com.zhilinghui.domain(领域) c、设计JavaBean ~~~ public class Student { private String idcard; private String examid; private String name; private String location; private float grade; public Student(){} public Student(String idcard, String examid, String name, String location, float grade) { super(); this.idcard = idcard; this.examid = examid; this.name = name; this.location = location; this.grade = grade; } public String getIdcard() { return idcard; } public void setIdcard(String idcard) { this.idcard = idcard; } public String getExamid() { return examid; } public void setExamid(String examid) { this.examid = examid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public float getGrade() { return grade; } public void setGrade(float grade) { this.grade = grade; } @Override public String toString() { return "Student [idcard=" + idcard + ", examid=" + examid + ", name=" + name + ", location=" + location + ", grade=" + grade + "]"; } } ~~~ d、开发DAO 数据访问对象 ~~~ public class StudentDao { /** * 保存学生信息到XML文件中 * @param student 封装要保存的信息 * @return 成功返回true,否则false * @throws Exception */ public boolean save(Student student) throws Exception{ if(student==null) throw new IllegalArgumentException("学生参数不能为null"); boolean result = false; /* * <student idcard="111" examid="222"> <name>刘丰</name> <location>湖北</location> <grade>100</grade> </student> */ //得到Document Document document = JaxpUtil.getDocument(); //创建一个student元素:设置属性 Element studentE = document.createElement("student");//<student></student> studentE.setAttribute("idcard", student.getIdcard()); studentE.setAttribute("examid", student.getExamid());//<student idcard="111" examid="222"></student> //创建name,location,grade元素,挂到student上 Element nameE = document.createElement("name"); nameE.setTextContent(student.getName());//<name>刘丰</name> Element locationE = document.createElement("location"); locationE.setTextContent(student.getLocation());//<location>湖北</location> Element gradeE = document.createElement("grade"); gradeE.setTextContent(student.getGrade()+"");//<grade>100</grade> studentE.appendChild(nameE); studentE.appendChild(locationE); studentE.appendChild(gradeE); //把student挂接到exam上 Node examNode = document.getElementsByTagName("exam").item(0); examNode.appendChild(studentE); //写到xml中 JaxpUtil.wirte2xml(document); //更改result的取值为true result = true; return result; } /** * 根据姓名删除信息 * @param name * @return 成功返回true,否则false */ public boolean delete(String name){ boolean result = false; try { Document document = JaxpUtil.getDocument(); //得到所有的name元素 NodeList nl = document.getElementsByTagName("name"); //遍历:比对文本内容是否和参数一样 for(int i=0;i<nl.getLength();i++){ if(nl.item(i).getTextContent().equals(name)){ //如果找到了一样的:爷爷干掉爸爸 nl.item(i).getParentNode().getParentNode().removeChild(nl.item(i).getParentNode()); //写回xml JaxpUtil.wirte2xml(document); break; } } result = true; } catch (Exception e) { throw new RuntimeException(e);//异常转译 } return result; } /** * 根据准考证号查询学生信息 * @param examid * @return 没有返回null */ public Student findByExamId(String examid){ Student student = null; try { Document document = JaxpUtil.getDocument(); //得到所有的student元素 NodeList nl = document.getElementsByTagName("student"); //遍历:比对examid属性 for(int i=0;i<nl.getLength();i++){ Element e = (Element) nl.item(i); if(e.getAttribute("examid").equals(examid)){ // 找到了:创建student对象,并设置相应的值 student = new Student(); student.setIdcard(e.getAttribute("idcard")); student.setExamid(examid); student.setName(e.getElementsByTagName("name").item(0).getTextContent()); student.setLocation(e.getElementsByTagName("location").item(0).getTextContent()); student.setGrade(Float.parseFloat(e.getElementsByTagName("grade").item(0).getTextContent())); break; } } } catch (Exception e) { throw new RuntimeException(e);//异常转译 } return student; } } ~~~ e、测试DAO的功能 ~~~ public class StudentDaoTest { public static void main(String[] args) { StudentDao dao = new StudentDao(); // Student student = new Student(); // student.setIdcard("333"); // // dao.save(student); // Student s = dao.findByExamId("444"); // System.out.println(s); System.out.println(dao.delete("阿娇")); } } ~~~ f、开发界面 ~~~ ublic class Main { public static void main(String[] args) throws Exception { StudentDao dao = new StudentDao(); System.out.println("a、添加用户\tb、查询成绩\tc、删除用户"); System.out.println("请输入操作类型:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String op = br.readLine();//读取用户输入的a|b|c if("a".equals(op)){ //添加 System.out.println("请输入学生姓名:"); String name = br.readLine(); System.out.println("请输入学生准考证号:"); String examid = br.readLine(); System.out.println("请输入学生身份证号:"); String idcard = br.readLine(); System.out.println("请输入学生所在地:"); String location = br.readLine(); System.out.println("请输入学生成绩:"); String grade = br.readLine(); //封装数据 Student student = new Student(idcard, examid, name, location, Float.parseFloat(grade)); //调用dao boolean b = dao.save(student); if(b){ System.out.println("------添加成功------"); }else{ System.out.println("------服务器忙------"); } }else if("b".equals(op)){ //查询 System.out.println("请输入学生准考证号:"); String examid = br.readLine(); Student s = dao.findByExamId(examid); if(s==null) System.out.println("------查无此人------"); else System.out.println(s); }else if("c".equals(op)){ //删除 System.out.println("请输入要删除的学生姓名:"); String name = br.readLine(); boolean b = dao.delete(name); if(b){ System.out.println("------删除成功------"); }else{ System.out.println("------服务器忙------"); } }else{ System.out.println("你傻呀,输错了"); } } } ~~~ ## sax解析原理 在使用 DOM 解析 XML 文档时,需要读取整个 XML 文档,在内存中构架代表整个 DOM 树的Doucment对象,从而再对XML文档进行操作。此种情况下,如果 XML 文档特别大,就会消耗计算机的大量内存,并且容易导致内存溢出。 SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档装载完才会文档进行操作。 SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器: 解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。 解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。 事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理 ### 基本解析操作 ~~~ //1解析器 SAXParser parse = SAXParserFactory.newInstance().newSAXParser(); //2获取xml读取器 XMLReader reader = parse.getXMLReader(); //3注册内容处理器 reader.setContentHandler(new ContentHandler1()); //4读取xml文档 reader.parse("src/book.xml"); ~~~ ### 封装读取书 封装到BOOK.java ~~~ public class sax3 { //封装读取书 public static void main(String[] args) throws Exception { SAXParser parse=SAXParserFactory.newInstance().newSAXParser(); XMLReader reader=parse.getXMLReader(); final List<Book> books=new ArrayList<Book>(); reader.setContentHandler(new DefaultHandler(){ private Book b=null; private String currentTagName=null; public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if("书".equals(qName)){ b=new Book(); } currentTagName=qName; } public void endElement(String uri, String localName, String qName) throws SAXException { if("书".equals(qName)){ books.add(b); b=null; } currentTagName=null; } @Override public void characters(char[] ch, int start, int length) throws SAXException { if("书名".equals(currentTagName)){ b.setName(new String(ch,start,length)); } if("作者".equals(currentTagName)){ b.setAuthor(new String(ch,start,length)); } if("售价".equals(currentTagName)){ b.setPrice(new String(ch,start,length)); } } }); reader.parse("src/book.xml"); for(Book book:books) System.out.println(book); } } ~~~ ## dom4j解析原理 Dom4j是一个简单、灵活的开放源代码的库。Dom4j是由早期开发JDOM的人分离出来而后独立开发的。与JDOM不同的是,dom4j使用接口和抽象基类,虽然Dom4j的API相对要复杂一些,但它提供了比JDOM更好的灵活性。 Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点。现在很多软件采用的Dom4j,例如Hibernate,包括sun公司自己的JAXM也用了Dom4j。 使用Dom4j开发,需下载dom4j相应的jar文件。 1、基本练习 a、拷贝jar包: 把dom4j-1.6.1.jar加入到你的classpath中 b、基本操作 // 1、得到某个具体的节点内容:金瓶梅 ~~~ @Test public void test1() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); List<Element> bookElements = root.elements(); // Element bookName = (Element) bookElements.get(0).elements().get(0); // System.out.println(bookName.getText()); System.out.println(bookElements.get(0).elementText("书名")); } ~~~ // 2、遍历所有元素节点:名称 ~~~ @Test public void test2()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); treeWalk(root); } public void treeWalk(Element rootElement){//递归 System.out.println(rootElement.getName()); int nodeCount = rootElement.nodeCount();//子节点的数量 for(int i=0;i<nodeCount;i++){ Node node = rootElement.node(i);//得到一个子节点 if(node instanceof Element){ treeWalk((Element)node); } } } ~~~ // 3、修改某个元素节点的主体内容:10元---20 ~~~ @Test public void test3()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); //得售价 Element priceElement = root.element("书").element("售价"); priceElement.setText("21元"); //写回XML文档 // OutputFormat format = OutputFormat.createCompactFormat();//去除空格回车换行,适合运行期间 OutputFormat format = OutputFormat.createPrettyPrint();//漂亮的格式 默认编码是UTF-8 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book.xml"), format); writer.write(document); writer.close(); } ~~~ // 4、向指定元素节点中增加子元素节点:黑马程序员 ~~~ @Test public void test4()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); //得售价 Element bookElement = root.element("书"); //创建新元素 Element publisherElement = DocumentHelper.createElement("出版社"); publisherElement.setText("黑马程序员"); bookElement.add(publisherElement); //写回XML文档 // OutputFormat format = OutputFormat.createCompactFormat();//去除空格回车换行,适合运行期间 OutputFormat format = OutputFormat.createPrettyPrint();//漂亮的格式 默认编码是UTF-8 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book.xml"), format); writer.write(document); writer.close(); } ~~~ // 5、向指定元素节点上增加同级元素节点:21元 添加 ~~~ @Test public void test5()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); //得售价 Element bookElement = root.element("书"); //创建新元素 Element priceElement = DocumentHelper.createElement("批发价"); priceElement.setText("30元"); List<Element> bookChildren = bookElement.elements();//得到书的子元素 bookChildren.add(2, priceElement); //写回XML文档 // OutputFormat format = OutputFormat.createCompactFormat();//去除空格回车换行,适合运行期间 OutputFormat format = OutputFormat.createPrettyPrint();//漂亮的格式 默认编码是UTF-8 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book.xml"), format); writer.write(document); writer.close(); } ~~~ // 6、删除指定元素节点:批发价 ~~~ @Test public void test6()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); Element priceElement = root.element("书").element("批发价"); priceElement.getParent().remove(priceElement); //写回XML文档 // OutputFormat format = OutputFormat.createCompactFormat();//去除空格回车换行,适合运行期间 OutputFormat format = OutputFormat.createPrettyPrint();//漂亮的格式 默认编码是UTF-8 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book.xml"), format); writer.write(document); writer.close(); } ~~~ // 7、操作XML文件属性 ~~~ @Test public void test7()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); Element book = root.element("书"); System.out.println(book.attributeValue("ISBN")); } @Test public void test8()throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); //首先要得到根元素 Element root = document.getRootElement(); Element book = root.element("书"); book.addAttribute("A", "B"); //写回XML文档 // OutputFormat format = OutputFormat.createCompactFormat();//去除空格回车换行,适合运行期间 OutputFormat format = OutputFormat.createPrettyPrint();//漂亮的格式 默认编码是UTF-8 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book.xml"), format); writer.write(document); writer.close(); } ~~~ ## Xpath XPath是一个努力为XSL转换XSLT和XPointer [ ] [ ]之间共享一个共同的XPointer功能语法和语义的结果。它的主要目的是解决一个XML XML文档部分[ ]。为了支持这一功能,还提供用于处理字符串的基本设施、数字和布尔值。XPath使用一个紧凑的、非XML语法方便使用在uri和XML属性值的XPath。XPath操作基于XML文档的逻辑结构,而不是其表面的语法。Xpath的名字来自其使用的符号在URL路径通过一个XML文档的层次结构导航。 除了用于定位,XPath还设计有一个真子集,可用于匹配(测试一个节点是否符合一个模式);使用XPath进行XSLT。 XPath模型的XML文档的节点树。有不同类型的节点,包括元素节点、属性节点和文本节点。XPath定义了一个方法来计算每个节点类型字符串值。某些类型的节点也有名字。XPath完全支持XML命名空间的XML名称] [。因此,一个节点的名称被建模为一个地方的部分和一个可能的空命名空间URI;这就是所谓的扩展名。在[ 5数据模型]中详细描述了数据模型。 ~~~ @Test//Xpath public void test11() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); Node n = document.selectSingleNode("//书[1]/书名"); System.out.println(n.getText()); } @Test//Xpath:第一本书的ISBN的值 public void test12() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read("src/book.xml"); Node n = document.selectSingleNode("//书[1]"); System.out.println(n.valueOf("@ISBN")); } ~~~ ## xml约束之schema XML Schema 也是一种用于定义和描述 XML 文档结构与内容的模式语言,其出现是为了克服 DTD 的局限性 XML Schema 文件自身就是一个XML文件,但它的扩展名通常为.xsd。支持名称空间。 一个XML Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的xml文件称之为实例文档。 和XML文件一样,一个XML Schema文档也必须有一个根结点,但这个根结点的名称为schema。 编写了一个XML Schema约束文档后,通常需要把这个文件中声明的元素绑定到一个URI地址上,在XML Schema技术中有一个专业术语来描述这个过程,即把XML Schema文档声明的元素绑定到一个名称空间上,以后XML文件就可以通过这个URI(即名称空间)来告诉解析引擎,xml文档中编写的元素来自哪里,被谁约束。 学习目标:不需要我们编写xsd 重点:根据xsd编写出xml文档。 难点:在xml中引入xsd约束 ## 基本操作步骤: a、根据xsd文件,找到根元素 ~~~ <?xml version="1.0" encoding="UTF-8"?> <书架> </书架> ~~~ b、根元素来在哪个名称空间 使用xmlns关键字来声明名称空间。 ~~~ <?xml version="1.0" encoding="UTF-8"?> <itheima:书架 xmlns:tf="http://www.zhilinghui.com"> </itheima:书架> ~~~ c、名称空间和哪个xsd文件对应 ~~~ <?xml version="1.0" encoding="UTF-8"?> <tf:书架 xmlns:tf="http://www.zhilinghui.com" schemaLocation="http://www.zhilinghui.com book.xsd"> </itheima:书架> ~~~ d、schemaLocation来自一个标准的名称空间:固定写法 ~~~ <?xml version="1.0" encoding="UTF-8"?> <tf:书架 xmlns:tf="http://www.zhilinghui.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.zhilinghui.com book.xsd"> </itheima:书架> ~~~
';

html5全解析

最后更新于:2022-04-01 14:32:34

htm是软件开发中非常基础的知识,也是很重要的知识,在web中是很重要的知识点,在此梳理一下主要内容: ## 1、HTML是什么? 全称为HyperText Markup Language,超文本标记语言,非编程语言。 XHTML:功能和HTML差不多,但书写要求比HTML严格。 dhtml:动态页面:js+csss+html=dhtml ## 2、HTML的基本架构? 以下 HTML Source Code 便是一份 HTML 文件的基本架构 : ~~~ <HTML> <HEAD> <TITLE> 网页的标题 </TITLE> </HEAD> <BODY> 网页的内容,很多标记都作用于此 </BODY> </HTML> ~~~ 特点解说: * 整份文件处于标记与之间。  用以声明这是 HTML 文件,让浏览器认出并正确处理此 HTML 文件。 * 文件分两部分,由至称为开头,至称本文。  基本上两者各有适用的标记,如只可出现于开头部分。 * 开头部分用以存载重要资讯,而只有本文部分会被显示。  所以大部分标记会运用于本文部分。 * 所标示的是文件的标题。  会出现于浏览器顶部及为别人 Bookmark 时的名称,所以每页有不同而明确的标题 是需要的。 上述标记中只有具参数设定。 ~~~ ■ <BODY> 之参数设定: ~~~ 例子: <BODY text="#000000" link="#0000FF" alink="#FF0000" vlink="#0000FF" background="bg1.gif" bgcolor="#FFFFFF" leftmargin=2 topmargin=2 bgproperties="fixed"> * text="#000000"  用以设定文字颜色。 #000000 代表黑色,亦可以采用颜色的名称,即 text="black" 。 * link="#0000FF"  设定一般文字连结颜色。 * alink="#FF0000"  设定刚按下时文字连结颜色。 * vlink="#0000FF"  设定连结后的颜色。(被按过)。 * background="bg1.gif"  设定背景墙纸。GIF 或 JPEG 皆可,可以是绝对途径或相对途径。 * bgcolor="#FFFFFF"  设定背景颜色。当己设定背景墙纸时会失去作用,除非墙纸有透明部分。 * leftmargin=2  设定整份文件显示画面的左方边沿空间,单位为像素。 『只适用于IE』 * topmargin=2  设定整份文件显示画面的上方边沿空间。 『只适用于IE』 * bgproperties="fixed"  固定背景墙纸,当卷动文字时墙纸不会跟著卷动。 『只适用于IE』 标记及参数之字母大小都可以。   ## 3、编写规范 a、写法 ·        任何标记皆由""及">"所围住,如 p> ·        标记名与小于号之间不能留有空白字符。 ·        某些标记 要加上参数,某些则不必。如 size="+2">Hello ·        参数只可加于起始标记中。 ·        在起始标记之标记名前加上符号"/"便是其终结标记,如 /font> ·        标记字母大小写皆可。建议全部小写 b、HTML标记的分类 b.1围堵标记:aaaa b.1空白标记:aaaa ## 4、常用的HTML标记 ### 排版标记:  < !--注解-- >;< !--由这处开始是产品订购表格-- > < P > ;段落标记:< p align="center" > < BR > ;换行 < HR > ; 水平线 < HR align="LEFT" size="2" width="70%" color="#0000FF" noshade > < CENTER > ;居中标记 < PRE > ;预设格式标记 < DIV > ;区隔标记。作用:设定字、画、表格等的摆放位置。 < NOBR > ;< NOBR >称为不折行标记。作用:令某些文字不因太长而绕行,一 显示于同一行或下一 行。它对住址、数学算式、一行数字、程式码等尤为有用。 < WBR > ; < WBR >称为建议折行标记。作用:预设折行部位。 ### 字体标记:  < h1 >..< h6 >h1最大字体 < b ></b> < storong >< /storong > :加粗 < i >< /i > :斜体 < em >< /em >:定义着重文字 < u >< /u >:下划线 < s >< /s >:中划线、删除线 < sup >:上标 < sub >:下标 < font > :字体大小 < font face="Arial" size="+2" color="#008000">Creation of Webpage</font > ### 图像标记: < img src="logo.gif" width=100 height=100 hspace=5 vspace=5 border=2 align="top" alt="Logo of PenPals Garden" lowsrc="pre_logo.gif" > ### 清单标记: < ol > < ul > < dl >< li >< dt >< dd > 无序清单:ul < UL type="square" > 有序清单:ol < ol type="i" start="4"></ol > 自定义清单:dl dt dd ### 表格标记: ~~~ <TABLE> <TR> <TD> : ~~~ 这三个标记是定义表格的最重要的标记,可以说只学这三个己足够。 < TABLE >是一个容器标记,意思是说它用以声明这是表格而且其他表格标记只能在他的 范围内才适用,属容器标记的还有其他。 < TR >用以标示表格列( row ) < TD >用以标示储存格( cell ) <TABLE> 的参数设定(常用): 例如: < table width="400" border="1" cellspacing="2" cellpadding="2" align="CENTER" valign="TOP" background="myweb.gif" bgcolor="#0000FF" bordercolor="#FF00FF" bordercolorlight="#00FF00" bordercolordark="#00FFFF" cols="2" > width="400" 表格宽度,接受绝对值(如 80)及相对值(如 80%)。 border="1" 表格边框厚度,不同浏览器有不同的内定值,故请指明。 cellspacing="2" 表格格线厚度,请看例子三,那是加厚到 5 的格线。 cellpadding="2" 文字与格线的距离,请看例子四,那是加至 10 的 padding。 align="CENTER" 表格的摆放位置(水平),可选值为: left, right, center,请看例子五或六,那表格 是放于中间的,为怕一些浏览器不支援,故特加上居中标记<CENTER>,只是多 层保证而己,当然只用<CENTER>亦可。 valign="TOP". 表格内字画等的摆放贴 位置(垂直),可选值为: top, middle, bottom。 background="myweb.gif" 表格 纸,与 bgcolor 不要同用。 bgcolor="#0000FF" 表格底色,与 background 不要同用,请看例子六。 bordercolor="#FF00FF" 表格边框颜色,NC 与 IE 有不同的效果,请看例子六。 bordercolorlight="#00FF00" 表格边框向光部分的颜色,请看例子二。『只适用于 IE』 bordercolordark="#00FFFF" 表格边框背光部分的颜色,请看例子二,使用 bordercolorlight 或 bordercolordark 时 bordercolor 将会失效。『只适用于 IE』 cols="2" 表格栏位数目,只是让浏览器在下载表格是先画出整个表格而己。 < TR > 的参数设定(常用): 例如:<tr align="RIGHT" valign="MIDDLE" bgcolor="#0000FF" bordercolor="#FF00FF" bordercolorlight="#808080" bordercolordark="#FF0000"> align="RIGHT" 该一列内字画等的摆放贴 位置(水平),可选值为: left, center, right。 valign="MIDDLE" 该一列内字画等的摆放贴 位置(垂直),可选值为: top, middle, bottom。 bgcolor="#0000FF" 该一列底色,请看例子五。 bordercolor="#FF00FF" 该一列边框颜色,请看例子三。『只适用于 IE』 bordercolorlight="#808080" 该一列边框向光部分的颜色,请看例子三。『只适用于 IE』 bordercolordark="#FF0000" 该一列边框背光部分的颜色,请看例子三,使用 bordercolorlight 或 bordercolordark 时 bordercolor 将会失效。『只适用于 IE』 <TD> 的参数设定(常用): 例如:<td width="48%" height="400" colspan="5" rowspan="4" align="RIGHT" valign="BOTTOM" bgcolor="#FF00FF" bordercolor="#808080" bordercolorlight="#FF0000" bordercolordark="#00FF00" background="myweb.gif"> width="48%" 该一储存格宽度,接受绝对值(如 80)及相对值(如 80%)。 height="400" 该一储存格高度。 colspan="5" 该一储存格向右打通的栏数。请看例子六 rowspan="4" 该一储存格向下打通的列数。请看例子六 align="RIGHT" 该一储存格内字画等的摆放贴 位置(水平),可选值为: left, center, right。 valign="BOTTOM" 该一储存格内字画等的摆放贴 位置(垂直),可选值为: top, middle, bottom。 bgcolor="#FF00FF" 该一储存格底色,请看例子四。 bordercolor="#808080" 该一储存格边框颜色,请看例子三。『只适用于 IE』 bordercolorlight="#FF0000" 该一储存格边框向光部分的颜色,请看例子三。『只适用于 IE』 bordercolordark="#00FF00" 该一储存格边框背光部分的颜色,请看例子三,使用 bordercolorlight 或 bordercolordark 时 bordercolor 将会失效。『只适用于 IE』 background="myweb.gif" 该一储存格 纸,与 bgcolor 任用其一。
';

数据结构与算法,每日一道

最后更新于:2022-04-01 14:32:32

【程序1】  题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?  1.程序分析: 兔子的规律为数列1,1,2,3,5,8,13,21....  【程序2】  题目:判断101-200之间有多少个素数,并输出所有素数。  1.程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除,  则表明此数不是素数,反之是素数。  【程序3】  题目:打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个"水仙花数",因为153=1的三次方+5的三次方+3的三次方。  1.程序分析:利用for循环控制100-999个数,每个数分解出个位,十位,百位。  【程序4】  题目:将一个正整数分解质因数。例如:输入90,打印出90=2 * 3 * 3 * 5。  程序分析:对n进行分解质因数,应先找到一个最小的质数k,然后按下述步骤完成:  (1)如果这个质数恰等于n,则说明分解质因数的过程已经结束,打印出即可。  (2)如果n (3)如果n不能被k整除,则用k+1作为k的值,重复执行第一步。  【程序5】  题目:利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。  1.程序分析:(a>b)?a:b这是条件运算符的基本例子。  【程序6】  题目:输入两个正整数m和n,求其最大公约数和最小公倍数。  1.程序分析:利用辗除法。  【程序7】  题目:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。  1.程序分析:利用while语句,条件为输入的字符不为'\n'.  【程序8】  题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。  1.程序分析:关键是计算出每一项的值。  【程序9】  题目:一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3.编程 找出1000以内的所有完数。  【程序10】  题目:一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在 第10次落地时,共经过多少米?第10次反弹多高?  【程序11】  题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?  1.程序分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。  【程序12】  题目:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%;60万到100万之间时,高于60万元的部分,可提成1.5%,高于100万元时,超过100万元的部分按1%提成,从键盘输入当月利润I,求应发放奖金总数?  1.程序分析:请利用数轴来分界,定位。注意定义时需把奖金定义成长整型。  【程序13】  题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?  1.程序分析:在10万以内判断,先将该数加上100后再开方,再将该数加上268后再开方,如果开方后的结果满足如下条件,即是结果。请看具体分析:  【程序14】  题目:输入某年某月某日,判断这一天是这一年的第几天?  1.程序分析:以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本年的第几天,特殊情况,闰年且输入月份大于3时需考虑多加一天。  【程序15】  题目:输入三个整数x,y,z,请把这三个数由小到大输出。  1.程序分析:我们想办法把最小的数放到x上,先将x与y进行比较,如果x>y则将x与y的值进行交换,然后再用x与z进行比较,如果x>z则将x与z的值进行交换,这样能使x最小。  【程序16】  题目:输出9 * 9口诀。  1.程序分析:分行与列考虑,共9行9列,i控制行,j控制列。  【程序17】  题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下 的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。  1.程序分析:采取逆向思维的方法,从后往前推断。  【程序18】  题目:两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。  1.程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除, 则表明此数不是素数,反之是素数。  【程序19】  题目:打印出如下图案(菱形)  *  ***  ******  ********  ******  ***  *  1.程序分析:先把图形分成两部分来看待,前四行一个规律,后三行一个规律,利用双重 for循环,第一层控制行,第二层控制列。  【程序20】  题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。  1.程序分析:请抓住分子与分母的变化规律。  【程序21】  题目:求1+2!+3!+...+20!的和  1.程序分析:此程序只是把累加变成了累乘。  【程序22】  题目:利用递归方法求5!。  1.程序分析:递归公式:fn=fn_1 * 4!  【程序23】  题目:有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?  1.程序分析:利用递归的方法,递归分为回推和递推两个阶段。要想知道第五个人岁数,需知道第四人的岁数,依次类推,推到第一人(10岁),再往回推。  【程序24】  题目:给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。  【程序25】  题目:一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。  【程序26】  题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续 判断第二个字母。  1.程序分析:用情况语句比较好,如果第一个字母一样,则判断用情况语句或if语句判断第二个字母。  【程序27】  题目:求100之内的素数  【程序28】  题目:对10个数进行排序  1.程序分析:可以利用选择法,即从后9个比较过程中,选择一个最小的与第一个元素交换, 下次类推,即用第二个元素与后8个进行比较,并进行交换。  【程序29】  题目:求一个3*3矩阵对角线元素之和  1.程序分析:利用双重for循环控制输入二维数组,再将a[i][i]累加后输出。  【程序30】  题目:有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。  1\. 程序分析:首先判断此数是否大于最后一个数,然后再考虑插入中间的数的情况,插入后此元素之后的数,依次后移一个位置。  【程序31】  题目:将一个数组逆序输出。  1.程序分析:用第一个与最后一个交换。  【程序32】  题目:取一个整数a从右端开始的4~7位。  程序分析:可以这样考虑:  (1)先使a右移4位。  (2)设置一个低4位全为1,其余全为0的数。可用~(~0 (3)将上面二者进行&运算。  【程序33】  题目:打印出杨辉三角形(要求打印出10行如下图)  1.程序分析:  1  1 1  1 2 1  1 3 3 1  1 4 6 4 1  1 5 10 10 5 1  【程序34】  题目:输入3个数a,b,c,按大小顺序输出。  1.程序分析:利用指针方法。  【程序35】  题目:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。  【程序36】  题目:有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数  【程序37】  题目:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。  【程序38】  题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。  【程序39】  题目:编写一个函数,输入n为偶数时,调用函数求1/2+1/4+...+1/n,当输入n为奇数时,调用函数1/1+1/3+...+1/n(利用指针函数)  【程序40】  题目:字符串排序。  【程序41】  题目:海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子凭据分为五份,多了一个,这只猴子把多的一个扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一个,它同样把多的一个扔入海中,拿走了一份,第三、第四、第五只猴子都是这样做的,问海滩上原来最少有多少个桃子?  【程序42】  题目:809 * ??=800 * ??+9 * ??+1 其中??代表的两位数,8*??的结果为两位数,9*??的结果为3位数。求??代表的两位数,及809 * ??后的结果。  【程序43】  题目:求0—7所能组成的奇数个数。  【程序44】  题目:一个偶数总能表示为两个素数之和。  【程序45】  题目:判断一个素数能被几个9整除  【程序46】  题目:两个字符串连接程序  【程序47】  题目:读取7个数(1—50)的整数值,每读取一个值,程序打印出该值个数的*。  【程序48】  题目:某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密规则如下:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。  【程序49】  题目:计算字符串中子串出现的次数  【程序50】  题目:有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成绩),计算出平均成绩,况原有的数据和计算出的平均分数存放在磁盘文件"stud"中。 虽然可以用其他语言实现,但是这里要求用java实现
';

Android应用UI设计流程

最后更新于:2022-04-01 14:32:29

## Android应用UI设计流程 ### 设计原理 1、在移动设计中,使用环境是最关键的因素。原型设计方法必须考虑尺寸因素 2、用户测试必须涵盖运动、声音和多点触控等方面: 进行移动设计和测试时,请将你知道的有关与计算机交互的一切都抛到 脑后。与计算机交互时,用户只使用鼠标和键盘,这种大一统模式并不 适用于移动设备。移动时代的一个重要特征是充分利用人体的自然运动: 刮划表示深入挖掘;摇动表示拒绝;将手机放到耳边表示要说话。从语 音识别数字助理到计步器(它利用GPS传感器根据身体摇摆情况判断日 常身体活动的速度和健康状况),当今的移动设备正以前所未有的程度大 量利用运动、声音和多点触摸手势,以便从用户那里获取日益复杂的输入。 要设计出有效的用户界面,原型创建和用户测试方法必须考虑所有这些 与设备交互的新模式。 3、触控界面必须既简约又精巧:触控界面必须独特而不复杂, 同时又非常精巧。这意味着从很多方面说,创建触控界面原型都比创建 台式机Web界面原型更容易(如果使用的是不那么逼真的方法,如在纸 上绘制原型,这一点尤为明显),但前提条件是必须深入研究界面的微妙 方面。 4、愉悦不可或缺:新的移动平台是伴随着游戏成长起来的, 游戏已溶入其血液和DNA,因此,不管要完成的任务有多单调、多微不 足道,设计人员都必须确保软件使用起来令人愉悦,最起码也要帮助用 户尽快完成任务。 ### 移动设计案例研究 以下为举例说明: ### 第1步:范围、概念和规划: - 使用环境:在城市私人车库里进 行旧货出售 - 目标用户:钱 和时间都不充裕的年青大学生 - 现场调查和情景访谈、 - 设想:用户会如何使用该产品 - 预算:打算在设计和开发上投入多少时间和资金 ### 第2步:设计研讨会 为实时地将各种设计方案完整记录下来,一种很不错的方法是使用故事板。 这里的关键在于,尽可能少考虑实际的界面设计,只对用户如何使用应 用加以描述。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6e16e87.jpg) ### 第3步:使用便利贴做RITE调查 多年来,RITE一直是UCD工具箱的一部分。为让RITE在移动设计流程 中也能扮演核心角色,我对其做了简单修改:使用在便利贴上绘制的原型。 一叠便利贴的尺寸与移动设备相当,这种简单而精致的原 型让你能够测试人体工程、多点触摸、加速运动等,而这些是使用传统 线框无法完成的。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6e4691a.jpg) 使用便利贴原型很容易模拟分支。为此,可只将一叠便利贴最上面的那个“屏 幕”递给受试者;等受试者轻按该屏幕上的按钮或执行某项功能后,再背 着受试者从手里一系列表示分支的便利贴中选出合适的“屏幕”,并递给 受试者。这样,测试将非常逼真:如果下一个受试者轻按了另一个控件, 他将看到不同的屏幕;这让你能够测试分支和迂回工作流程、回溯以及 其他真实行为,从而获得丰富而可靠的行为数据。 ### 第4步:视觉设计 值得一提的是,视觉设计既能提升交互设计意图,也能让它逊色。有时候, 视觉设计影响重大,因此最好再对应用做几次测试,确保即便经过演化后, 最终的设计版本依然秉承了设想故事板的简约和优雅。风格能营造情感联系, 也能破坏情感联系,因此也有必要对其进行测试。
';

AsnycTask的内部的实现机制

最后更新于:2022-04-01 14:32:27

## AsnycTask的内部的实现机制 ### 写在前面 我们为什么要用AsnycTask。 在Android程序开始运行的时候会单独启动一个进程,默认情况下所有 这个程序操作都在这个进程中进行。一个Android程序默认情况下只有 一个进程,但是一个进程却是可以有许线程的。 我们通常会把一些比较耗时的操作,例如网络请求、数据库操作、复杂计算等逻辑都封装到单独的线程,这样就可以避免阻塞主线程。 ### 内部机制 主要分为两大部分: 1、与主线的交互,它内部实例化了一个静态的自定义类InternalHand- ler,这个类是继承自Handler的,在这个自定义类中绑定了一个叫做 AsyncTaskResult的对象,每次子线程需要通知主线程,就调用send- ToTarget发送消息给handler。然后在handler的handleMessage 中AsyncTaskResult根据消息的类型不同(例如MESSAGE POST- PROGRESS会更新进度条 , MESSAGEPOST CANCEL取消任务)而 做不同的操作,值得一提的是,这些操作都是在UI线程进行的,意味着, 从子线程一旦需要和UI线程交互,内部自动调用了handler对象把消息 放在了主线程了。 ~~~ mFuture = new FutureTask<Result>(mWorker) { @Override protected void More ...done() { Message message; Result result = null; try { result = get(); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { message = sHandler.obtainMessage(MESSAGE_POST_CANCEL, new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null)); message.sendToTarget(); return; } catch (Throwable t) { throw new RuntimeException("An error occured while executing " `+ "doInBackground()", t);` } message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); } }; private static class InternalHandler extends Handler { @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void More ...handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } } ~~~ 2、AsyncTask内部调度,虽然可以新建多个AsyncTask的子类的实例, 但是AsyncTask的内部Handler和ThreadPoolExecutor都是static 的,这么定义的变量属于类的,是进程范围内共享的,所以AsyncTask 控制着进程范围内所有的子类实例,而且该类的所有实例都共用一个线 程池和Handler。 ~~~ public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 1; private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread More ...newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_ SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static final int MESSAGE_POST_CANCEL = 0x3; ~~~ 从代码还可以看出,默认核心线程池的大小是5,缓存任务队列是10。 意味着,如果线程池的线程数量小于5,这个时候新添加一个异步任 务则会新建一个线程;如果线程池的数量大于等于5,这个时候新建一 个异步任务这个任务会被放入缓存队列中等待执行。 ### 注意的地方 - 由于Handler需要和主线程交互,而Handler又是内置于AsnycTask中的,所以, AsyncTask的创建必须在主线程。 - AsyncTaskResult的doInBackground(mParams)方法执行异步任务运行在子线 程中,其他方法运行在主线程中,可以操作UI组件。 - 不要手动的去调用AsyncTask的onPreExecute, doInBackground, publishPro- gress, onProgressUpdate, onPostExecute方法,这些都是由Android系统自 动调用的 - 一个任务AsyncTask任务只能被执行一次。 - 运行中可以随时调用cancel(boolean)方法取消任务,如果成功调用isCancelled() 会 返 回 true,并 且 不 会 执 行 onPostExecute() 方 法 了,取 而 代 之 的 是 调 用 onCancelled() 方法。而且从源码看,如果这个任务已经执行了这个时候调用 cancel是不会真正的把task结束,而是继续执行,只不过改变的是执行之后的回 调方法是onPostExecute还是onCancelled。 -------------- ### Activity OnConfiguration 1、如果你的App没有明确指定屏幕 方向和configChanges时,当用户旋转屏幕的时候Activity就会重新 启动,而这个时候您的异步加载数据的线程可能正在请求网络。当一 个新的Activity被重新创建之后,可能由重新启动了一个新的任务去 请求网络,这样之前的一个异步任务不经意间就泄露了,假设你还在 onPostExecute写了一些其他逻辑,这个时候就会发生意想不到异常。 2、一般简单的数据类型的,对付configChanges我们很好处理,我们直接 可 以 通 过onSaveInstanceState() 和 onRestoreInstanceState() 进 行 保 存 与 恢 复。 3、对于AsyncTask,我们可以用到Square开源的EventBus类库http://square.github.io/ otto/。首先自定义一个AsyncTask的子类,在onPostExecute方法中, 把返回结果抛给事件总线。 ~~~ @Override protected String doInBackground(Void... params) { Random random = new Random(); final long sleep = random.nextInt(10); try { Thread.sleep(10 * 6000); } catch (InterruptedException e) { e.printStackTrace(); } return "Slept for " + sleep + " seconds"; } @Override protected void onPostExecute(String result) { MyBus.getInstance().post(new AsyncTaskResultEvent(result)); } ~~~ 在Activity的onCreate中注册这个事件总线,这样异步线程的消息就会 被otta分发到当前注册的activity,这个时候返回结果就在当前activity 的onAsyncTaskResult中了。 ~~~ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.otto_layout); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyAsyncTask().execute(); } }); MyBus.getInstance().register(this); } @Override protected void onDestroy() { MyBus.getInstance().unregister(this); super.onDestroy(); } @Subscribe public void onAsyncTaskResult(AsyncTaskResultEvent event) { Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show(); } ~~~
';

Android系统的安全设计与架构

最后更新于:2022-04-01 14:32:25

## Android系统的安全设计与架构 **一、安全策略** 1、Android 的总体架构由5个主要层次上的组件构成,这5层是:Android应用层、 Android框架层、Dalvik虚拟机层、用户空间原生代码层和Linux内核层。 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6d87c08.jpg "") 2、安全边界,有时也会称为信任边界,是系统中分隔不同信任级别的特殊区域。 一个最直接的例子就是内核空间与用户空间之间的边界。内核空间中的 代码可以对硬件执行一些底层操作并访问所有的虚拟和物理内存,而用 户空间中的代码则由于CPU的安全边界控制,无法访问所有内存。 3、Android沙箱的核 心机制基于以下几个概念:标准的Linux进程隔离、大多数进程拥有唯 一的用户ID(UID),以及严格限制文件系统权限。 ~~~ #define AID_SHELL 2000 / * adb shell 与 debug shell 用户* / #define AID_CACHE 2001 / *缓存访问* / #define AID_DIAG 2002 / *访问诊断资源* / ~~~ 4、某些情况下,权限也可能以Linux权能的形式出现,例如,AID_INET_ ADMIN用户组中的成员授予CAP_NET_ADMIN权能,允许用户配置网络接 口和路由表。 ### 1.2权限 1、API权限:用于控制访问高层次的功能,这些功能存在于Android API、框 架层,以及某种情况下的第三方框架中。一个使用API权限的常见例子是 READ_PHONE_STATE, 2、文件权限:默认情 况下,应用的唯一UID和GID都只能访问文件系统上相应的数据存储路径。 ~~~ root@android:/ # ls -l /data/data drwxr-x--x u0_a3 u0_a3 ... com.android.browser drwxr-x--x u0_a4 u0_a4 ... com.android.calculator2 drwxr-x--x u0_a5 u0_a5 ... com.android.calendar drwxr-x--x u0_a24 u0_a24 ... com.android.camera ~~~ 3、IPC权限:IPC权限直接涉及应用组件(以及一些系统的IPC设施)之间的通信,虽 然与API权限也有一些重叠。这些权限的声明和检查实施可能发生在不 同层次上,包括运行环境、库函数,或直接在应用上。具体来说,这 个权限集合应用于一些在Android Binder IPC机制之上建立的主要 Android应用组件。 **二、层次** ### 2.1应用层 应用通常被分为两类:预装应用与用户安装的应用。 1、AndroidManifest.xml:Manifest文件中一个特别有趣的部分是sharedUserId属性。简单地说, 如果两个应用由相同的密钥签名,它们就可以在各自的Manifest文件中 指明同一个用户标识符。在这种情况下,这两个应用就会在相同的UID 环境下运行,从而能使这些应用访问相同的文件系统数据存储以及潜在 的其他资源。 2、Intent:Intent是一种消息对象,其中包含一个要执行操作的相关信息,将执行操作的目标组件信息(可选),以及其他一些(对接收方可能非常关键的)标志位或支持性信息。几乎所有常用的动作都涉及在系统中传递 ~~~ Intent。 <permission android:name="com.wiley.permission.INSTALL_WIDGET" android:protectionLevel="signature" /> ... <activity android:name=".InstallWidgetActivity" android:permission="com.wiley.permission.INSTALL_WIDGET"/> ~~~ 3、Activity:是一种面向用户的应用组件或用户界面(UI)。 Activity基于Activity基类,包括一个窗口和相关的UI元素。Activity 的底层管理是由被称为Activity管理服务(Activity Manager)的组件 来进行处理的,这一组件也处理应用之间或应用内部用于调用Activity 的发送Intent。 4、Broadcast Receiver:通常会在应用希 望接收一个匹配某种特定标准的隐式Intent时出现也可以使用。 registerReceiver方法在运行时以编程方式 注册,这个方法可以被重载以对Receiver设置权限。 ~~~ <receiver android:name=".MySMSReceiver"> <intent-filter android:priority:"999"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> ~~~ 5、Service是一类在后台运行而无需用户界面的应用组件,用户不用直接与Service所属应用进行交互。 ~~~ <service android:name="com.yougetitback.androidapplication.FindLocationService"> <intent-filter> <action android:name="com.yougetitback.androidapplication.FindLocationService" /> </intent-filter> </service> ~~~ Service通常可以被停止、启动或绑定,所有这些动作都通过Intent来触发。 6、Content Provider是为各种通用、共享的数据存储提供的结构化访问接口。 ~~~ <provider android:name="com.wiley.example.MyProvider" android:writePermission="com.wiley .example.permission.WRITE" android:authorities="com.wiley .example.data" /> ~~~ Content URI采用 content://[authorityname]的格式,可以额外包含路径和参数信息(如 content://com.wiley.example.data/foo),而这些信息对Provider 的底层实现可能非常关键。 ### 2.2框架层 Android框架层为开发者提供了执 行通用任务的部件——程序包及其类。这些任务可能包括管理UI元素、 访问共享数据存储,以及在应用组件中传递消息等。也就是说,框架层 中包含任何仍然在DalvikVM中执行的非应用特定代码。 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6dadaa9.jpg "") 1、DalvikVM是基于寄存器而不是栈的。。class->.dex->.apk。DalvikVM使用Java Native Interface(JNI)与 底层原生代码进行交互。这一功能允许在Dalvik代码和原生代码之间相 互调用。 2、Android设备启动时,Zygote进程是最先运行的进程之一。接下来, Zygote负责启动其他服务以及加载Android框架所使用的程序库。然后, Zygote进程作为每个Dalvik进程的加载器,通过复制自身进程副本(也 被称为forking,分支)来创建进程。 Zygote的第二大功能是启动system_server进程,这个进程容纳了所 有系统核心服务,并在system的AID用户环境中以特权权限运行。 ### 2.3用户空间原生代码层 操作系统用户空间内的原生代码构成了Android系统的一大部分,这一 层主要由两大类组件构成:程序库和核心系统服务。 1、Android框架层中的较高层次类所依赖的许多底层功能都是通过共享程 序库的方式来实现,并通过JNI进行访问的。在这其中,许多程序库都也 是在其他类Unix系统中所使用的知名开源项目。比如,SQLite提供了本 地数据存储功能,Webkit提供了可嵌入的Web浏览器引擎,FreeType 提供了位图和矢量字体渲染功能。 并非所有的底层程序库都是标准的,Bionic就是一个值得注意的特例。 Bionic是BSD C运行时库的一个变种,旨在提供更小的内存使用空间。 这些库是使用原生代码开发的,因而很容易出现内存破坏漏洞 2、核心服务是指建立基本操作系统环境的服务与Android原生组件。这些 服务包括初始化用户空间的服务(如init)、提供关键调试功能的服务(如adbd和debugggerd)等。 3、其他服务:提供一些不一定是必需 的额外功能(取决于设备和服务) ![33页截图](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6dd547c.jpg "") ### 2.4内核 1、Android对Linux内核的主要修改(例举2): ~~~ Binder: IPC机制,提供额外的一些特性,比如对调 用者和被调用者的安全验证。它已被大量的 系统和框架服务所使用 OOM修改: "Out Of Memory"-killer在内存空间低的时 候杀掉进程,在Android分支中,OOM在 内存即将用尽时,较传统Linux内核能更快 地杀掉进程 ~~~ 2、Binder:Binder作为 一个架构,以客户端—服务器模型运行,允许一个进程同时调用多个“远程” 进程中的多个方法。Binder架构将底层细节进行了抽象,使得这些方法 调用看起来就像是本地函数调用。 AIDL 允许两个应用使用“协商确定”或者标准化的接口,来发送和接收数据, 使得接口独立于具体的实现。AIDL类似于其他的接口定义语言文件,比 如C/C++中的头文件。 ~~~ // IRemoteService.aidl package com.example.android; // Declare any non-default types here with import statements //在此声明任何非默认类型导入声明 /*范例服务接口*/ interface IRemoteService { /**请求这一服务的进程ID,做点“有趣”的事情**/ int getPid(); /**显示一些用作AIDL参数和返回值的基本类型**/ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); } ~~~ 3、ashmem:匿名共享内存服务,它广泛应用于大多数Android核心组件中, 包括Surface Flinger、Audio Flinger、系统服务器和DalvikVM等。 ashmem能够自动收缩内存缓存,并在全局可用内存较低时回收内存区域, 因而非常适用于低内存环境。 ~~~ int fd = ashmem_create_region("SomeAshmem", size); if(fd == 0) { data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ... ~~~ 2011年,ashmem被证明存在一个非常严重的安全缺陷,允许 通过Android属性进行特权提升 4、日志记录器:它根据信息的类型, 提供了4个独立的日志缓冲区:main(主缓冲区)、radio(无线电缓冲区)、 event(事件缓冲区)与system(系统缓冲区)。 ~~~ $ adb -d logcat ~~~ ### 写在最后 在仔细观察了Android的设计与架构之后,我们已经清楚地了解到,Android操 作系统是一种非常复杂的系统。设计者坚持了最低权限原则,也就是说任何特定 组件都应该只能访问它真正所需要访问的东西。不过,这虽然有助于提高安全性, 却也增加了复杂性。
';

电商活动中刮刮卡的实现

最后更新于:2022-04-01 14:32:22

**一、实现原理** Paint.setXferMode(); a、绘制我们的圆形 b、setXferMode(SrcIn) c、绘制矩形(正方形)图片 **二、主要思想:** 将一个view设计成多层:背景层,含中奖信息等; 遮盖层,用于刮奖,使用关联一个Bitmap的Canvas 在该Bitmap上,使用它的canvas.drawPath的api来处理 手势滑动(类似刮奖的动作) 使用paint.setXfermode 来进行消除手势滑动区域 当刮开90%的时候会全部消失。 **三、代码实现** ~~~ public class GuaGuaKa extends View { private Paint mOutterPaint; private Path mPath; private Canvas mCanvas; private Bitmap mBitmap; private int mLastX; private int mLastY; private Bitmap mOutterBitmap; // ------------------------------- // private Bitmap bitmap; private String mText; private Paint mBackPaint; /** * 记录刮奖信息文本的宽和高 */ private Rect mTextBound; private int mTextSize; private int mTextColor; // 判断遮盖层区域是否消除达到阈值 private volatile boolean mComplete = false; /** * 刮刮卡刮完的回调 * * */ public interface OnGuaGuaKaCompleteListener { void complete(); } private OnGuaGuaKaCompleteListener mListener; public void setOnGuaGuaKaCompleteListener( OnGuaGuaKaCompleteListener mListener) { this.mListener = mListener; } public GuaGuaKa(Context context) { this(context, null); } public GuaGuaKa(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GuaGuaKa(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); TypedArray a = null; try { a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.GuaGuaKa, defStyle, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.GuaGuaKa_text: mText = a.getString(attr); break; case R.styleable.GuaGuaKa_textSize: mTextSize = (int) a.getDimension(attr, TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_SP, 22, getResources().getDisplayMetrics())); break; case R.styleable.GuaGuaKa_textColor: mTextColor = a.getColor(attr, 0x000000); break; } } } finally { if (a != null) a.recycle(); } } public void setText(String mText) { this.mText = mText; // 获得当前画笔绘制文本的宽和高 mBackPaint.getTextBounds(mText, 0, mText.length(), mTextBound); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); // 初始化我们的bitmap mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); mCanvas = new Canvas(mBitmap); // 设置绘制path画笔的一些属性 setupOutPaint(); setUpBackPaint(); // mCanvas.drawColor(Color.parseColor("#c0c0c0")); mCanvas.drawRoundRect(new RectF(0, 0, width, height), 30, 30, mOutterPaint); mCanvas.drawBitmap(mOutterBitmap, null, new Rect(0, 0, width, height), null); } /** * 设置我们绘制获奖信息的画笔属性 */ private void setUpBackPaint() { mBackPaint.setColor(mTextColor); mBackPaint.setStyle(Style.FILL); mBackPaint.setTextSize(mTextSize); // 获得当前画笔绘制文本的宽和高 mBackPaint.getTextBounds(mText, 0, mText.length(), mTextBound); } /** * 设置绘制path画笔的一些属性 */ private void setupOutPaint() { mOutterPaint.setColor(Color.parseColor("#c0c0c0")); mOutterPaint.setAntiAlias(true); mOutterPaint.setDither(true); mOutterPaint.setStrokeJoin(Paint.Join.ROUND); mOutterPaint.setStrokeCap(Paint.Cap.ROUND); mOutterPaint.setStyle(Style.FILL); mOutterPaint.setStrokeWidth(20); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int x = (int) event.getX(); int y = (int) event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: mLastX = x; mLastY = y; mPath.moveTo(mLastX, mLastY); break; case MotionEvent.ACTION_MOVE: int dx = Math.abs(x - mLastX); int dy = Math.abs(y - mLastY); if (dx > 3 || dy > 3) { mPath.lineTo(x, y); } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: if (!mComplete) new Thread(mRunnable).start(); break; } if (!mComplete) invalidate(); return true; } ~~~ ~~~ </pre>我们在ACTION_UP的时候就行计算,首先我们还是给大家灌输下计算的原理,如果大家用心看了,应该知道我们所有的操作基本都在mBitmap,现在我们获得mBItmap上所有的像素点的数据,统计被清除的区域(被清除的像素为0);最后与我们图片的总像素数做个除法元算,就可以拿到我们清除的百分比了;不过,计算可能会是一个耗时的操作,具体速度跟图片大小有关,所以我们决定使用异步的方式去计算:<pre name="code" class="java"> ~~~ ~~~ private Runnable mRunnable = new Runnable() { @Override public void run() { int w = getWidth(); int h = getHeight(); float wipeArea = 0; float totalArea = w * h; Bitmap bitmap = mBitmap; int[] mPixels = new int[w * h]; // 获得Bitmap上所有的像素信息 bitmap.getPixels(mPixels, 0, w, 0, 0, w, h); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int index = i + j * w; if (mPixels[index] == 0) { wipeArea++; } } } if (wipeArea > 0 && totalArea > 0) { int percent = (int) (wipeArea * 100 / totalArea); Log.e("TAG", percent + ""); if (percent > 90) { // 清除掉图层区域 mComplete = true; postInvalidate(); } } } }; @Override protected void onDraw(Canvas canvas) { // canvas.drawBitmap(bitmap, 0 , 0, null); canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2, getHeight() / 2 + mTextBound.height() / 2, mBackPaint); if (!mComplete) { drawPath(); canvas.drawBitmap(mBitmap, 0, 0, null); } if (mComplete) { if (mListener != null) { mListener.complete(); } } } private void drawPath() { mOutterPaint.setStyle(Style.STROKE); mOutterPaint.setXfermode(new PorterDuffXfermode(Mode.DST_OUT)); mCanvas.drawPath(mPath, mOutterPaint); } /** * 进行一些初始化操作 */ private void init() { mOutterPaint = new Paint(); mPath = new Path(); mOutterBitmap = BitmapFactory.decodeResource(getResources(), cn.zhilinghui.guaguaka.R.drawable.fg_guaguaka); mText = "谢谢惠顾"; mTextBound = new Rect(); mBackPaint = new Paint(); mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 22, getResources().getDisplayMetrics()); } } ~~~ view的自定义控件: ~~~ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:hyman="http://schemas.android.com/apk/res/cn.zhilinghui.guaguaka" android:layout_width="match_parent" android:layout_height="match_parent" > <cn.zhilinghui.guaguaka.view.GuaGuaKa android:id="@+id/id_guaguaka" android:layout_width="300dp" android:layout_height="100dp" android:layout_centerInParent="true" hyman:text="¥500,0000" hyman:textColor="#ff00f0" hyman:textSize="30sp" /> </RelativeLayout> ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6d6731e.jpg)
';

安卓性能优化手册

最后更新于:2022-04-01 14:32:20

本手册适合至少有初级经验的开发者查阅或复习相关知识使用,新手可能会看不懂。 ## 1、java代码优化 ### 1.1安卓如何执行代码 dvm:.java->.class->.dex->.apk - 优化斐波那契数列: 斐波那契数列的递推公式是f(n)=f(n-1)+f(n-2),特征方程为:x2=x+1,解该方程得(1+sqrt(5))/2,(1-sqrt(5))/2.所以f(n)=Ax1n+Bx2n,带入f(0)=0,f(1)=1得A=sqrt(5)/5,B=-sqrt(5)/5.则f(n)求出。 - BigInteger: 用这个类来解决溢出问题。 斐波那契数列有如下性质: (fn fn-1)=(fn-1 fn-2)*A,可以得出A=(1 1;1 0) 递推可得:(fn fn-1)=(fn-1 fn-2)*A=(fn-2 fn-3)*A2=…=(f1 f0)*An-1 - 缓存结果: 用HashMap来充当缓存,当键是整数时,用SparseArray效率更高。 - LruCache: 适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。 - ~~~ private LruCache<integer, string=""> mJsonCache; /** * 缓存图片信息 */ private LruCache<integer, bitmap=""> mBitmapCache; public Util() { mJsonCache = new LruCache<integer, string="">(1 * 1024 * 1024); mBitmapCache = new LruCache<integer, bitmap="">(2 * 1024 * 1024); } ~~~ - 数据结构: 安卓在这些数据结构中增加了自身的一些实现,一般是为了提高性能,LruCache\SparseArray\SparseBooleanArray\SparseIntArray\Pair 还有Arrays和Collections类。例如使用Arrays.sort对数组排序。 - 响应能力:熟悉生命周期,例如设备方向变化时,配置Android:configChanges属性 - 推迟初始化:例如可以用android.view.ViewStub来推迟初始化。由于内存分配需要花时间,等到对象真正需要时才进行分配,也是一个很好的选择。 - StricMode:主线程不要用来访问网络等耗时操作。 - SQLite语句:加快要执行的SQL语句字符串的创建速度,在这种情况下使用+运算符来连接字符串不是最有效的方法,而使用StringBuilder对象,或调用String.format可以提高性能。 - 事务:一次性事务解决。可以使用SQLiteOpenHelper来替代手动创建数据库。 - 查询:创建cursor只获取第一列 ## 2、NDK 主要由c/c++编写,是安卓原生开发套件。 1、创建本地方法: ~~~ public native String encode(String text, int length); public native String decode(String text, int length); } ~~~ 2、实现JNI粘合层: 使用jdk的javah工具自动生成。 1. 在项目根目录下创建jni文件夹 1. 在jni文件中创建一个c文件 1. 在java代码中,创建一个本地方法helloFromC ~~~ public native String helloFromC(); ~~~ 4.在jni中定义函数实现这个方法,函数名必须为 ~~~ jstring Java_com_zhilinghui_helloworld1_MainActivity_helloFromC(JNIEnv* env, jobject obj) ~~~ 5.返回一个字符串,用c定义一个字符串 ~~~ char* cstr = "hello from c"; ~~~ 6.把c的字符串转换成java的字符串 ~~~ jstring jstr = (*env)->NewStringUTF(env, cstr); return jstr; ~~~ 1. 在jni中创建Android.mk文件 1. 在c文件中添加 ### 安卓中如何使用c/c++代码(总结) 前面可以说的有点乱,这里简单概括一下: - 步骤一: 在java中定义一个c方法的接口 ,相当于在java代码中定义了一个接口 接口的实现方法是C语言实现的。 public native String hello(); - 步骤二: 实现C代码 方法名 严格按照jni的规范 - 步骤三: 创建android.mk 告诉编译器 如何把c代码打包成函数库 LOCAL_PATH := $(call my-dir) - 步骤四: 把c代码 打包成函数库 用到了安装的环境 到相应目录下使用ndk-build打包 - 步骤五: 在java代码中 引入库函数 - 步骤六: 使用方法 ## 3、NDK进阶 汇编优化:安卓 NDK内置了GCC编译器的功能。 要确保使用正确版本的objdump反编译目标文件和库。 ARM模式和Thumb模式。 - 色彩转换:图形程序中最常用的操作是把颜色从一种格式转换为另一种,ARGB8888和RGB565. - RAM指令 ### 提高性能的技巧 - 内联函数:即直接在调用处实现替换调用。在函数定义前加上”inline“关键字就可以了。 - 循环展开:展开可以积极调度(或管道化)循环以掩盖一些延迟。如果有足够的空闲寄存器使变量保持活动状态,因为通过展开相关性链展露了关键路径,这将非常有用。 - 内存预读取:1.GCC的——builtin_prefetch();2、在ARM汇编代码中使用PLD和PLDW指令,还可以使用PLI指令。 - 用LDM/STM替换LDR/STD:使用单个指令加载多个寄存器比用多个LDR指令加载寄存器快。 ## 4、高效使用内存 java的char是16位(UTF-16),java的long是64位,而c的long是32位,long long是64位。 ### 访问内存: 减少数据缓存读未命中几率的方法:1、在有大量数据存储在数组中时,使用尽可能小的数据类型;2、选择顺序访问而不是随机访问,最大限度的重用已在缓存中的数据。 ### 垃圾收集: ### 内存泄漏 只有当某个对象不再引用时,它的内存才会被回收,当该被释放的对象引用仍然存在的时候就会发生OOM。 例如:屏幕旋转时。Eclipse中可以在DDMS中的Heap及Allocation Tracker跟踪。 也可以使用StricMode类来检测潜在在内存泄漏. ### 引用 - 强引用: 程序中绝大部分是这种引用。就是“正常”引用。 – `BigIntegerbi=BigInteger.valueOf(n);//强引用` `Integer i=new Integer(n);//强引用` i=null;//Integer对象变成可回收的垃圾。 - 软、弱、虚引用: 软可及对象,垃圾收集器自己决定想回收旧回收。弱可及对象,基本会被回收。虚引用,几乎很少用,可以用来注册引用队列。 - 垃圾收集 可能会在不定的时间触发,几乎无法控制其发生,例如堆被占满不能进行内存分配时,在分配新对象要进行内存回收。 ### API - ActivityManager的getMemoryInfo()、getMemoryClass()、getLargeMemoryClass(); - Debug的dumHprofData()、getMemoryInfo、getNativeHeapAllocatedSize()、getNativeHeapSize(). - ~~~ ActivityManager am=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo memInfo=new ActivityManager.MemoryInfo(); am.gettMemoryInfo(memInfo); ~~~ ## 5、多线程和同步 ### 线程 Thread对象。run()方法可以被复写,Runnable对象传递给Thread构造函数。记得调用start()。 线程优先级,一般是1-10(最高),默认是5.而Linux的优先级是从-20到19(最低). ### AsyncTask AsyncTask启动任务栈执行的输入参数,后台任务执行的百分比,后台任务执行返回iud结果,匿名类。 例如:后台下载文件时刷新前台进度条。 ### Handler和Looper 他们是多线程应用线程间通信的基石。 ### Handler - Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。 Handler在run()方法中创建,因为它需要被绑定到指定的Looper,这Looper就是在run()方法中调用Looper.prepare(0创建的。在线程产生之前,调用getHandler()返回null. ### Looper 有消息循环的线程。 - Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。 - Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。 - MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。 - Thread:线程,负责调度整个消息循环,即消息循环的执行场所 ### Activity生命周期 7个生命周期 调用AsyncTask.cancel()会在doInbackground()返回后触发onCancelled(),而不是onPoseExecute(). ## 6、性能评测和剖析 DDMS中的Traceview - 查看跟踪代码的执行时间,分析哪些是耗时操作 ;可以用于跟踪方法的调用,尤其是Android Framework层的方法调用关系 - TraceView罗列出了是所有监听到的方法,当然也包括Android系统很多方法的耗时,如何在这么多方法里面查找到自己关心的? 可以通过TraceView 底部的find 来查找,通常Android app都是有包名的,可以先针对某些关心的列排序后,在通过包名进行一个个查找,这些就省去自己筛选出自己app 方法耗时排行的时间。 ## 7、延长电池续航时间 一般屏幕和wifi很耗电。 ### 测量电池用量 可以通过检索固定的Intent。在应用启动时就获取电池当前电量,运行一段时间,在退出时再次获取电池电量。但是这个值并不准确,因为还有其他应用运行。 ### 禁用广播接收器 只有在需要时才启用广播接收器。 - 因为BatteryManager广播的是一个sticky的intent实体,这就意味着你不用非得注册一个广播接收者来让你的程序接受这个广播,你可以仅仅就是通过调用registerReceiver这个方法,在需要添加广播接收者位置的参数上传入null,当然你也可以新建一个广播接收者,并在注册广播接收者的时候传入。 ### 网络 最终解决问题,AT&T与来自密歇根大学的同事们的工作人员进行了深入的端到端数据传输路径的综合调查,最终发现是在设备和蜂窝网络之间复杂的相互作用的问题的根源,相互作用,是很难看到,给出分层性网络架构,故意隐藏低层协议的开发者在应用层的工作。 更多文章可以参考:[http://www.research.att.com/editions/201106_home.html](http://www.research.att.com/editions/201106_home.html) ### 位置 使用 ### WakeLock 例如,在用户观看视频或电影时,cpu需要做视频解码,同时保持屏幕开启,让用户能够观看。 ~~~ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); WakeLock sCpuWakeLock = pm.newWakeLock( PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,"okTag"); if (sCpuWakeLock!= null) { sCpuWakeLock.release(); sCpuWakeLock = null; } ~~~ - PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。 - SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯 - SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,允许关闭键盘灯 - FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度 ACQUIRE_CAUSES_WAKEUP:不会唤醒设备,强制屏幕马上高亮显示,键盘灯开启。有一个例外,如果有notification弹出的话,会唤醒设备。 - ON_AFTER_RELEASE:WakeLock 被释放后,维持屏幕亮度一小段时间,减少WakeLock 循环时的闪烁情况 如果申请了partial wakelock,那么即使按Power键,系统也不会进Sleep,如Music播放时 如果申请了其它的wakelocks,按Power键,系统还是会进Sleep 但如果不领会Android设计者的意图而滥用Wake Lock API,为了自身程序在后台的正常工作而长时间阻止AP进入休眠状态,就会成为待机电池杀手。 ### 提醒 AlarmManage有一个AlarmManagerService,该服务程序主要维护app注册下来的各类Alarm,并且一直监听Alarm设备,一旦有Alarm触发,或者是Alarm事件发生,AlarmManagerService就会遍历Alarm列表,找到相应的注册Alarm并发出广播。 Alarm Manager会维持一个cpu的wake lock。这样能保证电话休眠时,也能处理alarm的广播。一旦alarm receiver的onReceive() 方法执行完,wake lock会迅速被释放。如果在receiver中开启一个service,有可能service还没启动,wake lock已经被释放了。所以此时要实现单独的wake lock策略。 有4种Alarm类型: 1)RTC_WAKEUP 在指定的时刻(设置Alarm的时候),唤醒设备来触发Intent。 2)RTC 在一个显式的时间触发Intent,但不唤醒设备。 3)ELAPSED_REALTIME 从设备启动后,如果流逝的时间达到总时间,那么触发Intent,但不唤醒设备。流逝的时间包括设备睡眠的任何时间。注意一点的是,时间流逝的计算点是自从它最后一次启动算起。 4)ELAPSED_REALTIME_WAKEUP 从设备启动后,达到流逝的总时间后,如果需要将唤醒设备并触发Intent。 ### 传感器、图形 这里不做详细的介绍了 ## 8、图形 ### 布局优化 - 调用setContentView()。xml布局的到时候要减少创建对象数量,可以用不同的布局达到同样的视觉效果,消除不必要的对象,或者推迟创建对象。 - 嵌套线性布局会深化布局层次,从而导致布局和按键处理变慢。当显示10个及以上的项目的时候就需要相对布局。 - 用`<merge />`标签来合并布局 - ~~~ <merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/add"/> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/delete"/> ~~~ * 使用`<include />`标签重用布局 `<include layout="@layout/titlebar"/>` - ViewStub: ### 推迟加载 ~~~ <ViewStub android:id="@+id/viewstub_demo_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_marginTop="10dip" android:layout="@layout/viewstub_demo_text_layout"/> ~~~ ### 布局工具 在sdk->tools目录下,有hierarchyviewer和layoutopt,可以查看和分析布局 ### OpenGL ES OpenGL ES (为OpenGL for Embedded System的缩写) 为适用于嵌入式系统的一个免费二维和三维图形库。 任何复杂的2D或是3D图形都是通过这三种几何图形构造而成的。 OpenGL ES提供了两类方法来绘制一个空间几何图形: - public abstract void glDrawArrays(int mode, int first, int count) 使用VetexBuffer 来绘制,顶点的顺序由vertexBuffer中的顺序指定。 public abstract void glDrawElements(int mode, int count, int type, Buffer indices) ,可以重新定义顶点的顺序,顶点的顺序由indices Buffer 指定。 mode列表:GL_POINTS 绘制独立的点、GL_LINE_STRIP绘制一条线段、GL_LINE_LOOP绘制一条封闭线段(首位相连)、GL_LINES绘制多条线段、GL_TRIANGLES绘制多个三角形(两两不相邻)、GL_TRIANGLE_STRIP绘制多个三角形(两两相邻)、GL_TRIANGLE_FAN以一个点为顶点绘制多个相邻的三角形 对应顶点除了可以为其定义坐标外,还可以指定颜色,材质,法线(用于光照处理)等。 glEnableClientState 和 glDisableClientState 可以控制的pipeline开关可以有:GL_COLOR_ARRAY (颜色) ,GL_NORMAL_ARRAY (法线), GL_TEXTURE_COORD_ARRAY (材质), GL_VERTEX_ARRAY(顶点), GL_POINT_SIZE_ARRAY_OES等。 对应的传入颜色,顶点,材质,法线的方法如下: glColorPointer(int size,int type,int stride,Buffer pointer) glVertexPointer(int size, int type, int stride, Buffer pointer) glTexCoordPointer(int size, int type, int stride, Buffer pointer) glNormalPointer(int type, int stride, Buffer pointer) OpenGL ES 内部存放图形数据的Buffer有COLOR ,DEPTH (深度信息)等,在绘制图形只前一般需要清空COLOR 和 DEPTH Buffer。 - 通用的矩阵变换指令 这里介绍对应指定的坐标系(比如viewmodel, projection或是viewport) Android OpenGL ES支持的一些矩阵运算及操作。 矩阵本身可以支持加减乘除,对角线全为1的4X4 矩阵成为单位矩阵Identity Matrix 。 将当前矩阵设为单位矩阵的指令 为glLoadIdentity(). 矩阵相乘的指令glMultMatrix*() 允许指定任意矩阵和当前矩阵相乘。 选择当前矩阵种类glMatrixMode(). OpenGL ES 可以运行指定GL_PROJECTION,GL_MODELVIEW等坐标系,后续的矩阵操作将针对选定的坐标。 将当前矩阵设置成任意指定矩阵glLoadMatrix*() 在栈中保存当前矩阵和从栈中恢复所存矩阵,可以使用glPushMatrix()和glPopMatrix() 特定的矩阵变换平移glTranslatef(),旋转glRotatef() 和缩放glScalef() ### 纹理压缩 具体可以在官网看:[http://developer.android.com/training/graphics/opengl/index.html](http://developer.android.com/training/graphics/opengl/index.html) ### 着色、场景复杂性、消隐、渲染、 只有当场景变化才渲染帧。 ## 9、RenderScript RenderScript 是一种低级的高性能编程语言,用于3D渲染和处理密集型计算(3D播放等和关于CPU密集型的计算)。一直以来Android 在绘图性能的表现一直差强人意,引入NDK之后才有所改善,而在Honeycomb 中发布了RenderScript 这一杀手级在Framework 后,大大的增加了Android 本地语言的执行能力和计算能力。 RenderScript 在机器上进行第一遍编译,然后在目标设备上进行最后一遍编译(Just-In-Time Compiling),因而带来更高效的原生二进制代码。这也就是意味着,凡是支持RenderScript 的设备都可以运行你的代码。不用管什么架构。 目前 ,RenderScript 带来的代码只能在主处理器上运行,它会自动生成可利用多个核心的代码(如果设备上有多个核心)。就因此,编译出来的程序是针对该机器的最佳优化,这解决了Device Fragmentation,也就是说开发者再也不必担心使用者的手机、平板够不够好、有没有GPU…等等问题,全都交给RenderScript 去担心就好。没有GPU,RenderScript 写好的程序就交由CPU来处理(背后的编译技术其实是使用的LLVM)。
';

java基础知识——网络编程、IO流

最后更新于:2022-04-01 14:32:18

## IO流 字节流:处理字节数据的流对象,计算机中最小数据单元就是字节。InputStream OutputStream 字符流:字符编码问题,将字节流和编码表封装成对象就是字符流。Reader Write 读、写都会发生 IO 异常。io 异常的处理方式 :io 一定要写 finally。fw.flush();//刷新缓冲区,fw.close();//关闭流。 ~~~ IO 中的使用到了一个设计模式: 装饰设计模式。 装饰设计模式解决:对一组类进行功能的增强。 包装:写一个类(包装类)对被包装对象进行包装; * 1、包装类和被包装对象要实现同样的接口; * 2、包装类要持有一个被包装对象; * 3、包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现; ~~~ ### 字符流 - Reader : 用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。 - Writer : 写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。 ### 字节流 InputStream、OutputStream ~~~ BufferedWriter :是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明 确要提高具体的流对象的效率。 FileWriter fw = new FileWriter("bufdemo.txt"); BufferedWriter bufw = new BufferedWriter(fw); // 让缓冲区和指定流相关联。 for(int x=0; x<4; x++){ bufw.write(x+"abc"); bufw.newLine(); // 写入一个换行符,这个换行符可以依据平台的不同写入不同的换行符。 bufw.flush();//对缓冲区进行刷新,可以让数据到目的地中。 } bufw.close(); // 关闭缓冲区,其实就是在关闭具体的流。 ----------------------------- BufferedReader : FileReader fr = new FileReader("bufdemo.txt"); BufferedReader bufr = new BufferedReader(fr); String line = null; while((line=bufr.readLine())!=null){ e //readLine 方法返回的时候是不带换行符的。 System.out.println(line); } bufr.close(); ----------------------------- //记住,只要一读取键盘录入,就用这句话。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台 String line = null; while((line=bufr.readLine())!=null){ if("over".equals(line)) break; 51 / 65 bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出 bufw.newLine(); bufw.flush(); } bufw.close(); bufr.close(); ~~~ 流的操作规律: - 1 ,明确源和目的。 数据源:就是需要读取,可以使用两个体系:InputStream、Reader; 数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer; - 2 ,操作的数据是否是纯文本数据? 如果是:数据源:Reader 数据汇:Writer 如果不是:数据源:InputStream 数据汇:OutputStream - 3 ,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢? 明确操作的数据设备。 数据源对应的设备:硬盘(File),内存(数组),键盘(System.in) 数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。 - 4 ,需要在基本操作上附加其他功能吗?比如缓冲。 如果需要就进行装饰。 ### File类 - 将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹 进行操作。这些是流对象办不到的,因为流只操作数据。 - createNewFile() 、mkdir()、getAbsolutePath() ### 递归 使用情况:功能内部又用到该功能,但是传递的参数值不确定。 - 递归的注意事项: 1:一定要定义递归的条件。 2:递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出 错误。 其实递归就是在栈内存中不断的加载同一个函数。 Java递归算法的小例子 求1+2+3…+1000 和 ~~~ public class Test1 { int sum=0; int a=1; public void sum() { sum+=a; a++; if(a<=1000) { sum();//调用自身实现递归 } } public static void main(String[] args) { Test1 test=new Test1(); test.sum(); System.out.println("计算结果:"+test.sum+"!"); } ~~~ ### 扩展功能的流对象 ### PrintStream : 打印流 - PrintStream m 可以操作目的:1:File 对象。2:字符串路径。3:字节输出流。 - PrintWriter :该对象的目的地有四个:1:File 对象。2:字符串路径。3:字节输出流。4:字符输出流。 - PrintWriter out = new PrintWriter( new FileWriter(“out.txt”), true);//设置 true 后自动刷新 - System.in,System.out 这两个标准的输入输出流,在 jvm 启动时已经存在了。随时可以使用。当 jvm 结束了,这两个流就结束了。但是,当使用了显示的 close 方法关闭时,这两个流在提前结束了。 ### SequenceInputStream : 序列流 作用就是将多个读取流合并成一个读取流实现数据合并。 - 合并原理:多个读取流对应一个输出流。 切割原理:一个读取流对应多个输出流。 ### 管道流 管道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。 注意 :需要加入多线程技术,因为单线程,先执行 read,会发生死锁,因为 read 方法是阻塞式的,没有数据的 read 方法会让线程等待。 ~~~ public static void main(String[] args) throws IOException{ PipedInputStream pipin = new PipedInputStream(); PipedOutputStream pipout = new PipedOutputStream(); pipin.connect(pipout); new Thread(new Input(pipin)).start(); new Thread(new Output(pipout)).start(); } ~~~ ### 对象序列化 静态数据不能被序列化,因为静态数据不在堆内存中;用transient关键字修饰变量,可以将非静态数据不进行序列化。 - Serializable :用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一 个标记接口。 - ByteArrayInputStream : 源 : 内存 ByteArrayOutputStream :目的:内存。 这两个流对象不涉及底层资源调用,操作的都是内存中数组,所以不需要关闭。 ## 网络编程 端口:0-65535 ~~~ //通过名称(ip 字符串 or 主机名)来获取一个 ip 对象。 InetAddress ip = InetAddress.getByName("www.baidu.com");//java.net.UnknownHostException ~~~ ### socket 为网络服务提供的一种机制,通信的两端都有 Socket,网络通信其实就是 Socket 间的通信,数据在两个 Socket 间通过 IO 传输。 ### udp传输 数据一定要封装到数据包中,数据包中包括目的地址、端口、数据等信息。将 udp 封装成对象,易于我们的使用,这个对象就是 DatagramSocket 发送端: 1,建立 udp 的 socket 服务,创建对象时如果没有明确端口,系统会自动分配一个未被使用的端口。 2,明确要发送的具体数据。 3,将数据封装成了数据包。 4,用 socket 服务的 send 方法将数据包发送出去。 5,关闭资源。 ~~~ import java.net.*; class UdpSend{ public static void main(String[] args)throws Exception { // 1 1 ,建立 p udp 的 的 t socket 服务。 DatagramSocket ds = new DatagramSocket(8888);//指定发送端口,不指定系统会随机分配。 // 2 2 ,明确要发送的具体数据。 String text = "udp 传输演示 哥们来了"; byte[] buf = text.getBytes(); // 3 3 ,将数据封装成了数据包。 DatagramPacket dp = new DatagramPacket(buf, buf.length,InetAddress.getByName("10.1.31.127"),10000); // 4 4 ,用 t socket 服务的 d send 方法将数据包发送出去。 ds.send(dp); // 5 5 ,关闭资源。 ds.close(); } } ~~~ p udp 的接收端: 1,创建 udp 的 socket 服务,必须要明确一个端口,作用在于,只有发送到这个端口的数据才是这个接收端可 以处理的数据。 2,定义数据包,用于存储接收到数据。 3,通过 socket 服务的接收方法将收到的数据存储到数据包中。 4,通过数据包的方法获取数据包中的具体数据内容,比如 ip、端口、数据等等。 5,关闭资源。 ~~~ class UdpRece { public static void main(String[] args) throws Exception{ // 1 1 ,创建 p udp 的 的 t socket 服务。 DatagramSocket ds = new DatagramSocket(10000); // 2 2 ,定义数据包,用于存储接收到数据。先定义字节数组,数据包会把数据存储到字节数组中。 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); // 3 3 ,通过 t socket 服务的接收方法将收到的数据存储到数据包中。 ds.receive(dp);//该方法是阻塞式方法。 // 4 4 ,通过数据包的方法获取数据包中的具体数据内容,比如 ip ,端口,数据等等。 String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); String text = new String(dp.getData(),0,dp.getLength());//将字节数组中的有效部分转成字符串。 System.out.println(ip+":"+port+"--"+text); // 5 5 ,关闭资源。 ds.close(); } } ~~~ ### tcp传输 两个端点的建立连接后会有一个传输数据的通道,这通道称为流,而且是建立在网络基础上的流, 称之为 socket 流。该流中既有读取,也有写入。 TCP 客户端 : 1,建立 tcp 的 socket 服务,最好明确具体的地址和端口。这个对象在创建时,就已经可以对指定 ip 和端口 进行连接(三次握手)。 2,如果连接成功,就意味着通道建立了,socket 流就已经产生了。只要获取到 socket 流中的读取流和写入 流即可,只要通过 getInputStream 和 getOutputStream 就可以获取两个流对象。 3,关闭资源。 ~~~ import java.net.*; import java.io.*; //需求:客户端给服务器端发送一个数据。 class TcpClient{ public static void main(String[] args) throws Exception{ Socket s = new Socket("10.1.31.69",10002); OutputStream out = s.getOutputStream();// 获取了 t socket 流中的输出流对象。 out.write("tcp 演示,哥们又来了!".getBytes()); s.close(); } } ~~~ TCP 服务端: 1,创建服务端 socket 服务,并监听一个端口。 2,服务端为了给客户端提供服务,获取客户端的内容,可以通过 accept 方法获取连接过来的客户端对象。 3,可以通过获取到的 socket 对象中的 socket 流和具体的客户端进行通讯。 4,如果通讯结束,关闭资源。注意:要先关客户端,再关服务端。 ~~~ class TcpServer{ public static void main(String[] args) throws Exception{ 62 / 65 ServerSocket ss = new ServerSocket(10002);//建立服务端的 socket 服务 Socket s = ss.accept();//获取客户端对象 String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+".....connected"); // 可以通过获取到的 socket 对象中的 socket 流和具体的客户端进行通讯。 InputStream in = s.getInputStream();//读取客户端的数据,使用客户端对象的 socket 读取流 byte[] buf = new byte[1024]; int len = in.read(buf); String text = new String(buf,0,len); System.out.println(text); // 如果通讯结束,关闭资源。 注意:要先关客户端,在关服务端。 s.close(); ss.close(); } } ~~~ ### 反射技术 反射技术可以对一个类进行解剖。 - 基本步骤: 1 、 获得 s Class 对象 ,就是获取到指定的名称的字节码文件对象 。 2 、 实例化对象, 获得类的属性、方法或构造函数。 3、访问属性、调用方法、调用构造函数创建对象 ## 正则表达式 - 常见操作: 1,匹配:其实用的就是 String 类中的 matches 方法。 String reg = “[1-9][0-9]{4,14}”; boolean b = qq. matches(reg);//将正则和字符串关联对字符串进行匹配。 2,切割:其实用的就是 String 类中的 split 方法。 3,替换:其实用的就是 String 类中的 replaceAll(); 4,获取: 1),先要将正则表达式编译成正则对象。使用的是 Pattern 中静态方法 compile(regex); 2),通过 Pattern 对象获取 Matcher 对象。 Pattern 用于描述正则表达式,可以对正则表达式进行解析。 而将规则操作字符串,需要从新封装到匹配器对象 Matcher 中。 然后使用 Matcher 对象的方法来操作字符串。 如何获取匹配器对象呢? 通过 Pattern 对象中的 matcher 方法。该方法可以正则规则和字符串想关联。并返回匹配器对象。 3),使用 Matcher 对象中的方法即可对字符串进行各种正则操作。
';

剖析软件外包项目

最后更新于:2022-04-01 14:32:16

最近看了一本书,叫做《程序员接单宝典》,虽然只有短短的70多页,但是写得的确不错,里面的很多经验都值得我们学习和探讨,于是我借鉴这位大神的经验,来浅要的谈谈接外包项目的事情。同时也有很多精彩的摘要。我在这里就将这本书浓缩为我这篇博文了。 **一、接项目前的准备工作** 书中讲到了需要创建一个团队,当然对于很多学生或者初级开发者来说,似乎很难创建一个好的team来工作,或许我的想法是可以把自己之前做的一个项目或者demo,同时整理几个框架出来,便于之后更为敏捷的开发。同时作者也建议我们不要杂七杂八去接项目,当然都是要做自己熟悉的,”专业品质“。使用应用框架有五大优点:模块化,可重用性,可扩展性,简单性,可维护性。 **二、该上哪儿去找项目** 1、朋友介绍 程序员也要学会多交朋友,不要沉迷在一行行代码里面。技术固然重要,但友情也不能忽视。写得一手好的代码,固然值得称赞,但结交一些值得信任的朋友,在你困难的时候能给你伸出援助之手,才是更为重要的事情。 第二是把客户转化为朋友,“我们在合作时就要注意,尽量减少客户不必要的开支,不要一心只想着多赚钱而使客户对你产生反感。再就是多做些业务之外的事情,就是客户有什么事情时你可以主动帮忙,比如他们需要某些资料又得不到时,我们就会帮他搞到。甚至,他们生活中碰到的一些困难,只要我们知道又能做到时,就一定会帮助他们,这样,我们与客户就不再是合作的关系了,更多的就是朋友了。这样,一旦有什么大的项目时,他们一定会先想到我们。” 2、通过外包网站接单 书中介绍了四个国内的外包网站,拥有十年以上的外包网站,有的需要付费,但是我还是赞同的。“绝大多数的程序员,即使愿意去外包网站接项目,但是一听说是这个网站是要收费的,立马就拍拍屁股走人了。他们总想着免费接项目的好事情,其实,天下永远没有免费的午餐,凡是给你提供有价值的东西的,总是要收取一些相应的费用。就比如阁下您吧,你愿意免费替别人开发项目吗?恐怕不肯吧,如果有愿意免费开发的,请立即联系我,有多少这样的人我收多少,我还正愁找不到人来做项目呢。” 当然,这些网站我都已经收藏好了,第一个,软件商务网( http://www.bizsofts.com )、第二个,软件项目交易网( http://www.sxsoft.com )、第三个,CSTO外包网站( http://www.csto.com )、第四个,智城外包网 ( http://www.taskcity.com )。当然,还有其他的国外的网站,但是我相信可以逛得起这些英文网站的,英语水平那是肯定牛逼啊,那肯定不需要我介绍啦! 3、通过其他方式接单 拿书中的原话举个例子,“如果你们团队是做网站的,就可以上网找那种一两年不更新的企业网站,诚恳的告诉他们网站有很久没更新了,且布局凌乱、色调混乱,与自身的企业形象不相符,然后你告诉对方一些成功的案例,最好是对方的同行,因为做了好的网站而使得营销大增,让对方有了紧迫感。而且建议你避开大城市,去一些中小城市接单,据我所知,一些中小型城市的民众的电脑水平很低,也很少有人和你竞争,你可以尽情地忽悠得了。 不过呢,你得要有很好的表达能力,另外你得做好被别人推出门外的准备。你见过经常在小区转悠的推销人员吗?就是像这种人一样地去拜访陌生客户。但是鉴于一般的程序员都是脸皮薄的宅男,还是专门请一个业务做为团队的营销人员的为好,否则几次被人家拒之门外后,一般的程序员 是再没有勇气出去揽客的。” 我觉得吧,其他方式应该还可以通过搜索引擎,论坛、淘宝等,不过都有优点和缺点。 **三、如何才能争到项目** 这里首先要牢记的一个字是快:就是比别人抢先一步先联系客户。客户都有一种先入为主的思想,总是对于第一个联系他的人印象比较深刻,如果价格方面又满意的话,你就几乎是成功了一半。与客户联系上后的第一步,不是去谈什么需求,而是先要展示你们团队的实力。 “另外,你在向对方介绍你们团队实力的同时,也要通过积极的沟通,了解对方的底细。为什么要这样做呢?因为在现实的外包实践中,虽然大部分的发包方都是抱着真实的外包意愿来与你谈判的,但也不排除有少部分的发包方其实只是为了骗取解决方案、骗取报价、骗取案例、骗取源代码,甚至是借外包之名骗取网站点击率的。如果对方要求提供完整的解决方案,这应该只是想套取设计方案而已,发几张你们团队的推介广告和报价单给他即可。 还有的客户张口就要源码要设计文档设计方案的,这种人目的性太强了,如果你真给了他就再也不理你啦。还有的外包方死活不肯介绍自己,不肯告诉自己是谁、怎么称呼、怎么联系、是什么公司、做什么业务的,与这种连最基本的诚信都没有的客户就根本没有必要谈下去。还有的发包者说是要找人设计一个网站,但全部的需求就是要仿制某某网站,没有其他的要求,这可能是想借外包推广自己网站的,就是骗大家都去点击这个网址。如果你经常外包的话,相信你不久就会有一定的判断能力的。” 你在与客户谈需求的同时,要对客户做一个大致的了解,比如他是真心外包项目还是只是想打听一下价格;他是发的原始包还是转包,他的经济状况如何——是有经济实力的公司还是个只是人或者学生;他是外包的新手还是已经外包多次的老手甚至就是外包界的同行。 销售过程主要分了六步:第一步:探寻客户基本需求;第二步:通过纵深提问挖掘需求背后的原因;第三步:激发客户需求;第四步:引导客户解决问题;第五步:抛出解决方案并顺便达成交易;第六步:成交之后与客户建立长期的关系。 软件外包也有一个统一的计价标准,具体来说,外包费用=每位程序员每天的人工成本×项目所需要的工期(天数)×人数。 实际上首次交流一般是不能报价的,因为需求并没有完全明确,你还不能确定工作量到底有多少,所以这时你只能告诉客户你的报价原则,比如做一天需要多少钱,为什么需要这么多钱。你的后期服务能达到什么程度。 首先通过交流粗略估算出客户所处的社会层次及从事的工作,以及客户的背景是属于政府部门,还是大集团,还是小企业,或只是个人,甚至只是个没有什么支付能力的学生。通过报一个相对较高的价格来测试客户的反应,看看他对这个价格是怎么看的,你就大概知道他属于什么类型的客户了。当然,报价不要高的离谱,否则他就可能掉头就走,再不理你了。逐步地降低价格,慢慢接近他的价格底线。只要试探出了他的大概的心理价位,你就成功了大半。剩下的就是通过你的分析需求来说服他接受你的价格了。 项目开发合同主要包括:项目内容、开发费用、开发周期、验收标准、付款方式、保密协议,软件所有权、知识产权归属等等以及其他甲乙双方权利和义务还有违约责任。除了这份正式的合作,一般还应该有一份软件开发方案书,作为附件,对软件开发的需求分析、软件设计、程序编码、功能测试和后期维护五个过程如何去实现的一个总体设计思路和方案。 **四、如何才能完成项目** 1、团队成员紧密沟通: 无论是书中所说的team协作还是leader管理,这些都是非常有必要的,而且是很重要的方面。 2、团队与客户间沟通 一般来说,我建议使用瀑布式的开发模式,就是每完成一个阶段,就和用户及时的沟通,看看这个阶段的东西是不是用户想要的,如果是,那就开始下个阶段的开发,不是,那就进行修改。 3、需求更改的问题 既然用户需求不断变化是一种客观规律,那么程序员在心态上就要进行调整,将用户需求不能变化调整为用户需求一定会变化,树立起“变正常,不变不正常”的观点。做国内的外包要保持一个好的心态,遇到改要坚强,改十遍八遍我觉得很正常,接受不了的接单时就慎重。有了这样的心态,我们就不会去抱怨用户需求了,就会对用户需求的变更有更多的理解。 **五、如何收项目的尾款** 1、不作超出实力的承诺 切记:在接下项目之前要先考虑一下,自己有没有这个实力做,要很客观地对自己团队的实力做一个评价. 2、把开发细节列入合同 细节那也是相当重要的,同时书中作者也说了,细节决定整个项目的运行情况,所以我们还是要非常非常关注细节。 3、多与对方沟通互动 沟通技巧:热情、关注、喜欢、宽容、尊重 4、不见兔子不撒鹰 最后一招也是最有效的解决办法:不见尾款不交源 代码。 **六、项目的实施和维护** 软件项目后期维护的费用问题,定制软件不论收费多少,一般是要有一年或至少半年的维护期的,在这段时间内,软件自身的 BUG要免费的,无条件的修改。客户提出新要求,新功能,则另外收费。总之售后分为两部分:一是软件作者自身的问题,这个是无条件,免费的修正。二是客户新的要求,新的功能,这个是客户的原因,肯定要收费。在软件维护期满以后,如果客户还需要你继续维护的话,就需要收取一定的费用了。 写在最后 就我个人认为,外包对于程序员来说,是一个必须要经过的阶段。,通过这个阶段,首先可以打造一支自己的团队。要知道,真正成功的软件公司的核心骨干,并不是靠成立公司后招聘来的,而是人家作为团队时就在一起打拼出来的,也只有这样的团队才能为公司的发展提供良好的人和基础。比如马云的阿 里巴巴团队。 其次,通过外包可以为以后公司的成立积累资金。大家都知道,作为一家正规的公司,各方面的开销是很巨大的,人员工资,办公场所,各种税费,水费电费等等,没有一定的资金储备是不行的。而通过外包就可以积累起这些资金。再次,通过外包,可以积累团队的技术实力,提高团队的技术水平,为以后成立公司打造核心产品提供必要的技术积累。 最后书中给出的是作者的一些案例,让我很是受教,平时休息无事的时候多看一些书是非常好的事情,也可以让我不会那么颓废,可以让我整个人更加精神倍发,好了,就写到这里吧!本人编辑能力有限,还望见谅,不喜勿喷![大笑](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-19_569e21abc5518.gif)
';

java基础知识总结

最后更新于:2022-04-01 14:32:13

java在安卓的开发中是非常重要的,所以今天我总结了一下java的基础知识,当然,还有一些没有总结完,例如网络编程,io流等,将会在下一篇博文中写出。 ## 概述 javac :负责的是编译的部分 java :负责运行的部分.会启动 jvm.加载运行时所需的类库,并对 class 文件进行执行 ## 语法规则 ### 数据类型 1 1 ): 基本数据类型:byte(1个字节)、short(2)、int(4)、long(8)、float(4)、double(8)、char(2)、boolean。 1个字节占8位 2 2 ): 引用数据 类型: 数组、类、接口。 ### 运算符 > *+ - * / % %:任何整数模 2 不是 0 就是 1,所以只要改变被模数就可以实现开关运算 > &: 只有两边都为 true 结果是 true。否则就是 false。 > |:只要两边都为 false 结果是 false,否则就是 true > & 和 && 区别: & :无论左边结果是什么,右边都参与运算。 &&: 短路与,如果左边为 false,那么右边不参数与运算。 > | 和 || 区别:|:两边都运算。 || : 短路或,如果左边为 true,那么右边不参与运算。 > ++X(–X)表示在使用x之前,先使x的值增(减)1 ### 语句 If switch do while while for 当判断固定个数的值的时候,建议使用 switch,效率相对较高。 当判断数据范围,获取判断运算结果 boolean 类型时,需要使用 if。 当某些语句需要执行很多次时,就用循环结构,建议使用 for。因为 for 循环完毕,变量在内存中释放。 ### 函数 重载的定义是:在一个类中,如果出现了两个或者两个以上的同名函数,只要它们的参数的个数,或者参数的 类型不同,即可称之为该函数重载了。 如何区分重载:当函数同名时,只看参数列表。和返回值类型没关系。 ### 数据 ~~~ // 二分查找法。必须有前提: 数组中的元素要有序。 public static int halfSeach_2(int[] arr,int key){ int min,max,mid; min = 0; max = arr.length-1; mid = (max+min)>>1; //(max+min)/2; while(arr[mid]!=key){ if(key>arr[mid]){ min = mid + 1; } else if(key<arr[mid]) max = mid - 1; if(max<min) return -1; mid = (max+min)>>1; } return mid; } ~~~ java 分了 5 5 片内存。 1 :寄存器。2 :本地方法区。3 :方法区。4 :栈。5 :堆。 ~~~ 栈:存储的都是局部变量 ( 函数中定义的变量,函数上的参数,语句中的变量 ); 只要数据运算完成所在的区域结束,该数据就会被释放。后进先出。 堆:用于存储数组和对象,也就是 实体。啥是实体啊?就是用于封装多个数据的。 1 :每一个实体都有内存首地址值。 2 :堆内存中的变量都有默认初始化值。因为数据类型不同,值也不一样。 3 :垃圾回收机制。 ~~~ ## 面向对象 ### 类 属性是用于存储数据的 , 直接被访问,容易出现安全隐患 , 所以,类中的属性通 常被私有化,并对外提供公共的访问方法。 这个方法一般有两个,规范写法:对于属性 xxx ,可以使用 setXXX(),getXXX() 对其进行操作 主函数的存在,仅为该类是否需要独立运行 , 如果不需要,主函数是不用定义的。 主函数的解释:保证所在类的独立运行,是程序的入口,被 jvm 调用。 构造函数是在对象创建时,就被调用,用于初始化, 而且初始化动作只执行一次。 一般函数,是对象创建后,需要调用才执行,可以被调用多次。 ### 封装 将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。 ### this 用 this 调用构造函数,必须定义在构造函数的第一行。因为构造函数是用于初始化的,所以初 始化动作一定要执行。 否则编译失败。 ### static ~~~ 关键字,是一个修饰符 , 用于修饰成员( ( 成员变量和成员函数) ) 静态方法只能访问静态成员,不可以访问非静态成员。 静态方法中不能使用 this ,super 关键字。 成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。 静态代码块、构造代码块、构造函数同时存在时的执行顺序: 静态代码块 à 构造代码块 à 构造函数 ~~~ ### 单例设计模式 保证一个类在内存中的对象唯一性。 步骤: 1 1 ,因为创建对象都需要构造函数初始化 ,只要将本类中的构造函数私有化,其他程序就无法 再 创建 该类对象; 2 2 ,就在类中创建一个本类的对象 ; 3 3 ,定义一个方法,。 返回该对象,让其他程序可以通过方法就得到本类对象。 (作用:可控) 代码体现: 1 1 ,私有化构造函数 ; 2 2 ,创建私有并静态的本类对象 ; 3 3 ,定义公有并静态的方法,返回该对象 ~~~ //饿汉式 class Single{ private Single(){} // 私有化构造函数。 private static Single s = new Single(); // 创建私有并静态的本类对象。 public static Single getInstance(){ // 定义公有并静态的方法,返回该对象。 return s; } } //懒汉式:延迟加载方式。 class Single2{ private Single2(){} private static Single2 s = null; public static Single2 getInstance(){ if(s==null) s = new Single2(); return s; } } ~~~ ### 继承 java 中对于继承,java 只支持单继承。java 虽然不直接支持多继承,但是保留了这种多继承机制,进行改良。 java 支持多重继承。A 继承 B B 继承 C C 继承 D。 子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程。 super()和 和 this() 不可以同时出现的构造函数中。 在方法覆盖时,注意两点: 1:子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译 失败。 2:覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖) ### final 可以修饰类,方法,变量。不可以被继承、覆盖。 ### 抽象类 抽象类的特点: 1 1 : 抽象方法只能定义在 抽象类中 , 抽象类和抽象方法必须由 t abstract 关键字修饰 (可以描述类和 ) 方法,不可以描述变量) 。 2 2 : 抽象方法只定义方法声明,并不定义方法实现。 3 3 : 抽象类不可以被创建对象( ( 实例化) ) 。 4 4 : 只有通过子类继承抽象类并覆盖了抽象类中的 所有 抽象方法后,该子类 才 可以实例化。否则, 该子类还是一个抽象类。 抽象类可以大一非抽象方法,抽象类中有构造函数。 ### 接口 接口中有抽象方法,接口不可以实例化。 接口的子类必须实现了接口 中 所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。 类与类之间存在着继承关系,类与接口中间存在的是 实现关系。 继承用 extends ; 实现用 implements ### 多态 体现: 父类引用或者接口的引用指向了自己的子类对象。 //Animal a = new Cat(); instanceof ;// 判断对象是否实现了指定的接口或继承了指定的类 ### Object ~~~ public boolean equals(Object obj){ if(!(obj instanceof Person)) return false; Person p = (Person)obj; return this.age == p.age; } ~~~ 通常 equals , toString , hashCode ,在应用中都会被复写 , 建立具体对象的特有的内容。 ### 内部类 如果 A 类需要直接访问 B 类中的成员,而 B 类又需要建立 A 类的对象。这时,为了方便设计和访问, 直接将 A 类定义在 B 类中。就可以了。A 类就称为 内部类。内部类可以直接访问外部类中的成员。而外部类想要 访问内部类,必须要建立内部类的对象。 ~~~ class Outer{ int num = 4; class Inner { void show(){ System.out.println("inner show run "+num); } } public void method(){ Inner in = new Inner();//创建内部类的对象。 in.show();//调用内部类的方法。 } } ~~~ 匿名内部类: 当函数的参数是接口类型引用时,如果接口中的方法不超过 3 个。可以通过匿名内部类来完成参数的传递。 ### 异常 > 通过 throws 关键字完成, 格式: throws 异常类名, , 异常类名 > throw 用于抛出异常对象,后面跟的是异常对象;throw 用在函数内。 throws 用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws 用在函数上。 > 运行时异常,其中 Exception 有一个特殊的子类 RuntimeException,以及 RuntimeException 的子类是运 行异常,也就说这个异常是编译时不被检查的异常。 > finally 很有用,只要用户关闭资源。无论是否发生异常,资源都必须进行关闭。 System.exit(0); //退出 jvm,只有这种情况 finally 不执行。 常见 异常 : 1、脚标越界异常(IndexOutOfBoundsException)包括数组、字符串; 空指针异常(NullPointerException) 2、类型转换异常:ClassCastException 3、不支持操作异常; ### 包 常见的软件包: > java.lang : language java 的核心包,Object System String Throwable jdk1.2 版本后,该 包中的类自动被导入。 > java.awt : 定义的都是用于 java 图形界面开发的对象。 > javax.swing: : 提供所有的 windows 桌面应用程序包括的控件, 比如: Frame , Dialog, Table, List 等等, , 就是 java 的图形界面库 。 > java.net : 用于 java 网络编程方面的对象都在该包中。 > java.io : input output 用于操作设备上数据的对象都在该包中。比如:读取硬盘数据,往硬盘写 入数据。 > java.util a : java 的工具包,时间对象 , 集合框架。 > java.applet : application+let 端 客户端 a java 小程序。 server+let – > servlet 端 服务端 java 小程序 ### 多线程 创建线程: 方法1: ~~~ 步骤: 1,定义类继承 Thread 类; 2,目的是复写 run 方法,将要让线程运行的代码都存储到 run 方法中; 3,通过创建 Thread 类的子类对象,创建线程对象; 4,调用线程的 start 方法,开启线程,并执行 run 方法。 ~~~ 方法2: ~~~ 步骤: 1,定义类实现 Runnable 接口。 2,覆盖接口中的 run 方法(用于封装线程要运行的代码)。 3,通过 Thread 类创建线程对象; 4,将实现了 Runnable 接口的子类对象作为实际参数传递给 Thread 类中的构造函数。 为什么要传递呢?因为要让线程对象明确要运行的 run 方法所属的对象。 5,调用 Thread 对象的 start 方法。开启线程,并运行 Runnable 接口子类中的 run 方法。 Ticket t = new Ticket(); /* 直接创建 Ticket 对象,并不是创建线程对象。 因为创建对象只能通过 new Thread 类,或者 new Thread 类的子类才可以。 所以最终想要创建线程。既然没有了 Thread 类的子类,就只能用 Thread 类。 `*/` Thread t1 = new Thread(t); //创建线程。 /* 只要将 t 作为 Thread 类的构造函数的实际参数传入即可完成线程对象和 t 之间的关联 为什么要将 t 传给 Thread 类的构造函数呢?其实就是为了明确线程要运行的代码 run 方法。 `*/` t1.start(); ~~~ ### 同步 同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。 同步函数使用的锁是 this , 静态同步函数的锁是 该类的字节码文件对象。 同步是隐示的锁操作,而 k Lock 对象是显示的锁操作,它的出现就替代了同步。 ## API ### 字符串 ~~~ parseInt(string,radix); //将给定的数转成指定的基数进制; 在 在 5 jdk1.5 版本后,对基本数据类型对象包装类进行升级。在升级中,使用基本数据类型对象包装类可 以像使用基本数据类型一样,进行运算。 Integer i = new Integer(4); 5 //1.5 版本之前的写法; Integer i = 4; // 自动装箱 ,5 1.5 版本后的写法; i = i + 5; //i 对象是不能直接和 5 相加的,其实底层先将 i 转成 int 类型,在和 5 相加。而转成 int 类型的操作是隐 式的。 自动拆箱:拆箱的原理就是 i.intValue();i+5 运算完是一个 int 整数。 ~~~ ### 集合框架 1:对象封装数据,对象多了也需要存储。 集合用于存储对象。 2:对象的个数确定可以使用数组,但是不确定怎么办?可以用集合。因为 ### List接口 ~~~ List : 有序( ( 元素存入集合的顺序和取出的顺序一致) ) ,元素都有索引。元素可以重复。 | | -- ArrayList : 底层的数据结构 是数组, , 线程不同步 ,A At rrayList 了 替代了 Vector , 查询元素的速 度非常快。 | | -- LinkedList : 底层的数据结构是链表 , 线程不同步 , 增删元素的速度非常快。 | | -- Vector : 底层的数据结构就是数组 , 线程同步的 ,r Vector 无论查询和增删都巨慢。 ~~~ 对于 t list 集合,底层判断元素是否相同,其实用的是元素自身的 s equals 方法完成的。所以建议 元素都要复写 s equals ~~~ LinkedList : 的特有方法。 addFirst(); addLast(); 在 jdk1.6 以后。 offerFirst(); offerLast(); getFirst():获取链表中的第一个元素。如果链表为空,抛出 NoSuchElementException; getLast(); 在 jdk1.6 以后。 peekFirst();获取链表中的第一个元素。如果链表为空,返回 null。 peekLast(); removeFirst():获取链表中的第一个元素,但是会删除链表中的第一个元素。如果链表为空,抛出 NoSuchElementException removeLast(); 在 jdk1.6 以后。 pollFirst();获取链表中的第一个元素,但是会删除链表中的第一个元素。如果链表为空,返回 null。 pollLast(); ~~~ ### Set接口 当元素的 hashCode 值相同时,才继续判断元素的 equals 是否为 true。 ~~~ TreeSet: 用于对 Set 集合进行元素的指定顺序排序,排序需要依据元素自身具备的比较性。 如果元素不具备比较性,在运行时会发生 n ClassCastException 异常。 所以需要元素 实现 e Comparable 接口,强制让元素具备比较性, 复写 o compareTo 方法。 ~~~ ### Map集合 – Hashtable :底层是哈希表数据结构,是线程同步的。不可以存储 null 键,null 值。 – HashMap :底层是哈希表数据结构,是线程不同步的。可以存储 null 键,null 值。替代了 Hashtable. – TreeMap :底层是二叉树结构,可以对 map 集合中的键进行指定顺序的排序。 ### 把 把 p map 集合转成 t set 的方法 Set keySet(); Set entrySet(); //取的是键和值的映射关系。 ~~~ 取出 p map 集合中所有元素的方式一 : keySet() 方法。 可以将 map 集合中的键都取出存放到 set 集合中。对 set 集合进行迭代。迭代完成,再通过 get 方法对获取 到的键进行值的获取。 Set keySet = map.keySet(); Iterator it = keySet.iterator(); w w hile(it.hasNext()) { { Object key = it.next(); Object value = map.get(key); System.out.println(key+":"+value); } } 取出 p map 集合中所有元素的方式 二 : entry Set() 方法。 Set entrySet = map.entrySet(); Iterator it = entrySet.iterator(); while(it.hasNext()) { Map.Entry me = (Map.Entry)it.next(); System.out.println(me. getKey()+"::::"+me. getValue()); ~~~ ### 使用集合的技巧: ~~~ 看到 Array 就是数组结构,有角标,查询速度很快。 看到 link 就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast(); 看到 hash 就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖 hashCode,equals 方法。 看到 tree 就是二叉树,就要想到排序,就想要用到比较。 比较的两种方式 : 一个是 Comparable : 覆盖 o compareTo 方法 ; 一个是 Comparator : 覆盖 e compare 方法。 LinkedHashSet,LinkedHashMap:这两个集合可以保证哈希表有存入顺序和取出顺序一致,保证哈希表有序。 集合什么时候用? 当存储的是一个元素时,就用 Collection。当存储对象之间存在着映射关系时,就使用 Map 集合。 保证唯一,就用 Set 。不保证唯一 , 就用 List ~~~ ### 双列集合 Map ~~~ Map map = new HashMap(); map.put("a", "aaa"); // 传统方式:必须掌握这种方式 Set entrys = map.entrySet(); // 1.获得所有的键值对 Entry 对象 iter = entrys.iterator(); // 2.迭代出所有的 entry while(iter.hasNext()) { Map.Entry entry = (Entry) iter.next(); String key = (String) entry.getKey(); // 分别获得 key 和 value String value = (String) entry.getValue(); System.out.println(key + "=" + value); } ~~~ ### 泛型 当类中的操作的引用数据类型不确定的时候,以前用的 t Object 来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到的编译时期。 ~~~ class Tool<Q> { private Q obj; public void setObject(Q obj) { this.obj = obj; } public Q getObject() { return obj; } } ~~~ ### Data和日历类 将日期字符串转换成日期对象 :是 使用的就是 DateFormat 方法中的 Date parse(String source) ~~~ public static void method(){ Calendar c = Calendar.getInstance(); System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月" +getNum(c.get(Calendar.DAY_OF_MONTH))+"日" +"星期"+getWeek(c.get(Calendar.DAY_OF_WEEK))); } public static String getNum(int num){ return num>9 ? num+"" : "0"+num; } public static String getWeek(int index){ /* 查表法:建立数据的对应关系. 最好:数据个数是确定的,而且有对应关系。如果对应关系的一方,是数字,而且可以作为角标,那么可以 通过数组来作为表。 */ String[] weeks = {"","日","一","二","三","四","五","六"}; return weeks[index]; } ~~~
';

android开发常用工具箱

最后更新于:2022-04-01 14:32:11

我的工具包资料目录 我的个人总结,最近做的项目需要了的一些资料,感觉挺乱的,然后现在整理了一下。 Jar包 <table border="1" cellspacing="0" cellpadding="0" align="left" width="861"><tbody><tr><td valign="top"><p align="center">包名</p></td><td valign="top"><p align="center">版本号</p></td><td valign="top"><p align="center">作用</p></td><td valign="top"><p align="center">下载地址</p></td></tr><tr><td valign="top"><p align="center">xUtils</p></td><td valign="top"><p align="center">2.6.14和3.1.26</p></td><td valign="top"><p align="center">大文件上传下载等</p></td><td valign="top"><p align="center">旧版本:<a target="_blank" href="https://github.com/wyouflf/xUtils">https://github.com/wyouflf/xUtils</a></p><p align="center">新:<a target="_blank" href="https://codeload.github.com/wyouflf/xUtils3/zip/master">https://codeload.github.com/wyouflf/xUtils3/zip/master</a></p></td></tr><tr><td valign="top"><p align="center">Gson</p></td><td valign="top"><p align="center">2.2.3</p></td><td valign="top"><p align="center">Json解析</p></td><td valign="top"><p align="center"><a target="_blank" href="https://github.com/google/gson">https://github.com/google/gson</a></p></td></tr><tr><td valign="top"><p align="center">Cocos2d-android</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">安卓游戏引擎</p></td><td valign="top"><p align="center"><a target="_blank" href="http://www.cocos2d-x.org/download">http://www.cocos2d-x.org/download</a></p><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">Pinyin4j</p></td><td valign="top"><p align="center">2.5.0</p></td><td valign="top"><p align="center">汉字转拼音</p></td><td valign="top"><p align="center"><a target="_blank" href="http://pinyin4j.sourceforge.net/">http://pinyin4j.sourceforge.net/</a></p></td></tr><tr><td valign="top"><p align="center">nineoldandroids</p></td><td valign="top"><p align="center">2.4.0</p></td><td valign="top"><p align="center">开源动画库</p></td><td valign="top"><p align="center"><a target="_blank" href="http://nineoldandroids.com/">http://nineoldandroids.com/</a></p><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">BaiduLBS</p></td><td valign="top"><p align="center">V3.6</p></td><td valign="top"><p align="center">百度地图sdk</p></td><td valign="top"><p align="center"><a target="_blank" href="http://developer.baidu.com/map/">http://developer.baidu.com/map/</a></p></td></tr><tr><td valign="top"><p align="center">Smart Image View</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">从URL和通讯录中获取图像</p></td><td valign="top"><p align="center"><a target="_blank" href="http://loopj.com/android-smart-image-view/">http://loopj.com/android-smart-image-view/</a></p><p align="center"><a target="_blank" href="https://github.com/loopj/android-smart-image-view">https://github.com/loopj/android-smart-image-view</a></p><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">android-async-http-master</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">获取网络数据或者向服务器发送数据</p></td><td valign="top"><p align="center"><a target="_blank" href="https://github.com/loopj/android-async-http">https://github.com/loopj/android-async-http</a></p><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">RootTools</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">一款root权限破解工具</p></td><td valign="top"><p align="center"><a target="_blank" href="https://github.com/Stericson/RootTools">https://github.com/Stericson/RootTools</a></p></td></tr></tbody></table> 第三方库: <table border="1" cellspacing="0" cellpadding="0" width="797"><tbody><tr><td valign="top"><p align="center">包名</p></td><td valign="top"><p align="center">版本号</p></td><td valign="top"><p align="center">作用</p></td><td valign="top"><p align="center">下载地址</p></td></tr><tr><td valign="top"><p>SlidingMenu-master</p></td><td valign="top"><p>未知</p></td><td valign="top"><p>侧滑</p></td><td valign="top"><p><a target="_blank" href="https://github.com/jfeinstein10/SlidingMenu">https://github.com/jfeinstein10/SlidingMenu</a></p></td></tr><tr><td valign="top"><p>Android SMS SDK</p></td><td valign="top"><p>v 2.0.0</p></td><td valign="top"><p>mob的短信验证码</p></td><td valign="top"><p><a target="_blank" href="http://www.mob.com/#/downloadDetail/SMS/android">http://www.mob.com/#/downloadDetail/SMS/android</a></p></td></tr><tr><td valign="top"><p>PagerSlidingTabStrip-master</p></td><td valign="top"><p>未知</p></td><td valign="top"><p>TAB导航栏</p></td><td valign="top"><p> <a target="_blank" href="https://github.com/astuetz/PagerSlidingTabStrip">https://github.com/astuetz/PagerSlidingTabStrip</a></p></td></tr><tr><td valign="top"><p>Android-ViewPagerIndicator-master</p></td><td valign="top"><p>2.4.1</p></td><td valign="top"><p>分页指示器库</p></td><td valign="top"><p><a target="_blank" href="https://github.com/JakeWharton/ViewPagerIndicator">https://github.com/JakeWharton/ViewPagerIndicator</a></p><p> </p></td></tr></tbody></table>   软件 <table border="1" cellspacing="0" cellpadding="0" width="797"><tbody><tr><td valign="top"><p align="center">包名</p></td><td valign="top"><p align="center">版本号</p></td><td valign="top"><p align="center">作用</p></td><td valign="top"><p align="center">下载地址</p></td></tr><tr><td valign="top"><p align="center">Android逆向助手</p></td><td valign="top"><p align="center">v2.0</p></td><td valign="top"><p align="center">反编译软件</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">svn工具</p></td><td valign="top"><p align="center">2.73</p></td><td valign="top"><p align="center">版本管理工具</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">git</p></td><td valign="top"><p align="center">1.95</p></td><td valign="top"><p align="center">版本管理工具</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">Mindmanager_</p></td><td valign="top"><p align="center">9.1.157</p></td><td valign="top"><p align="center">思维导图软件</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">Color</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">取色器</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">HiJson</p></td><td valign="top"><p align="center">2.12</p></td><td valign="top"><p align="center">Json 格式化工具</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">size</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">尺寸测量工具</p></td><td valign="top"><p align="center"> </p></td></tr><tr><td valign="top"><p align="center">yaffs2img浏览器</p></td><td valign="top"><p align="center">未知</p></td><td valign="top"><p align="center">Android的img文件处理</p></td><td valign="top"><p align="center"> </p></td></tr></tbody></table> 其他资料: jni、linux    
';

安卓图像处理入门教程

最后更新于:2022-04-01 14:32:09

## 第1章 图像处理概念介绍 ### 1、RGBA模型分析 ~~~ RGB:red,green,blue,alpha 色相:物体传递的颜色 饱和度:颜色的灰度,从0(灰)-100% ~~~ 亮度:颜色的相对明暗程度 ColorMatrix setRotate() setSaturation() setScale() postConcat() ## 第2章 颜色矩阵变换与实例 图片中矩阵的作用通常用于初始化颜色矩阵 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6d2ec02.jpg "") ## 第3章 像素点分析与实例 1.使用getPixels()方法可获取图片的所有点的颜色值,一般采用以下格式:bitmap.getPixels(int[] ,0,width,0,0,width,height)。 参数说明:存放像素值的数组->最开始读取像素时的偏移量->行距(多少算作一行,一般去width)->(x,y)第一次读取参数的目标->要读取像素的长度->要读取像素的宽度 2.获取所有点的颜色值后,想对像素点进行操作,则需要一Color。red(color)方法读取R分量,G、B、Alpha分量类似。此处的color为之前存放在数组里的颜色值 3.处理RGB值之后,还需检查其是否依旧在(0,255)范围内 4.使用方法newPx[i] = Color.argb(a, r, g, b);将新的RGB值创造新的颜色值 5.使用bmp.setPixels(newPx, 0, width, 0, 0, width, height);将颜色值应用到图片上,返回图片 ~~~ r = Color.red(color); g = Color.green(color); b = Color.blue(color); a = Color.alpha(color); ~~~ 底片 ~~~ r = 255 - r; g = 255 - g; b = 255 - b; ~~~ 老照片 ~~~ r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b); g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b); b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b); ~~~ 浮雕 ~~~ r = (r - r1 + 127); g = (g - g1 + 127); b = (b - b1 + 127); ~~~ ## ImageHelper工具类 ~~~ public class ImageHelper { public static Bitmap handleImageEffect(Bitmap bm, float hue, float saturation, float lum) { Bitmap bmp=Bitmap.createBitmap(bm.getWidth(),bm.getHeight(),Bitmap.Config.ARGB_8888); Canvas canvas=new Canvas(bmp); Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG); ColorMatrix hueMatrix=new ColorMatrix(); hueMatrix.setRotate(0,hue); hueMatrix.setRotate(1, hue); hueMatrix.setRotate(2, hue); ColorMatrix saturationMatrix=new ColorMatrix(); saturationMatrix.setSaturation(saturation); ColorMatrix lumMatrix=new ColorMatrix(); lumMatrix.setScale(lum,lum,lum,1); ColorMatrix imageMatrix=new ColorMatrix(); imageMatrix.postConcat(hueMatrix); imageMatrix.postConcat(saturationMatrix); imageMatrix.postConcat(lumMatrix); paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix)); canvas.drawBitmap(bm,0,0,paint); return bmp; } public static Bitmap handleImageNegative(Bitmap bm){ int width = bm.getWidth(); int height = bm.getHeight(); int color; int r, g, b, a; Bitmap bmp = Bitmap.createBitmap(width, height , Bitmap.Config.ARGB_8888); int[] oldPx = new int[width * height]; int[] newPx = new int[width * height]; bm.getPixels(oldPx, 0, width, 0, 0, width, height); for (int i = 0; i < width * height; i++) { color = oldPx[i]; r = Color.red(color); g = Color.green(color); b = Color.blue(color); a = Color.alpha(color); r = 255 - r; g = 255 - g; b = 255 - b; if (r > 255) { r = 255; } else if (r < 0) { r = 0; } if (g > 255) { g = 255; } else if (g < 0) { g = 0; } if (b > 255) { b = 255; } else if (b < 0) { b = 0; } newPx[i] = Color.argb(a, r, g, b); } bmp.setPixels(newPx, 0, width, 0, 0, width, height); return bmp; } public static Bitmap handleImagePixelsoldPhoto(Bitmap bm) { Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888); int width = bm.getWidth(); int height = bm.getHeight(); int color = 0; int r, g, b, a, r1, g1, b1; int[] oldPx = new int[width * height]; int[] newPx = new int[width * height]; bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height); for (int i = 0; i < width * height; i++) { color = oldPx[i]; a = Color.alpha(color); r = Color.red(color); g = Color.green(color); b = Color.blue(color); r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b); g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b); b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b); if (r1 > 255) { r1 = 255; } if (g1 > 255) { g1 = 255; } if (b1 > 255) { b1 = 255; } newPx[i] = Color.argb(a, r1, g1, b1); } bmp.setPixels(newPx, 0, width, 0, 0, width, height); return bmp; } public static Bitmap handleImagePixelsRelief(Bitmap bm) { Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888); int width = bm.getWidth(); int height = bm.getHeight(); int color = 0, colorBefore = 0; int a, r, g, b; int r1, g1, b1; int[] oldPx = new int[width * height]; int[] newPx = new int[width * height]; bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height); for (int i = 1; i < width * height; i++) { colorBefore = oldPx[i - 1]; a = Color.alpha(colorBefore); r = Color.red(colorBefore); g = Color.green(colorBefore); b = Color.blue(colorBefore); color = oldPx[i]; r1 = Color.red(color); g1 = Color.green(color); b1 = Color.blue(color); r = (r - r1 + 127); g = (g - g1 + 127); b = (b - b1 + 127); if (r > 255) { r = 255; } if (g > 255) { g = 255; } if (b > 255) { b = 255; } newPx[i] = Color.argb(a, r, g, b); } bmp.setPixels(newPx, 0, width, 0, 0, width, height); return bmp; } } ~~~
';