Android使用mount获取SD卡目录及如何获取SD卡内存
Android的系统是linux系统,我们可以使用linux命令mouunt来获取linux的挂载目录。
使用命令获取到的目录我并没有遍历,如果你还是获取不到,可以把mount获去到的所有目录都遍历一次。
File sdcard ; @SuppressLint("SdCardPath") public File getSdCardFile() { if (sdcard != null) { return sdcard; } List<String> list = getExtSDCardPath(); boolean isRun = true; if (list.size() > 0) { sdcard = new File(list.get(list.size() - 1)); if (sdcard.isDirectory()) { if (sdcard.getFreeSpace() == 0) { isRun = true; } else { isRun = false; } } } if (isRun) { sdcard = Environment.getExternalStorageDirectory(); if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { sdcard = new File("/sdcard/"); log.e("sdcard spance:" + sdcard.getFreeSpace()); if (sdcard.getFreeSpace() == 0) { sdcard = new File("/sdcard1/"); log.e("sdcard1 space:" + sdcard.getFreeSpace()); } if (sdcard.getFreeSpace() == 0) { sdcard = new File("/sdcard2/"); log.e("sdcard2 space:" + sdcard.getFreeSpace()); } } } log.e("data:" + sdcard.getAbsolutePath()); return sdcard; } public List<String> getExtSDCardPath() { List<String> lResult = new ArrayList<String>(); try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("mount"); InputStream is = proc.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { if (line.contains("extSdCard")) { String[] arr = line.split(" "); String path = arr[1]; if (path.lastIndexOf("extSdCard") == path.length() - 9) { File file = new File(path); if (file.isDirectory()) { lResult.add(path); } } } else if (line.contains("/sdcard")) { String[] arr = line.split(" "); String path = arr[1]; if (path.lastIndexOf("/sdcard") == path.length() - 6) { File file = new File(path); if (file.isDirectory()) { lResult.add(path); } } else { String number = path.substring(path.lastIndexOf("/sdcard") + 7); try { Integer.parseInt(number); File file = new File(path); if (file.isDirectory()) { lResult.add(path); } } catch (Exception e) { } } } } isr.close(); } catch (Exception e) { } return lResult; }
不过,首选获取存储还是先使用Android提供的方法,判断并获取Sdcard目录。
//判断sdcard是否存在 Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); //如果存在,获取存储File目录 Environment.getExternalStorageDirectory();
Android SD卡路径问题以及如何获取SDCard 内存
在研究拍照后突破的存储路径的问题,开始存储路径写死为: private String folder = "/sdcard/DCIM/Camera/"(SD卡上拍照程序的图片存储路径); 后来发现这样写虽然一般不会出错,但不是很好,因为不同相机,可能路径会出问题。较好的方法是通过Environment 来获取路径,最后给出一个例子,教你怎样获取SDCard 的内存,显示出来告诉用户。讲述的内容如下:
0、获取sd卡路径。
1、讲述 Environment 类。
2、讲述 StatFs 类。
3、完整例子读取 SDCard 内存
0、获取sd卡路径
方法一: private String folder = "/sdcard/DCIM/Camera/"(SD卡上拍照程序的图片存储路径); //写死绝对路径,不赞成使用
方法二:
public String getSDPath(){ File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState() .equals(Android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在 if (sdCardExist) { sdDir = Environment.getExternalStorageDirectory();//获取跟目录 } return sdDir.toString(); }
然后:在后面加上斜杠,在加上文件名
String fileName = getSDPath() +"/" + name;//以name存在目录中
1、讲述 Environment 类
Environment 是一个提供访问环境变量的类。
Environment 包含常量:
MEDIA_BAD_REMOVAL
解释:返回getExternalStorageState() ,表明SDCard 被卸载前己被移除
MEDIA_CHECKING
解释:返回getExternalStorageState() ,表明对象正在磁盘检查。
MEDIA_MOUNTED
解释:返回getExternalStorageState() ,表明对象是否存在并具有读/写权限
MEDIA_MOUNTED_READ_ONLY
解释:返回getExternalStorageState() ,表明对象权限为只读
MEDIA_NOFS
解释:返回getExternalStorageState() ,表明对象为空白或正在使用不受支持的文件系统。
MEDIA_REMOVED
解释:返回getExternalStorageState() ,如果不存在 SDCard 返回
MEDIA_SHARED
解释:返回getExternalStorageState() ,如果 SDCard 未安装 ,并通过 USB 大容量存储共享 返回
MEDIA_UNMOUNTABLE
解释:返回getExternalStorageState() ,返回 SDCard 不可被安装 如果 SDCard 是存在但不可以被安装
MEDIA_UNMOUNTED
解释:返回getExternalStorageState() ,返回 SDCard 已卸掉如果 SDCard 是存在但是没有被安装
Environment 常用方法:
方法:getDataDirectory()
解释:返回 File ,获取 Android 数据目录。
方法:getDownloadCacheDirectory()
解释:返回 File ,获取 Android 下载/缓存内容目录。
方法:getExternalStorageDirectory()
解释:返回 File ,获取外部存储目录即 SDCard
方法:getExternalStoragePublicDirectory(String type)
解释:返回 File ,取一个高端的公用的外部存储器目录来摆放某些类型的文件
方法:getExternalStorageState()
解释:返回 File ,获取外部存储设备的当前状态
方法:getRootDirectory()
解释:返回 File ,获取 Android 的根目录
2、讲述 StatFs 类
StatFs 一个模拟linux的df命令的一个类,获得SD卡和手机内存的使用情况
StatFs 常用方法:
getAvailableBlocks()
解释:返回 Int ,获取当前可用的存储空间
getBlockCount()
解释:返回 Int ,获取该区域可用的文件系统数
getBlockSize()
解释:返回 Int ,大小,以字节为单位,一个文件系统
getFreeBlocks()
解释:返回 Int ,该块区域剩余的空间
restat(String path)
解释:执行一个由该对象所引用的文件系统
3、完整例子读取 SDCard 内存
存储卡在 Android 手机上是可以随时插拔的,每次的动作都对引起操作系统进行 ACTION_BROADCAST,本例子将使用上面学到的方法,计算出 SDCard 的剩余容量和总容量。代码如下:
package com.terry; import java.io.File; import java.text.DecimalFormat; import android.R.integer; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.StatFs; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; public class getStorageActivity extends Activity { private Button myButton; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findView(); viewHolder.myButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub getSize(); } }); } void findView(){ viewHolder.myButton=(Button)findViewById(R.id.Button01); viewHolder.myBar=(ProgressBar)findViewById(R.id.myProgressBar); viewHolder.myTextView=(TextView)findViewById(R.id.myTextView); } void getSize(){ viewHolder.myTextView.setText(""); viewHolder.myBar.setProgress(0); //判断是否有插入存储卡 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File path =Environment.getExternalStorageDirectory(); //取得sdcard文件路径 StatFs statfs=new StatFs(path.getPath()); //获取block的SIZE long blocSize=statfs.getBlockSize(); //获取BLOCK数量 long totalBlocks=statfs.getBlockCount(); //己使用的Block的数量 long availaBlock=statfs.getAvailableBlocks(); String[] total=filesize(totalBlocks*blocSize); String[] availale=filesize(availaBlock*blocSize); //设置进度条的最大值 int maxValue=Integer.parseInt(availale[0]) *viewHolder.myBar.getMax()/Integer.parseInt(total[0]); viewHolder.myBar.setProgress(maxValue); String Text="总共:"+total[0]+total[1]+"\n" +"可用:"+availale[0]+availale[1]; viewHolder.myTextView.setText(Text); }else if(Environment.getExternalStorageState().equals(Environment.MEDIA_REMOVED)){ Toast.makeText(getStorageActivity.this, "没有sdCard", 1000).show(); } } //返回数组,下标1代表大小,下标2代表单位 KB/MB String[] filesize(long size){ String str=""; if(size>=1024){ str="KB"; size/=1024; if(size>=1024){ str="MB"; size/=1024; } } DecimalFormat formatter=new DecimalFormat(); formatter.setGroupingSize(3); String result[] =new String[2]; result[0]=formatter.format(size); result[1]=str; return result; } }
Android中View绘制流程以及invalidate(),requsetLaytout()以及requestFocus()方法分析
整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为根据之前设置的状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、以及是否需要重绘(draw),其框架过程如下:
步骤其实为host.layout()
接下来温习一下整个View树的结构,对每个具体View对象的操作,其实就是个递归的实现。
流程一: mesarue()过程
主要作用:为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth),每个View的控件的实际宽高都是由父视图和本身视图决定的。
具体的调用链如下:
ViewRoot根对象地属性mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调
View/ViewGroup对象的onMeasure()方法,该方法实现的功能如下:
1、设置本View视图的最终大小,该功能的实现通过调用setMeasuredDimension()方法去设置实际的高(对应属性: mMeasuredHeight)和宽(对应属性:mMeasureWidth) ;
2 、如果该View对象是个ViewGroup类型,需要重写该onMeasure()方法,对其子视图进行遍历的measure()过程。
2.1 对每个子视图的measure()过程,是通过调用父类ViewGroup.java类里的measureChildWithMargins()方法去实现,该方法内部只是简单地调用了View对象的measure()方法。(由于measureChildWithMargins()方法只是一个过渡层更简单的做法是直接调用View对象的measure()方法)。
整个measure调用流程就是个树形的递归过程
measure函数原型为 View.java 该函数不能被重载
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { //.... //回调onMeasure()方法 onMeasure(widthMeasureSpec, heightMeasureSpec); //more }
为了大家更好的理解,采用“二B程序员”的方式利用伪代码描述该measure流程
//回调View视图里的onMeasure过程 private void onMeasure(int height , int width){ //设置该view的实际宽(mMeasuredWidth)高(mMeasuredHeight) //1、该方法必须在onMeasure调用,否者报异常。 setMeasuredDimension(h , l) ; //2、如果该View是ViewGroup类型,则对它的每个子View进行measure()过程 int childCount = getChildCount() ; for(int i=0 ;i<childCount ;i++){ //2.1、获得每个子View对象引用 View child = getChildAt(i) ; //整个measure()过程就是个递归过程 //该方法只是一个过滤器,最后会调用measure()过程 ;或者 measureChild(child , h, i)方法都 measureChildWithMargins(child , h, i) ; //其实,对于我们自己写的应用来说,最好的办法是去掉框架里的该方法,直接调用view.measure(),如下: //child.measure(h, l) } } //该方法具体实现在ViewGroup.java里 。 protected void measureChildWithMargins(View v, int height , int width){ v.measure(h,l) }
流程二、 layout布局过程:
主要作用 :为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。
具体的调用链如下:
host.layout()开始View树的布局,继而回调给View/ViewGroup类中的layout()方法。具体流程如下
1 、layout方法会设置该View视图位于父视图的坐标轴,即mLeft,mTop,mLeft,mBottom(调用setFrame()函数去实现)接下来回调onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局) ;
2、如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法去设置它的坐标值。
layout函数原型为 ,位于View.java
/* final 标识符 , 不能被重载 , 参数为每个视图位于父视图的坐标轴 * @param l Left position, relative to parent * @param t Top position, relative to parent * @param r Right position, relative to parent * @param b Bottom position, relative to parent */ public final void layout(int l, int t, int r, int b) { boolean changed = setFrame(l, t, r, b); //设置每个视图位于父视图的坐标轴 if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT); } onLayout(changed, l, t, r, b);//回调onLayout函数 ,设置每个子视图的布局 mPrivateFlags &= ~LAYOUT_REQUIRED; } mPrivateFlags &= ~FORCE_LAYOUT; }
同样地, 将上面layout调用流程,用伪代码描述如下:
// layout()过程 ViewRoot.java // 发起layout()的"发号者"在ViewRoot.java里的performTraversals()方法, mView.layout() private void performTraversals(){ //... View mView ; mView.layout(left,top,right,bottom) ; //.... } //回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现 private void onLayout(int left , int top , right , bottom){ //如果该View不是ViewGroup类型 //调用setFrame()方法设置该控件的在父视图上的坐标轴 setFrame(l ,t , r ,b) ; //-------------------------- //如果该View是ViewGroup类型,则对它的每个子View进行layout()过程 int childCount = getChildCount() ; for(int i=0 ;i<childCount ;i++){ //2.1、获得每个子View对象引用 View child = getChildAt(i) ; //整个layout()过程就是个递归过程 child.layout(l, t, r, b) ; } }
流程三、 draw()绘图过程
由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。
调用流程 :
mView.draw()开始绘制,draw()方法实现的功能如下:
1 、绘制该View的背景
2 、为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框)
3、调用onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)
4、调用dispatchDraw ()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)
值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
4.1 dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,这个地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatchDraw()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
5、绘制滚动条
于是,整个调用链就这样递归下去了。
同样地,使用伪代码描述如下:
// draw()过程 ViewRoot.java // 发起draw()的"发号者"在ViewRoot.java里的performTraversals()方法, 该方法会继续调用draw()方法开始绘图 private void draw(){ //... View mView ; mView.draw(canvas) ; //.... } //回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现 private void draw(Canvas canvas){ //该方法会做如下事情 //1 、绘制该View的背景 //2、为绘制渐变框做一些准备操作 //3、调用onDraw()方法绘制视图本身 //4、调用dispatchDraw()方法绘制每个子视图,dispatchDraw()已经在Android框架中实现了,在ViewGroup方法中。 // 应用程序程序一般不需要重写该方法,但可以捕获该方法的发生,做一些特别的事情。 //5、绘制渐变框 } //ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法 @Override protected void dispatchDraw(Canvas canvas) { // //其实现方法类似如下: int childCount = getChildCount() ; for(int i=0 ;i<childCount ;i++){ View child = getChildAt(i) ; //调用drawChild完成 drawChild(child,canvas) ; } } //ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法 protected void drawChild(View child,Canvas canvas) { // .... //简单的回调View对象的draw()方法,递归就这么产生了。 child.draw(canvas) ; //......... }
强调一点的就是,在这三个流程中,Google已经帮我们把draw()过程框架已经写好了,自定义的ViewGroup只需要实现measure()过程和layout()过程即可 。
这三种情况,最终会直接或间接调用到三个函数,分别为invalidate(),requsetLaytout()以及requestFocus() ,接着这三个函数最终会调用到ViewRoot中的schedulTraversale()方法,该函数然后发起一个异步消息,消息处理中调用performTraverser()方法对整个View进行遍历。
invalidate()方法 :
说明:请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。
一般引起invalidate()操作的函数如下:
1、直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。
2、setSelection()方法 :请求重新draw(),但只会绘制调用者本身。
3、setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。
4 、setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。
requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。
说明:只是对View树重新布局layout过程包括measure()和layout()过程,不会调用draw()过程,但不会重新绘制任何视图包括该调用者本身。
一般引起invalidate()操作的函数如下:
1、setVisibility()方法:
当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。
同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。
requestFocus()函数说明:
说明:请求View树的draw()过程,但只绘制“需要重绘”的视图。
下面写个简单的小Demo吧,主要目的是给大家演示绘图的过程以及每个流程里该做的一些功能。截图如下:
1、 MyViewGroup.java 自定义ViewGroup类型
/** * @author http://http://blog.csdn.net/qinjuning */ //自定义ViewGroup 对象 public class MyViewGroup extends ViewGroup{ private static String TAG = "MyViewGroup" ; private Context mContext ; public MyViewGroup(Context context) { super(context); mContext = context ; init() ; } //xml定义的属性,需要该构造函数 public MyViewGroup(Context context , AttributeSet attrs){ super(context,attrs) ; mContext = context ; init() ; } //为MyViewGroup添加三个子View private void init(){ //调用ViewGroup父类addView()方法添加子View //child 对象一 : Button Button btn= new Button(mContext) ; btn.setText("I am Button") ; this.addView(btn) ; //child 对象二 : ImageView ImageView img = new ImageView(mContext) ; img.setBackgroundResource(R.drawable.icon) ; this.addView(img) ; //child 对象三 : TextView TextView txt = new TextView(mContext) ; txt.setText("Only Text") ; this.addView(txt) ; //child 对象四 : 自定义View MyView myView = new MyView(mContext) ; this.addView(myView) ; } @Override //对每个子View进行measure():设置每子View的大小,即实际宽和高 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ //通过init()方法,我们为该ViewGroup对象添加了三个视图 , Button、 ImageView、TextView int childCount = getChildCount() ; Log.i(TAG, "the size of this ViewGroup is ----> " + childCount) ; Log.i(TAG, "**** onMeasure start *****") ; //获取该ViewGroup的实际长和宽 涉及到MeasureSpec类的使用 int specSize_Widht = MeasureSpec.getSize(widthMeasureSpec) ; int specSize_Heigth = MeasureSpec.getSize(heightMeasureSpec) ; Log.i(TAG, "**** specSize_Widht " + specSize_Widht+ " * specSize_Heigth *****" + specSize_Heigth) ; //设置本ViewGroup的宽高 setMeasuredDimension(specSize_Widht , specSize_Heigth) ; for(int i=0 ;i<childCount ; i++){ View child = getChildAt(i) ; //获得每个对象的引用 child.measure(50, 50) ; //简单的设置每个子View对象的宽高为 50px , 50px //或者可以调用ViewGroup父类方法measureChild()或者measureChildWithMargins()方法 //this.measureChild(child, widthMeasureSpec, heightMeasureSpec) ; } } @Override //对每个子View视图进行布局 protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub //通过init()方法,我们为该ViewGroup对象添加了三个视图 , Button、 ImageView、TextView int childCount = getChildCount() ; int startLeft = 0 ;//设置每个子View的起始横坐标 int startTop = 10 ; //每个子View距离父视图的位置 , 简单设置为10px吧 。 可以理解为 android:margin=10px ; Log.i(TAG, "**** onLayout start ****") ; for(int i=0 ;i<childCount ; i++){ View child = getChildAt(i) ; //获得每个对象的引用 child.layout(startLeft, startTop, startLeft+child.getMeasuredWidth(), startTop+child.getMeasuredHeight()) ; startLeft =startLeft+child.getMeasuredWidth() + 10; //校准startLeft值,View之间的间距设为10px ; Log.i(TAG, "**** onLayout startLeft ****" +startLeft) ; } } //绘图过程Android已经为我们封装好了 ,这儿只为了观察方法调用程 protected void dispatchDraw(Canvas canvas){ Log.i(TAG, "**** dispatchDraw start ****") ; super.dispatchDraw(canvas) ; } protected boolean drawChild(Canvas canvas , View child, long drawingTime){ Log.i(TAG, "**** drawChild start ****") ; return super.drawChild(canvas, child, drawingTime) ; } }
2、MyView.java 自定义View类型,重写onDraw()方法 ,
//自定义View对象 public class MyView extends View{ private Paint paint = new Paint() ; public MyView(Context context) { super(context); // TODO Auto-generated constructor stub } public MyView(Context context , AttributeSet attrs){ super(context,attrs); } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ //设置该View大小为 80 80 setMeasuredDimension(50 , 50) ; } //存在canvas对象,即存在默认的显示区域 @Override public void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); Log.i("MyViewGroup", "MyView is onDraw ") ; //加粗 paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); paint.setColor(Color.RED); canvas.drawColor(Color.BLUE) ; canvas.drawRect(0, 0, 30, 30, paint); canvas.drawText("MyView", 10, 40, paint); } }
主Activity只是显示了该xml文件,在此也不罗嗦了。 大家可以查看该ViewGroup的Log仔细分析下View的绘制流程以及相关方法的使用。第一次启动后捕获的Log如下,网上找了些资料,第一次View树绘制过程会走几遍,具体原因可能是某些View 发生了改变,请求重新绘制,但这根本不影响我们的界面显示效果 。
总的来说: 整个绘制过程还是十分十分复杂地,每个具体方法的实现都是我辈难以立即的,感到悲剧啊。对Android提供的一些ViewGroup对象,比如LinearLayout、RelativeLayout布局对象的实现也很有压力。 本文重在介绍整个View树的绘制流程,希望大家在此基础上,多接触源代码进行更深入地扩展。
Android中将布局文件/View添加至窗口过程分析
本文主要内容是讲解一个视图View或者一个ViewGroup对象是如何添加至应用程序窗口中的。
下文中提到的窗口可泛指我们能看到的界面,包括一个Activity呈现的界面(我们可以将之理解为应用程序窗口),一个Dialog,一个Toast,一个Menu菜单等。
首先对相关类的作用进行一下简单介绍:
Window 类 位于 /frameworks/base/core/java/android/view/Window.java
说明:该类是一个抽象类,提供了绘制窗口的一组通用API。可以将之理解为一个载体,各种View在这个载体上显示。
源文件(部分)如下:
public abstract class Window { //... //指定Activity窗口的风格类型 public static final int FEATURE_NO_TITLE = 1; public static final int FEATURE_INDETERMINATE_PROGRESS = 5; //设置布局文件 public abstract void setContentView(int layoutResID); public abstract void setContentView(View view); //请求指定Activity窗口的风格类型 public boolean requestFeature(int featureId) { final int flag = 1<<featureId; mFeatures |= flag; mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag; return (mFeatures&flag) != 0; } //... }
PhoneWindow类 位于/frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java
说明: 该类继承于Window类,是Window类的具体实现,即我们可以通过该类具体去绘制窗口。并且,该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。 简而言之,PhoneWindow类是把一个FrameLayout类即DecorView对象进行一定的包装,将它作为应用窗口的根View,并提供一组通用的窗口操作接口。
源文件(部分)如下:
public class PhoneWindow extends Window implements MenuBuilder.Callback { //... // This is the top-level view of the window, containing the window decor. private DecorView mDecor; //该对象是所有应用窗口的根视图 , 是FrameLayout的子类 //该对象是Activity布局文件的父视图,一般来说是一个FrameLayout型的ViewGroup // 同时也是DecorView对象的一个子视图 // This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. private ViewGroup mContentParent; //设置标题 @Override public void setTitle(CharSequence title) { if (mTitleView != null) { mTitleView.setText(title); } mTitle = title; } //设置背景图片 @Override public final void setBackgroundDrawable(Drawable drawable) { if (drawable != mBackgroundDrawable || mBackgroundResource != 0) { mBackgroundResource = 0; mBackgroundDrawable = drawable; if (mDecor != null) { mDecor.setWindowBackground(drawable); } } } //... }
DecorView类 该类是PhoneWindow类的内部类
说明: 该类是一个FrameLayout的子类,并且是PhoneWindow的子类,该类就是对普通的FrameLayout进行功能的扩展,更确切点可以说是修饰(Decor的英文全称是Decoration,即“修饰”的意思),比如说添加TitleBar(标题栏),以及TitleBar上的滚动条等 。最重要的一点是,它是所有应用窗口的根View 。
如下所示 :
源文件(部分)如下:
private final class DecorView extends FrameLayout { //... //触摸事件处理 @Override public boolean onTouchEvent(MotionEvent event) { return onInterceptTouchEvent(event); } //... }
打个不恰当比喻吧,Window类相当于一幅画(抽象概念,什么画我们未知) ,PhoneWindow为一副齐白石先生的山水画 (具体概念,我们知道了是谁的、什么性质的画),DecorView则为该山水画的具体内容(有山、有水、有树,各种界面)。
DecorView呈现在PhoneWindow上。
当系统(一般是ActivityManagerService)配置好启动一个Activity的相关参数(包括Activity对象和Window对象信息)后,就会回调Activity的onCreate()方法,在其中我们通过设置setContentView()方法类设置该Activity的显示界面,整个调用链由此铺垫开来。setContentView()的三个构造方法调用流程本质上是一样的,我们就分析setContentView(intresId)方法。
Step 1 、Activity.setContentView(intresId) 该方法在Activity类中
该方法只是简单的回调Window对象,具体为PhoneWindow对象的setContentView()方法实现 。
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); } public Window getWindow() { return mWindow; //Window对象,本质上是一个PhoneWindow对象 }
Step 2 、PhoneWindow.setContentView() 该方法在PhoneWindow类中
@Override public void setContentView(int layoutResID) { //是否是第一次调用setContentView方法, 如果是第一次调用,则mDecor和mContentParent对象都为空 if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } }
方法根据首先判断是否已经由setContentView()了获取mContentParent即View对象, 即是否是第一次调用该PhoneWindow对象setContentView()方法。如果是第一次调用,则调用installDecor()方法,否则,移除该mContentParent内所有的所有子View。最后将我们的资源文件通过LayoutInflater对象转换为View树,并且添加至mContentParent视图中。
PS:因此,在应用程序里,我们可以多次调用setContentView()来显示我们的界面。
Step 3、 PhoneWindow. installDecor() 该方法在PhoneWindow类中
private void installDecor() { if (mDecor == null) { //mDecor为空,则创建一个Decor对象 mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); } if (mContentParent == null) { //generateLayout()方法会根据窗口的风格修饰,选择对应的修饰布局文件 //并且将id为content(android:id="@+id/content")的FrameLayout赋值给mContentParent mContentParent = generateLayout(mDecor); //... }
首先、该方法首先判断mDecor对象是否为空,如果不为空,则调用generateDecor()创建一个DecorView(该类是FrameLayout子类,即一个ViewGroup视图) ;
generateDecor()方法原型为:
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
其次、继续判断mContentParent对象是否为空,如果不为空,则调用generateLayout()方法去创建mContentParent对象。
generateLayout()方法如下:
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. //...1、根据requestFreature()和Activity节点的android:theme="" 设置好 features值 //2 根据设定好的 features值,即特定风格属性,选择不同的窗口修饰布局文件 int layoutResource; //窗口修饰布局文件 int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { layoutResource = com.android.internal.R.layout.dialog_title_icons; } else { layoutResource = com.android.internal.R.layout.screen_title_icons; } // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = com.android.internal.R.layout.screen_progress; // System.out.println("Progress!"); } //... //3 选定了窗口修饰布局文件 ,添加至DecorView对象里,并且指定mcontentParent值 View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { ProgressBar progress = getCircularProgressBar(false); if (progress != null) { progress.setIndeterminate(true); } } //... return contentParent; }
该方法会做如下事情:
1、根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图)。这些窗口修饰布局文件指定一个用来存放 Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id="@android:id/content"。
例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种:
①、指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;
②、为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法
获取值。
举例如下,隐藏标题栏有如下方法:requestWindowFeature(Window.FEATURE_NO_TITLE);
或者 为Activity配置xml属性:android:theme=”@android:style/Theme.NoTitleBar”。
PS:因此,在Activity中必须在setContentView之前调用requestFeature()方法。
确定好窗口风格之后,选定该风格对应的布局文件,这些布局文件位于 frameworks/base/core/res/layout/ ,典型的窗口布局文件有:
R.layout.dialog_titile_icons R.layout.screen_title_icons
R.layout.screen_progress R.layout.dialog_custom_title
R.layout.dialog_title
R.layout.screen_title // 最常用的Activity窗口修饰布局文件
R.layout.screen_simple //全屏的Activity窗口布局文件
分析Activity最常用的一种窗口布局文件,R.layout.screen_title :
<!-- This is an optimized layout for a screen, with the minimum set of features enabled. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:fitsSystemWindows="true"> <FrameLayout android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize" style="?android:attr/windowTitleBackgroundStyle"> <TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle" android:background="@null" android:fadingEdge="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> </LinearLayout>
该布局文件很简单,一个LinearLayout下包含了两个子FrameLayout视图,第一个FrameLayout用来显示标题栏(TitleBar),该TextView 视图id为title(android:id="@android:id/title");第二个FrameLayout用来显示我们Activity的布局文件的父视图,该FrameLayoutid为content(android:id="@android:id/content") 。
全屏的窗口布局文件 R.layout.screen_simple:
<--This is an optimized layout for a screen, with the minimum set of features enabled. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/content" android:fitsSystemWindows="true" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" />
该布局文件只有一个FrameLayout,用来显示我们Activity的布局文件,该FrameLayoutid为
android:id="@android:id/content"
2、前面一步我们确定窗口修饰布局文件后,mDecor做为根视图将该窗口布局对应的视图添加进去,并且获取id为content的View,将其赋值给mContentParent对象,即我们前面中提到的第二个FrameLayout。
At Last、产生了mDecor和mContentParent对象后,就将我们的Activity布局文件直接添加至mContentParent父视图中即可。
我们再次回到 Step 2 中PhoneWindow.setContentView() 该方法在PhoneWindow类中
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } }
整个过程主要是如何把Activity的布局文件添加至窗口里,上面的过程可以概括为:
1、创建一个DecorView对象,该对象将作为整个应用窗口的根视图
2、创建不同的窗口修饰布局文件,并且获取Activity的布局文件该存放的地方,由该窗口修饰布局文件内id为content的FrameLayout指定 。
3、将Activity的布局文件添加至id为content的FrameLayout内。
最后,当AMS(ActivityManagerService)准备resume一个Activity时,会回调该Activity的handleResumeActivity()方法,
该方法会调用Activity的makeVisible方法 ,显示我们刚才创建的mDecor 视图族。
//系统resume一个Activity时,调用此方法 final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { ActivityRecord r = performResumeActivity(token, clearHide); //... if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } }
handleResumeActivity()方法原型如下: 位于ActivityThread类中
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); // 获取WindowManager对象 wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); //使其处于显示状况 }
接下来就是,如何把我们已经创建好的窗口通知给WindowManagerService ,以便它能够把这个窗口显示在屏幕上。
<pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <div style="padding-top:20px"> <p style="font-size:12px;">版权声明:本文为博主原创文章,未经博主允许不得转载。</p> </div>
Android中被使用的消息队列的代码在目录\sources\android-22\android\os下,主要涉及到以下几个类文件
Handler.java 在这里面代表一个消息实体对象
Looper.java 主要用来监听MessageQueue的消息,他存放于ThreadLocal中
Message.java 主要用来处理消息的发送,以及响应消息的业务处理
MessageQueue.java 是一个单独的线程,可与Looper整合,实现一个完全独立的消息处理机制
Message.java public final class Message implements Parcelable { public int what; //消息种类 public int arg1; //低开销的整型参数 public int arg2; public Object obj; //Object型数据 public Messenger replyTo; //消息处理完后通知给发送者 /*package*/ int flags; //消息标记:正在使用和异步等 /*package*/ long when; //消息创建时的时间 /*package*/ Bundle data; //消息附带的额外数据 /*package*/ Handler target; //消息接受者,处理者 /*package*/ Runnable callback; //优先使用回调处理来处理消息 /*package*/ Message next; //下一个消息,形成链表 private static Message sPool; //消息池中的头消息
上面中的target,通常由重新实现的Handler子类的handleMessage函数来处理消息
public static Message obtain() { //获取消息的函数,如果有消息的话则获取出来m,链表指针移动一位,否则则返回一条空消息 synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } public void sendToTarget() { //发送消息给处理者 target.sendMessage(this); //调用Handler.java中的函数 } }
MessageQueue.java public final class MessageQueue { Message mMessages; //当前要处理的消息 //当需要从链表中获取一个消息时,就会调用next函数,若消息队列中没有消息,则会阻塞等待,通过调用nativePollOnce函数来完成 Message next() {...} boolean enqueueMessage(Message msg, long when) { //按时间顺序添加消息 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); //调用底层唤醒函数,管道唤醒 } } return true; }
Looper.java public final class Looper { final MessageQueue mQueue; //消息队列 final Thread mThread; //Looper联系的线程 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //先会检查是否有Looper,若有则抛出异常,没有的话则创建一个Looper实例保存起来 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //在这个线程中运行消息队列,调用quit()停止 public static void loop() { ... final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // 从消息队列中取出一条消息 if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); //交给msg的handler分发消息处理 ... } //取出当前线程的Looper,返回空则表示当前线程没有Looper public static Looper myLooper() { return sThreadLocal.get(); } }
Handler.java public class Handler { //定义Callback接口 public interface Callback { public boolean handleMessage(Message msg); } //子类要实现的消息处理方法 public void handleMessage(Message msg) { } * Handle system messages here. */ public void dispatchMessage(Message msg) { //分发信息 if (msg.callback != null) { //若指定了msg.callback,则由它处理 handleCallback(msg); } else { if (mCallback != null) { //若指定了Handler.mCallback,则由它处理 if (mCallback.handleMessage(msg)) { //调用mCallback接口的实现方法 return; } } handleMessage(msg); 最后才调用Handler自身重载的handleMessage方法 } } 分发消息函数中,消息先会检查自身有没有处理自身的回调Runnable,若有则由它处理,若没有则会检查该handler有无自身的回调处理,若有则调用,若没有则调用自身重载的handleMessage方法 //Handler的生成总是和它当前所处线程有关的,如果当前线程中没有一个Looper,则会报错,UI线程中默认有产生Looper的函数 public Handler() { this(null, false); } //使用指定的Looper(可以处理那个Looper线程中的消息),不用默认的从当前线程中取出Looper public Handler(Looper looper) { this(looper, null, false); } ... }
下面分为生成JSON数据和解析JSON数据,所用的包是org.json
(1)生成JSON数据方法:
比如要生成一个这样的json文本
{
"phone" : ["12345678", "87654321"], //数组
"name" : "dream9", // 字符串
"age" : 100, // 数值
"address" : { "country" : "china", "province" : "guangdong" }, // 对象
}
try { JSONObject obj = new JSONObject(); // 首先创建一个对象 JSONArray phone = new JSONArray(); // 添加数据到数组中序号是从0递增的 phone.put("12345678"); phone.put("87654321"); obj.put("phone", phone); obj.put("name", "dream9"); obj.put("age", 100); JSONObject address = new JSONObject(); address.put("country", "china"); address.put("province", "jiangsu"); obj.put("address", address); Log.e("huang", obj.toString()); }
结果:
(2)解析JSON数据方法(以上面那个为例):
private void anaylse(String data){
try {
JSONObject obj = new JSONObject((String)data);
JSONArray phone = obj.getJSONArray("phone");
for(int t=0; t<phone.length(); ++t){
Log.e("huang", phone.getString(t)); //解析phone数组
}
Log.e("huang", obj.getString("name"));
Log.e("huang", obj.getInt("age")+"");
JSONObject o = obj.getJSONObject("address");
Log.e("huang", o.getString("country"));
Log.e("huang", o.getString("province"));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
结果:
android Json解析详解
android2.3提供的json解析类
android的json解析部分都在包org.json下,主要有以下几个类:
JSONObject:可以看作是一个json对象,这是系统中有关JSON定义的基本单元,其包含一对儿(Key/Value)数值。它对外部(External: 应用toString()方法输出的数值)调用的响应体现为一个标准的字符串(例如:{"JSON": "Hello, World"},最外被大括号包裹,其中的Key和Value被冒号":"分隔)。其对于内部(Internal)行为的操作格式略微,例如:初始化一个JSONObject实例,引用内部的put()方法添加数值:new JSONObject().put("JSON", "Hello, World!"),在Key和Value之间是以逗号","分隔。Value的类型包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object 。
JSONStringer:json文本构建类 ,根据官方的解释,这个类可以帮助快速和便捷的创建JSON text。其最大的优点在于可以减少由于 格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntax rules)创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。。其最大的优点在于可以减少由于格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntax rules)创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。
JSONArray:它代表一组有序的数值。将其转换为String输出(toString)所表现的形式是用方括号包裹,数值以逗号”,”分隔(例如: [value1,value2,value3],大家可以亲自利用简短的代码更加直观的了解其格式)。这个类的内部同样具有查询行为, get()和opt()两种方法都可以通过index索引返回指定的数值,put()方法用来添加或者替换数值。同样这个类的value类型可以包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object。
JSONTokener:json解析类
JSONException:json中用到的异常
JSONObject, JSONArray来构建json文本
// 假设现在要创建这样一个json文本 // { // "phone" : ["12345678", "87654321"], // 数组 // "name" : "yuanzhifei89", // 字符串 // "age" : 100, // 数值 // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象 // "married" : false // 布尔值 // } try { // 首先最外层是{},是创建一个对象 JSONObject person = new JSONObject(); // 第一个键phone的值是数组,所以需要创建数组对象 JSONArray phone = new JSONArray(); phone.put("12345678").put("87654321"); person.put("phone", phone); person.put("name", "yuanzhifei89"); person.put("age", 100); // 键address的值是对象,所以又要创建一个对象 JSONObject address = new JSONObject(); address.put("country", "china"); address.put("province", "jiangsu"); person.put("address", address); person.put("married", false); } catch (JSONException ex) { // 键为null或使用json不支持的数字格式(NaN, infinities) throw new RuntimeException(ex); }
getType和optType api的使用
getType可以将要获取的键的值转换为指定的类型,如果无法转换或没有值则抛出JSONException
optType也是将要获取的键的值转换为指定的类型,无法转换或没有值时返回用户提供或这默认提供的值
try { // 所有使用的对象都是用上面创建的对象 // 将第一个电话号码转换为数值和将名字转换为数值 phone.getLong(0); person.getLong("name"); // 会抛异常,因为名字无法转换为long phone.optLong(0); // 代码内置的默认值 phone.optLong(0, 1000); // 用户提供的默认值 person.optLong("name"); person.optLong("name", 1000); // 不像上面那样抛异常,而是返回1000 } catch (JSONException ex) { // 异常处理代码 }
除了上面的两个类,还可以使用JSONStringer来构建json文本
Java代码
try { JSONStringer jsonText = new JSONStringer(); // 首先是{,对象开始。object和endObject必须配对使用 jsonText.object(); jsonText.key("phone"); // 键phone的值是数组。array和endArray必须配对使用 jsonText.array(); jsonText.value("12345678").value("87654321"); jsonText.endArray(); jsonText.key("name"); jsonText.value("yuanzhifei89"); jsonText.key("age"); jsonText.value(100); jsonText.key("address"); // 键address的值是对象 jsonText.object(); jsonText.key("country"); jsonText.value("china"); jsonText.key("province"); jsonText.value("jiangsu"); jsonText.endObject(); jsonText.key("married"); jsonText.value(false); // },对象结束 jsonText.endObject(); } catch (JSONException ex) { throw new RuntimeException(ex); }
json文本解析类JSONTokener
按照RFC4627规范将json文本解析为相应的对象。
对于将json文本解析为对象,只需要用到该类的两个api:
构造函数
public Object nextValue(); // { // "phone" : ["12345678", "87654321"], // 数组 // "name" : "yuanzhifei89", // 字符串 // "age" : 100, // 数值 // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象 // "married" : false // 布尔值 // } private static final String JSON = "{" + " \"phone\" : [\"12345678\", \"87654321\"]," + " \"name\" : \"yuanzhifei89\"," + " \"age\" : 100," + " \"address\" : { \"country\" : \"china\", \"province\" : \"jiangsu\" }," + " \"married\" : false," + "}"; try { JSONTokener jsonParser = new JSONTokener(JSON); // 此时还未读取任何json文本,直接读取就是一个JSONObject对象。 // 如果此时的读取位置在"name" : 了,那么nextValue就是"yuanzhifei89"(String) JSONObject person = (JSONObject) jsonParser.nextValue(); // 接下来的就是JSON对象的操作了 person.getJSONArray("phone"); person.getString("name"); person.getInt("age"); person.getJSONObject("address"); person.getBoolean("married"); } catch (JSONException ex) { // 异常处理代码 }
其它的api基本就是用来查看json文本中的文本的
try { JSONTokener jsonParser = new JSONTokener(JSON); // 继续向下读8个json文本中的字符。此时刚开始,即在{处 jsonParser.next(8); //{ "phone。tab算一个字符 // 继续向下读1个json文本中的字符 jsonParser.next(); //" // 继续向下读取一个json文本中的字符。该字符不是空白、同时也不是注视中的字符 jsonParser.nextClean(); //: // 返回当前的读取位置到第一次遇到'a'之间的字符串(不包括a)。 jsonParser.nextString('a'); // ["12345678", "87654321"], "n(前面有两个空格) // 返回当前读取位置到第一次遇到字符串中(如"0089")任意字符之间的字符串,同时该字符是trimmed的。(此处就是第一次遇到了89) jsonParser.nextTo("0089"); //me" : "yuanzhifei // 读取位置撤销一个 jsonParser.back(); jsonParser.next(); //i // 读取位置前进到指定字符串处(包括字符串) jsonParser.skipPast("address"); jsonParser.next(8); //" : { "c // 读取位置前进到执行字符处(不包括字符) jsonParser.skipTo('m'); jsonParser.next(8); //married" } catch (JSONException ex) { // 异常处理代码 }
以下是一个标准的JSON请求实现过程:
HttpPost request = new HttpPost(url); // 先封装一个 JSON 对象 JSONObject param = new JSONObject(); param.put("name", "rarnu"); param.put("password", "123456"); // 绑定到请求 Entry StringEntity se = new StringEntity(param.toString()); request.setEntity(se); // 发送请求 HttpResponse httpResponse = new DefaultHttpClient().execute(request); // 得到应答的字符串,这也是一个 JSON 格式保存的数据 String retSrc = EntityUtils.toString(httpResponse.getEntity()); // 生成 JSON 对象 JSONObject result = new JSONObject( retSrc); String token = result.get("token");
下面这个是自己修改别人的小例子,主要是加一些注释和讲解,这个例子主要是使用android进行json解析。
单数据{'singer':{'id':1,'name':'tom','gender':'男'}}
多个数据{'singers':[{'id':'2','name':'tom','gender':'男'},{'id':'3','name':'jerry','gender':'男'},{'id':'4','name':'jim','gender':'男'},{'id':'5','name':'lily','gender':'女'}]}
下面的类主要是解析单个数据parseJson()和多个数据的方法parseJsonMulti():
public class JsonActivity extends Activity { /** Called when the activity is first created. */ private TextView tvJson; private Button btnJson; private Button btnJsonMulti; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tvJson = (TextView) this.findViewById(R.id.tvJson); btnJson = (Button) this.findViewById(R.id.btnJson); btnJsonMulti = (Button) this.findViewById(R.id.btnJsonMulti); btnJson.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // url // String strUrl = "http://10.158.166.110:8080/AndroidServer/JsonServlet"; String strUrl = ServerPageUtil.getStrUrl(UrlsOfServer.JSON_SINGER); //获得返回的Json字符串 String strResult = connServerForResult(strUrl); //解析Json字符串 parseJson(strResult); } }); btnJsonMulti.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String strUrl = ServerPageUtil.getStrUrl(UrlsOfServer.JSON_SINGERS); String strResult = connServerForResult(strUrl); //获得多个Singer parseJsonMulti(strResult); } }); } private String connServerForResult(String strUrl) { // HttpGet对象 HttpGet httpRequest = new HttpGet(strUrl); String strResult = ""; try { // HttpClient对象 HttpClient httpClient = new DefaultHttpClient(); // 获得HttpResponse对象 HttpResponse httpResponse = httpClient.execute(httpRequest); if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { // 取得返回的数据 strResult = EntityUtils.toString(httpResponse.getEntity()); } } catch (ClientProtocolException e) { tvJson.setText("protocol error"); e.printStackTrace(); } catch (IOException e) { tvJson.setText("IO error"); e.printStackTrace(); } return strResult; } // 普通Json数据解析 private void parseJson(String strResult) { try { JSONObject jsonObj = new JSONObject(strResult).getJSONObject("singer"); int id = jsonObj.getInt("id"); String name = jsonObj.getString("name"); String gender = jsonObj.getString("gender"); tvJson.setText("ID号"+id + ", 姓名:" + name + ",性别:" + gender); } catch (JSONException e) { System.out.println("Json parse error"); e.printStackTrace(); } } //解析多个数据的Json private void parseJsonMulti(String strResult) { try { JSONArray jsonObjs = new JSONObject(strResult).getJSONArray("singers"); String s = ""; for(int i = 0; i < jsonObjs.length() ; i++){ JSONObject jsonObj = (JSONObject)jsonObjs.get(i); int id = jsonObj.getInt("id"); String name = jsonObj.getString("name"); String gender = jsonObj.getString("gender"); s += "ID号"+id + ", 姓名:" + name + ",性别:" + gender+ "\n" ; } tvJson.setText(s); } catch (JSONException e) { System.out.println("Jsons parse error !"); e.printStackTrace(); } } }下面一起来看一篇关于ubuntu 12.04 编译Android下的ffmpeg 1.2的例子,希望此教程能够对各位同学带来有效的帮助。
编译环境:ubuntu 12.04
NDK版本:android-ndk-r8d
ffmpeg版本:1.2
新建Android工程,在工程目录中创建jni文件夹
1、在jni目录下添加如下文件Android.mk ,内容如下:
include $(all-subdir-makefiles)
2.在jni/ffmpeg下添加Android.mk内容如下:
3.在jni/ffmpeg下添加av.mk
4.在jni/ffmpeg/libavformat下添加Android,mk内容如下:
5. 在jni/ffmpeg/libavcodec下添加Android,mk内容如下:
6.在jni/ffmpeg/libavutil libpostproc libswscale libswresample 下添加Android,mk内容如下:
7.在jni/ffmpeg 下添加可执行文件config.sh, 内容如下
上面PREBUILT PLATFORM的路径自己根据NDK的路径自己更改,并增加可执行权限,命令如下:
chmod +x config.sh
4、命令行到jni/ffmpeg目录下执行
./config.sh
5、修改
a、删除 libavformat libavcodec libavutil libpostproc libswscale libswresample 目录下Makefile下的
include $(SUBDIR)../config.mak
b、删除libavcodec libavutil libswresample目录下Makefile下的
log2_tab.o \
c、修改jni/ffmpeg/config.h下的
#define avrestrict restrict为#define restrict
e、把 ffmpeg/libavutil/time.h更名为avtime.h,
同时修改下面文件中的引用libavutil/time.h为libavutil/avtime.h
ffmpeg/libavformat/avformat.h:211
ffmpeg/libavformat/avio.c:25
ffmpeg/libavformat/hls.c:33
ffmpeg/libavformat/mux.c:39:30
ffmpeg/libavformat/utils.c:40
ffmpeg/libavutil/time.c:36
6、接下来就是编译了
cd ..
命令行到项目project目录下执行
$NDK/ndk-build
问题一:
Install : libffplay.so => libs/armeabi-v7a/libffplay.so
Install : libiptv_media_player_jni.so => libs/armeabi-v7a/libiptv_media_player_jni.so
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: warning: hidden symbol '__aeabi_atexit' in ./obj/local/armeabi-v7a/libgnustl_static.a(atexit_arm.o)
is referenced by DSO ./obj/local/armeabi-v7a/libffplay.so
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/libswscale.a(swscale.o): in function swScale:jni/libffmpeg/libswscale/swscale.c:426:
error: undefined reference to 'av_get_cpu_flags'
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/libswscale.a(utils.o): in function sws_init_context:jni/libffmpeg/libswscale/utils.c:917:
error: undefined reference to 'av_get_cpu_flags'
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/libswresample.a(resample.o): in function multiple_resample:jni/libffmpeg/libswresample/resample.c:315:
error: undefined reference to 'av_get_cpu_flags'
将Android.mk中的
LOCAL_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale libswresample libmediaplayer
改为
LOCAL_WHOLE_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale libswresample libmediaplayer
相关文章
- 下面本文章来给大家介绍在php中成员变量的一些对比了,文章举了四个例子在这例子中分别对不同成员变量进行测试与获取操作,下面一起来看看。 有如下4个代码示例,你认...2016-11-25
- php 获取用户IP与IE信息程序 function onlineip() { global $_SERVER; if(getenv('HTTP_CLIENT_IP')) { $onlineip = getenv('HTTP_CLIENT_IP');...2016-11-25
- php获取一个文件夹的mtime的程序了,这个就是时间问题了,对于这个问题我们来看小编整理的几个例子,具体的操作例子如下所示。 php很容易获取到一个文件夹的mtime,可以...2016-11-25
- 下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
- 获取网站icon,常用最简单的方法就是通过website/favicon.ico来获取,不过由于很多网站都是在页面里面设置favicon,所以此方法很多情况都不可用。 更好的办法是通过google提供的服务来实现:http://www.google.com/s2/favi...2014-06-07
- 主要功能:获取浏览器显示区域(可视区域)的高度 : $(window).height(); 获取浏览器显示区域(可视区域)的宽度 :$(window).width(); 获取页面的文档高度 $(document).height(); 获取页面的文档宽度 :$(document).width();...2015-10-21
Android开发中findViewById()函数用法与简化
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20- 如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
- jquery中jquery.offset().top / left用于获取div距离窗口的距离,jquery.position().top / left 用于获取距离父级div的距离(必须是绝对定位的div)。 (1)先介绍jquery.offset().top / left css: 复制代码 代码如下: *{ mar...2013-10-13
- 夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
- 1、先讲讲JQuery的概念,JQuery首先是由一个 America 的叫什么 John Resig的人创建的,后来又很多的JS高手也加入了这个团队。其实 JQuery是一个JavaScript的类库,这个类库集合了很多功能方法,利用类库你可以用简单的一些代...2014-05-31
- 这篇文章主要介绍了浅谈redis key值内存消耗以及性能影响,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-07
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
- 这篇文章主要介绍了详解分析MySQL8.0的内存消耗,帮助大家更好的理解和学习使用MySQL,感兴趣的朋友可以了解下...2021-03-23
- 深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
- TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
- 这篇文章主要介绍了C#获取字符串后几位数的方法,实例分析了C#操作字符串的技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25