耗时整整3个月,我梳理了200道Android面试基础(上)【面试必考,全网最全,每天一遍】

前言

本篇内容比较快捷短小。属于快问快答形式。大家可以下载下来,每天一遍。
记住,答案只是引导,在回答完后发散自己的引导思维。去引申。

准备好挑战了吗?那么我们开始

  1. SD卡

路径

  a).Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 	//sd卡挂载状态
  b).Environment.getExternalStorageDirectory().getPath()	//sd卡路径 ->/storage/emulated/0
  c).Environment.getDownloadCacheDirectory().getPath()	//data/cache路径
  d).Environment.getDataDirectory().getPath()			//data

权限

  <uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE/>
  <uses-permission android:name=“android.permission.MOUNT_UNMOUNT_FILESYSTEMS/>
  1. android的数据存储方式
  • 文件存储
    a).InputStream getResouces().openRawResource(int id) //返回InputStream,针对res/raw
    b).InputStream getAssets().open(String path) //open(“img/smail.jpg”)
    c).Bitmap BitmapFactory.decodeResouce(resources,int,option)
  • xml、SharedPreference
    //1).SharedPreference位于data/data/包名/shared_prefs的xxx.xml文件
  • SQLiteDatabase
  • ContentProvider
  • 网络
  1. BroadcastReceiver
//隐式广播需要在manifest中加上
>>class Receiver:BroadcastReceiver{//自定义监听器
    onReceive(context:Context?,intent:Intent?){
	val action=intent.getAction()
        when(action)->{

        }
    }
  }
>>IntentFilter if=IntentFilter() 	 	//动态注册广播
  if.addAction("action1")
  registerReceiver(re:BroadcastReceiver,if)
  
  override fun destroy(){
    unregisterReceiver(re:BroadcastReceiver)
  }
>>隐式广播(通过action和category匹配)
  :1.Manifest上添加<category android:name="android.intent.category.DEFAULT"/>
  :2.标识为android.intent.category.LAUNCHER,则不用添加DEFAULT
>>sendBroadcast(intent)			//发送普通广播,静态注册必须描述action标签
  :1.val intent=Intent()
     intent.action=""
     intent.addCategory()
     
>>sendOrderedBroadcast(intent,receivePermission)			//发送有序广播
  a).<receiver android:name=".smsReceiver">  
       <intent-filter android:priority="1000">  
         <action android:name="android.provider.Telephony.SMS_RECEIVED"/>  
       </intent-filter>  
     </receiver>  
  b).setResultExtras(bundle:Bundle)			//后续广播通过getResultExtras(true)取回Bundle
  c).abortBroadcast()				//终止广播传递
>>sendStickyBroadcast(intent)			//发送粘性广播,粘性广播以最后一次的内容进行保留
  :1.removeStickyBroadcast(intent)		
  :2.Manifest中声明android.permission.BROADCAST_STICKY
  1. sp频繁操作会有什么后果?sp能存多少数据?

sp的底层由xml来实现,操作sp的过程就是xml的序列化和解析。xml是存在磁盘上的,因此要考虑io速度。另外dvm的内存是有限的,同时dvm的堆内存为16M,所以不能超过此数字

  1. dvm与jvm的区别

架构不同

  • JVM基于栈则意味着需要去栈中读写数据
  • DVM是基于寄存器的

字节码的执行顺序不同

  • 执行顺序为: .java文件 -> .class文件 -> .jar文件
  • java文件 –>.class文件-> .dex文件
  • 每个应用都运行在一个DVM实例中,且一个DVM运行在一个独立的进程空间

dvm的垃圾回收

  • 采用标记清除
    缺点:容易产生很多彼此分隔的小内存块
  • dvm可划分为Active Heap+Zygote Heap+Card Table+Live Heap Bitmap+MARK HEAP Bitamp
    ->Zygote Heap 为初始时的进程,而应用程序是由Zygote fork出来的
    ->当创建了应用程序后,会从Zygote heap分出一部分创建Active Heap,之后分配的对象都在Active堆进行
    ->堆的垃圾回收标记 采用Heap Bitmap位图来存储,划分为Live + Mark
    ->垃圾回收遍历采用递归,垃圾回收的标准为Live=1,Mark=0
    ->垃圾回收的两个阶段,第一个阶段标记根对象 (静态对象、栈、全局变量、寄存器),不允许其他线程运行
    第二个阶段遍历被根对象所引用的对象,并标记,此时为Concurrent GC
    ->CardTable,用位图标识第二阶段需要被重新标记的对象,在第二阶段完成后继续标记,此时禁止其他线程
  1. ART

//android4.4之后采用ART作为虚拟机
GC算法分为多种
:1.Mark-Sweep GC,标记-清楚
:2.Compacting GC,标记-压缩
新生代采用复制算法,老年代采用标记压缩 //复制算法:内存分为相等的两块,一块用于分配

  1. Activity的生命周期
//结合ActivityThread
->onCreate	创建时执行
  a).mInstrumentation.callActivityOnCreate
  b).performStart
  c).mInstrumentation.callActivityOnRestoreInstanceState
  
->onStart	可见时执行
->onResume	获取焦点时执行
  a).handleResumeActivity
     >performResumeActivity
->onPause	失去焦点时执行
  a).handlePauseActivity
     >performPauseActivity
      >if(!r.activity.mFinished&&r.saveState)
	mInstrumentation.callActivityOnSaveInstanceState
->onStop	不可见时执行
  a).handleStopActivity
     >performStopActivityInner 
       >performPauseActivityIfNeeded
->onDestroy	销毁
  a).handleDestroyActivity
->案例分析:Activity A -> Activity B
  :1.Activity A的创建,A::onCreate , A:onStart ,A:onResume
  :2.跳转 , A:onPause ,B::onCreate,B:onStart,B:onResume,A:onStop
  :3.Activity B返回,B:onPause,A:onRestart,A:onStart,A:onResume,B:onStop,B:onDestroy
  1. Application能不能启动Activity

startActivity(intent:Intent,FLAG_ACTIVITY_NEW_TASK)
standard模式启动的Activity,该实例会存放在启动该Activity的Activity所在的任务栈中,而 ApplicationContext不存放在任务栈中

  1. Activity的状态都有哪些

->foreground activity
->visible activity
->background activity
->empty process

  1. 横竖屏切换时Activity的生命周期
  • 在没有配置activity的configChanges属性时
    Activity的生命周期:onPause->onStop->onCreate->onStart->onResume
  • 配置了activity的configChanges
    onConfigurationChanged
  1. 如何设置activity成窗口样式
android:theme=@android:style/Theme.Dialog
  1. Activity的启动方式
  • standard 不管有没有已存在的实例,都生成新实例
  • singleTop 如果发现有对应的Activity实例位于栈顶,则重复利用,否则创建实例
  • singleTask
    a)栈内复用,复用时具有clearTop机制
    b)single taskAffinity in task
  • singleInstance
    a)启用一个新的栈结构,将Activity放置于栈结构中,并保证不会有其它Activity实例进入
    b)方便多个应用共享全局唯一的实例
  1. Service的生命周期

通过startService调用 onCreate->onStartCommand->onDestroy
通过bindService调用 onCreate->onBind->onUnbind->onDestroy

Service Binder

>>//示例
  a).public class LocalService extends Service{
	private static LBinder binder=new LBinder();			//必须声明为static
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
	  super.onCreate(savedInstanceState);
	}
        @Override
	public IBinder onBind(Intent intent)
	{
	  return binder;
	}
	
	static class LBinder extends Binder{
	  public int getCount(){
	    return count;
 	  }
	}
    }
  1. IntentService

//开启线程处理耗时操作

>>onCreate 				//创建单独的HandlerThread处理所有的intent请求
  :1.HandlertThread thread = new HandlerThread("") 
     thread.start()
     mServiceLooper =thread.getLooper()
     mServiceHandler =new ServiceHandler(mServiceLooper) 

onSTartCommand.

  :1.public int onStartCommand(Intent intent,int startId){
	onStart(intent,startId)
	return mRedelivery?START_REDELIVER_INTENT:START_NOT_STICKY;//在于service挂掉了intent是否会回传
     }
  :2.public void onStart(Intent intent,int startId)
     {
	Message msg=mServiceHandler.obtainMessage()
        msg.obj=intent
	msg.arg1=startId
	mServiceHandler.sendMessage(msg)
     }

ServiceHandler.

//onHandleIntent,为自定义的类所实现的耗时操作
  :1.private final class ServiceHandler extends Handler{
	public ServiceHandler(Looper looper){super(looper);}
	public void handleMessage(Message msg)
       {
	  onHandleIntent((Intent)msg.obj)
	  stopSelf(msg.arg1)//当id为最后一个startService的startId时,进行关闭
	}
     }

总结
a).通过HandlerThread创建线程,并调用getLooper获取looper变量,以新建Handler
b).IntentService通过onStartCommand接收新的intent,并放在message.obj发送过handler
c).handler调用实现的onHandleIntent(intent)
d).Message msg=mServiceHandler.obtainMessage()
//obtainMessage和Looper调用msg.relycleUnchecked是以插入链表头部和拆除的形式分配、释放Message资源

  1. Fragment和Activity的onCreateOptionsMenu
>>override fun onCreateOptionsMenu(menu:Menu)
  {
    getMenuInflater.inflate(R.menu.menu1,menu)
    return super.onCreateOptionsMenu(menu)
  }
>>setHasOptionsMenu(true)	//Fragment onCreate
  1. Service的onStartCommand有几种返回值
>>START_STICKY	当Service因内存不足被系统kill掉后,会尝试重建Service,创建成功后回调onStartCommand,但Intent是空的
>>START_NOT_STICKY 系统不会尝试重建Service
>>START_REDELIVER_INTENT	重建Service并传递最后一次Intent
  1. Service的onRebind什么情况下执行
  • 当Service刚启动时,通过调用bindService都会进入Service的onBind回调
  • 在Service启动过后,再进行绑定进入onRebind,并在onUnbind返回true
  1. Handler防止内存泄露
  • 原因
    a).声明Handler为静态类,同时对于Activity的this引用封装成WeakReference
    否则会导致匿名类引用了外部Activity,导致Activity不能被回收
    b).如果这时对手机硬件进行横竖屏切换,讲导致Activity被创建,却不能被回收
  • 措施
  a).public void onDestroy()
     {
	mHandler.removeCallbacksAndMessages(null);
     }
>>static class MyHandler extends Handler {
    WeakReference<Activity > mActivityReference;

    MyHandler(Activity activity) {
        mActivityReference= new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}	
  1. IntentFilter的匹配法则
  • action //匹配manifest中的其中一个
  • data //如果manifest声明了data匹配,则隐式intent必须设置data
  <data android:scheme=‘content’ host=‘pwcong.me’ path=/hello’ port=8080>
     Uri.parse(‘content://pwcong.me:8080/hello')
  • category
    //隐式Intent可以没有category
    a).隐式intent默认添加 android.intent.category.DEFAULT
    b).AndroidManifest
    c).隐式intent设置的category必须全部能匹配命中

例子 :1.val uri=Uri.parse(“http://www.baidu.com:80/2.jsp”)
val intent=Intent()
intent.setDataAndType(uri,“abc/eft”)

19.Fragment与Activity传值

  • 接口或者方法调用
    :1.在Fragment中可以通过getActivity()直接获得其绑定的Activity
  • 通过fragmentManager查找
    :1.在Activity中可以通过FragmentManager获取fragment的实例
 FragmentManager fragmentManager=getFragmentManager()
 Fragment fragment=fragmentManager.findFragmentByTag(tag)
 Fragment fragment=fragmentManager.findFragmentById(id)

:2.fragment的入栈行为

 Fragment oneFragment=OneFragment.newInstance(arg1:String,arg2:String)
 val transaction=getFragmentManager().beginTransaction()
 transaction.replace(R.id.frament_hodler,oneFragment,tag:String?)
 transaction.addToBackStack(null)
 transaction.commit()

20.Fragment 生命周期

>>onAttach						//Fragment与Activity发生关联
>>onCreate
>>onCreateView(LayoutInflater,ViewGroup,Bundle)	//创建Fragment视图
>>onActivityCreated(Bundle)				//当Activity的onCreate执行完
>>onStart->onResume->onPause->onStop
>>onDestroyView						//Fragment视图被移除
>>onDetach
>>Fragment使用
  a).//静态
	<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" >  
  
        <fragment  
          android:id="@+id/id_fragment_title"  
          android:name="com.zhy.zhy_fragments.TitleFragment"  
          android:layout_width="fill_parent"  
          android:layout_height="45dp" />  
  
        <fragment  
          android:layout_below="@id/id_fragment_title"  
          android:id="@+id/id_fragment_content"  
          android:name="com.zhy.zhy_fragments.ContentFragment"  
          android:layout_width="fill_parent"  
          android:layout_height="fill_parent" />  
        </RelativeLayout>  
  b).//动态
    a).FragmentManager fm = getFragmentManager();	
       ContentFragment contentFragment=(ContentFragment)fm.findFragmentById(R.id.id_fragment_container)
       if(contentFragment==null)
       {
	  contentFragment=new ContentFragment();
          fm.beginTransaction().add(R.id.id_fragment_container,contentFragment).commit();
       }
    b).宿主Activity必须实现OnFragmentInteractionListener
>>ActivityThread关于Fragment的回调
  a).Activity持有mFragments
	//FragmentController mFragments = FragmentController.createController(new HostCallbacks())
  b).HostCallbacks的初始化	//class HostCallbacks extends FragmentHostCallback<Activity>
     >public HostCallbacks(){
	 super(Activity.this)	//因为HostCallbacks为Activity的内部类
      }
  c).FragmentHostCallback
     >FragmentHostCallback(Activity activity)
      {
	   > FragmentHostCallback(activity,context,handler,windowAnimations) ->
	     mActivity=activity
	     mContext=context
	     mHandler=handler
      }
     >成员FragmentManagerImpl
  d).FragmentController
     >private FragmentController(FragmentHostCallback<?> callbacks) {  //至此,mFragments持有HostCallbacks
        mHost = callbacks;
      }
  e).ActivityThread::performLaunchActivity
     >attach…
     >mFragments.attachHost(null)	//主要是让mFragmentManager持有对外部HostCallbacks对象
     Activity::performCreate		//1.onCreate(bundle) 2.performCreateCommon()
     Activity::onCreate		//一般通过动态添加fragment时,都会在此调用getFragmentManager
     {
	super.onCreate(savedInstanceState)
		//mFragments.dispatchCreate()->mFragmentManagerImpl.dispatchCreate()
		//FragmentmnagerImpl::mCurState=Fragment.CREATED
	getFragmentManager()	//mFragments.getFragmentManager()-> mHost.getFragmentManagerImpl()
         .beginTransaction()	//生成BackStackRecord,并让其持有FragmentManagerImpl对象
         .replace(R.id.container,mContentFragment)
		//fragment.mContainerId =containerViewId
		//Op op = new Op() -> op.cmd = opcmd -> op.fragment = fragment -> addOp(op)
         .commit();//
     }
  1. Fragment的add和replace的区别 //replace==remove|append
>>	transaction.add(R.id.fragment_container, oneFragment).commit()
第一个参为容器id,第二个为需要添加的fragment,添加不会导致容器的内容被清空,
同一个fragment添加多次会报错,添加进去的fragment都会按层级显示,大多数情况都是与remove、hide
搭配用
>>	replace,replace(R.id.fragment_container, oneFragment).commit();
替换会把容器的所有内容都替换掉,只保存单独一个fragment
  1. Fragment如何实现Activity栈的压栈和出栈
>>	transaction.addToBackStack(null)         	//指定transaction对象
>>	getSupportFragmentManager().popBackStack()
  1. 什么情况下造成内存泄露
  • 资源释放问题 长期保存Context、Cursor的引用,资源得不到释放
  • 对象内存过大 Bitmap文件,如decodeStream不设置inSampleSize
  • static关键字的使用
    //考虑到static声明的对象不再属于类实例,而是属于类范围,因此对象的生命周期将会延长,用WeakReference进行替换
  • 线程内存溢出 1.将线程的内部类该为静态内部类,静态内部类不会对外部类对象拥有强引用
    2.在线程内部用WeakReference去引用Context
  • 采用软引用缓存图片//?
  1. 图片过大导致OOM
    等比例缩小图片
    1.获取图片的原始宽高
BitmapFactory.Options opts=new BitmapFactory.Options()
opts.inJustDecodeBounds=true
BitmapFactory.decodeStream(is, null, opts)

2.计算图片的压缩比

Bitmap bitmap=null
int i=0
while(true){
  if(opts.width>>i<=size&&opts.height>>I<=size)
  {
	//进行解析
	BitmapFactory.Options options=new BitmapFactory.Options()
	options.inSampleSize=1<<i
	Bitmap bmp=null
        bmp=BitmapFactory.decodeStream(is,null,options)	
  }
}

25.SoftReference跟WeakRefrence的区别

  • WeakRefrence => 与强引用对象的生命周期一致,不会增加引用计数
  • SoftReference => 内存不足时才进行回收

26.dp与px

  • dp转px
    a).float scale=context.getResources().getDisplayMetrics().density
    (int)(dpValue*scale+0.5f)
  • px转dp
    a).float scale=context.getResources().getDisplayMetrics().density
    pxValue/scale+0.5f

27.设置布局为一半宽、高

	DisplayMetrics metrics=new DisplayMetrics()
	getWindowManager().getDefaultDisplay().getMetrics(metrics)
	Constant.srceenHeight = metrics.heightPixels; 
	Constant.srceenWidth = metrics.widthPixels;

28.多分辨率支持的清单文件配置

android:normalScreens=true”	android:smallScreens=true”	
android:xlargeScreens=true”	android:anyDensity=true

29.Android的事件分发机制

//事件起点是DecorView
ViewGroupView的继承关系和接口
  :1.ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。
  :2.View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于ViewViewGroup::dispatchTouchEvent
  :1.intercepted = onInterceptTouchEvent(ev)  //是否截取点击touch_down事件
     -> 默认只有满足滑动条点击时时才返回true,可以重写
     -> intercepted==true
 	调用super.dispatchTouchEvent(et)		//View父类
  :2.遍历子View
     -> ArrayList<View> preorderedList=buildTouchDispatchChildList()//建立前序子view列表
        >根据子view下标进行遍历,z=elevation+translationZ
	>根据z的大小插入perorderedList
     -> 遍历子view,让其消费事件
     for (int i = childrenCount - 1; i >= 0; i--) {
	View child=getAndVerifyPreorderedView(i,children)
        if(!isTransfromedTouchPointInView(x,y,child,null))//触点不在child上
		continue;
	newTouchTarget = getTouchTarget(child)	//返回是否有匹配view的TouchTarget
	dispatchTransformedTouchEvent(ev,..,child..)	//当子View成功消费ev时,返true
	> offsetX=mScrollX-child.mLeft
    	> offsetY=mScrollY-child.mTop
	> boolean handled=child.dispatchTouchEvent(event)//此处递归
	> return handled
     -> 遍历结束
  	if(dispatchTransformedTouchEvent())//当子View在onTouch或者onTouchEvent上成功消费
        {						
	   newTouchTarget = addTouchTarget(child, idBitsToAssign)//新建TouchTarget
	   target.next=mFirstTouchTarget//将TouchTarget插入链首
	   mFristTouchTarget=target     //设置为头指针
	   alreadyDispatchedToNewTouchTarget=true.    //标记成功消费MotionEvent.ACTION_DOWN
	}
     -> 针对MotionEvent.ACTION_UP和MontionEvent.ACTION_MOVE
        target=mFirstTouchTarget
	while(target!=null)
	{
	  next:TouchTarget=target.next
	  if(dispatchTransformedTouchEvent(ev,...,target.child,...))
	     handled=true
	  target=next
	}
        
  :3.是否需要自己处理MotionEvent
     if (mFirstTouchTarget == null) {
       handled = dispatchTransformedTouchEvent(ev,.., null,TouchTarget.ALL_POINTER_IDS)
     }

View的事件分发顺序:
  :1.dispatchEvent流程 : onTouch > onTouchEvent >onTouchEvent(@Overide)
     onTouchEvent检查ACTION_UP,查看是否有mOnClickListener,有则回调onClick
  :2.onTouchEvent	//如果没有经过重写,则调用View.onTouchEvent
     >case MotionEvent.ACTION_UP:
        if (mPerformClick == null) {  
           mPerformClick = new PerformClick();  
        }  
        if (!post(mPerformClick)) {  
           performClick();  //mOnClickListener.onClick(this)
        }   
  

touch事件的层级传递
a).在setOnTouchListener中只有当ACTION_DOWN返回true,才会继续传来ACTION_UP、ACTION_MOVE
>>ViewGroup

>>ViewGroupView组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup>>触摸事件由Action_DownAction_MoveAciton_UP组成,其中一次完整的触摸事件中,DownUp都只有一个,Move有若干个,可以为0个。

>>Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true>>当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的MoveUp事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView>>ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

>>onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止UpMove事件向目标View传递,使得目标View所在的ViewGroup捕获UpMove事件。

32.ArrayList和LinkedList的区别

  • ArrayList
//基于数组进行实现
  :1.transient Object[] elementData;     //elementData存在一些没使用的空间,所以声明为transient
  :2.public void add(int index,E element)
     {
	ensureCapacityInternal(minCapacity:size + 1)	   //size代表当前非空元素的长度
   	  >if (minCapacity - elementData.length > 0)
            grow(minCapacity) 				   //给数组开辟新的空间,
            > int oldCapacity = elementData.length;
              int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容因子为3/2
              if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
              if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity)
	       elementData = Arrays.copyOf(elementData, newCapacity)//分配内存,并数组项的复制
        System.arraycopy(elementData,index,elementData,index+1,size-index)//移动
	elementData[index] = element;
        size++;
     }
  :3.public boolean addAll(Collection<? extends E> c){
	Object[] a=c.toArray()
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount,并且如果有需要进行扩容
        System.arraycopy(src:a, pos:0, dat:elementData, pos:size, len:numNew);
        size += numNew;
        return numNew != 0;	

     }
  :4.public boolean contains(Object o) -> return indexOf(o)   //时间复杂度为O(n)
     {
	 > if (o == null) {
             for (int i = 0; i < size; i++)
               if (elementData[i]==null)
                  return i;
               } 
           }else{
	       for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                      return i;
 	   }
	   return -1;
     }
  :5.public E remove(int index)
     {
	modCount++
 	int s = size
	E oldValue = (E) elementData[index]
	int numMoved=size-index-1
	if(numMoved>0)
	 System.arraycopy(src:elementData, srcPos:index + 1, dest:elementData, destPos:index, len:numMoved)
	
	elementData[-size]=null
	return oldValue
     }
  • LinkedList
//支持随机访问的双向链表
  :1.成员
    ->transient int size=0 
    ->transient Node<E> first,last	
  :2.在头部插入
     public void linkFirst(E e)
     {
	final Node<E> f=first
	final Node<E> newNode=new Node<>(prev:null,e,next:f)
	first=newNode//设置为头结点
	if(f==null)
	  last=newNode
	else
	  f.prev=newNode
	size++;modCount++;
     }
  :3.在尾部插入
     public void linkLast(E e)
     {
	final Node<E> l=last
	final Node<E> newNode=new Node<>(l,e,null)
	last=newNode
	if(l==null)
	  first=newNode
	else
	  l.next=newNode	
	size++;modCount++;
     }
  e).boolean add(E e)//默认尾部插入
     {
	linkLast(e)
	return true
     }
  f).boolean remove(Object o)
     {
	for(Node<E> x=first;x!=null;x=x.next)
	{
	  if(o.equals(x.item))
	  {
	    unlink(x);return true;
	  }
	}
	return false
     }
  g).E unlink(Node<E> x) 			//从链表中移除该结点
     {
	final E element=x.item
	final Node<E> next=x.next
	final Node<E> prev=x.prev
	//处理前置
	if(prev==null)//如果待删除结点的前置为空
	  first=next
	else{
	  prev.next=next
	  x.prev=null
	}

	//处理后置
	if(next==null)
	  last=prev
	else{
	  next.prev=prev
	  x.next=null
	}

	x.item=null;size—;modCount++;
	return element
     }

ArrayList和LinkedList在末尾增加一项的时间是固定的,但是ArrayList可能会造成数组空间的重新分配
在ArrayList中插入、删除元素会造成移动,而LinkedList不会
LinkedList不支持高效的随机元素访问
LinkedList的空间花费集中在内部类Node的开销,而ArrayList则体现在扩容

33.LruCache

//缓存淘汰框架
>>实现
  :1.采用LinkedHashMap作为数据结构
  :2.放入缓存
    >public final V put(K key,V value)
     {
	V previous;
	synchronized(this)
	{
	  putCount++
	  size+=safeSizeOf(key, value)
	  previous=map.put(key, value)
	  if(previous!=null)
	    size-=safeSizeOf(key, value)

	}
	trimToSize(maxSize)
     }
    >public void trimToSize(int maxSize){		//对队列进行调整
	while(true)
	{
	  K key;
	  V value;
	  synchronized(this){
	    if(size<=maxSize||map.isEmpty())
		break
	    Map.Entry<K,V> evict=map.entrySet().iterator().next()
	    key=evict.getKey()
	    value=evict.getValue()
	    map.remove(key)
	    size-=safeSizeOf(key, value)
	  }
	}
     }
 
>>示例
  :1.初始化
   int maxSize=(int)Runtime.getRuntime().maxMemory()/8;  //为总内存的1/8
   LruCache<String,Bitmap> cache=new LruCache<String,Bitmap>(maxSize)
   {
      @Override
      protected int sizeOf(String key, Bitmap value) {
          return value.getByteCount();
        }
    }
  :2.放入缓存
    cache.put(key:String, value:Bitmap)

36.什么是ANR,如何规避

  • ANR application not responding
    a).keyDispatch timeout 5s
    b).broadcast timeout 10s
    c).service timeout 20s

  • UI线程

  a).Activity&AsyncTask:onPreExecute、onPostExecute、onProgressUpdate
    >>public Result doInBackground(String …arg)
      {publishProgress(int)}
  • 避免keydisptach timeout

    a).ui线程不做耗时操作
    b).使用handler来处理与其它thread的交互
    //new Thread(new Runnable(){ public void run(){…handler.sendMessage(msg)}})
    //匿名内部类持有对外部类实例的引用,导致不能被回收,因将其改为内部静态类,并且把Activity声明为WeakReference

  1. 描述Service的启动方式
//主要针对Service和Activity的优先级比较
- Activity.startService	
- bindService(intent, conn, flag)		
  :1.flag的种类
    >BIND_AUTO_CREATE				//当service不存在时自动创立
    >BIND_ADJUST_WITH_ACTIVITY			//service的优先级随着其绑定的Activity发生变换,back<->fore
    >BIND_ABOVE_CLIENT				//service优先级超过Activity
    >BIND_IMPORTANT				//service优先级相当于前台Activity
    >BIND_NOT_FOREGROUND			//service优先级不高于前台Activity,但至少会提高到客户端优先级之上
- onStartCommand的返回标志
  :1.START_NOT_STICKY		//在经过onStartCommand之后,service挂了也不会重启
  :2.START_STICKY		//重启
  :3.START_REDELIVER_INTENT	//重启,并传入最后的intent
  1. Android有哪几种布局
  • LinearyLayout 按照水平或垂直的顺序排列子元素,android:layout_weight生效
  • FrameLayout 后来的子元素覆盖在之前子元素的上层,以左上角为基准点
  • AbsoluteLayout android:layout_x和android:layout_y会生效
  • RelativeLayout android:layout_below和android:layout_above

TableLayout //表格布局由TableRow构成,layout_column指定开始列,layout_span制定范围
a).全局属性
android:shrinkColumns=“0,1” //可收缩的列
android:stretchColumns=“0,1” //可拉伸的列
b).单元格属性
android:layout_span=“2” //该TableRow中的子View所横跨的列
android:layout_column=“1” //制定子view在哪列开始排列

  1. HashMap、HashTable的区别 //从线程安全性、速度
>>线程方面
  :1.HashTable是线程安全的
>>HashTable的key、value不允许设置null,而hashmap可以
>>是否可在遍历时进行操作
  :1.HashMap:fail-fast	
     a).既在遍历HashMap时,如果通过实例对象进行修改则报ConcurrentModificationException, 
      expectedModCount==modCount
>>解决冲突
  :1.HashMap的装载因子是3/4,链接法解决冲突,当链表过长时转化为红黑树
>>实现
  :1.HashMap 通过数组+链表的结构
    >transient Node<K,V>[] table;
    >static class Node<K,V> implements Map.Entry<K,V>{
	final int hash;
	final K key;
	final V value;
	Node<K,V> next;
	public final int hashCode(){
	  return Objects.hashCode(key)^Objects.hashCode(v)
	}
     }
  :2.hashCode 通过Objects.hashCode(key)^Objects.hashCode(value)计算
  :3.扩容  哈希桶的容量形若100...0,扩容容量为2倍,阀值也为2倍,需要rehash
    >final Node<K,V>[] resize(){
	//oldTab 为当前表的哈希桶
        Node<K,V>[] oldTab = table;
	int oldCap=oldTab.length
	//当前的阈值
        int oldThr = threshold;
	if (oldCap > 0) {
	  if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
	  {
	     newThr=oldThr<<1;
	  }
	}	
	//更新阈值 
        threshold = newThr
	table= newTab
        if(oldTab!=null){
	   for (int j = 0; j < oldCap; ++j) {
	     Node<K,V> e;
	     if((e=oldTab[j])!=null){
		oldTab[j] = null//将原哈希桶置空以便GC
		if (e.next == null)
		  newTab[e.hash & (newCap - 1)] = e; //直接将这个元素放置在新的哈希桶里
		else
		{
		   //因为扩容是容量翻倍,所以原链表上的每个节点,现在可能存放在原来的下标,即low位, 或者扩容后的下标,
		   //即high位。 high位=  low位+原哈希桶容量
                   //低位链表的头结点、尾节点
                   Node<K,V> loHead = null, loTail = null;
		   //高位链表的头节点、尾节点
                   Node<K,V> hiHead = null, hiTail = null;
                   Node<K,V> next;//临时节点 存放e的下一个节点
		   do {
                      next = e.next;
                      //这里又是一个利用位运算 代替常规运算的高效点: 利用哈希值 与 旧的容量 可以得到哈希值去模后,
		      //是大于等于oldCap还是小于oldCap,等于0代表小于oldCap,应该存放在低位,否则存放在高位
                      if ((e.hash & oldCap) == 0) {
                         //给头尾节点指针赋值
                         if (loTail == null)
                            loHead = e;
                         else
                            loTail.next = e;
                         loTail = e;
                       }//高位也是相同的逻辑
                          else {
                            if (hiTail == null)
                               hiHead = e;
                            else
                               hiTail.next = e;
                            hiTail = e;
                          }//循环直到链表结束
                        } while ((e = next) != null)
			//将低位链表存放在原index处,
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        //将高位链表存放在新index处
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
		}
 	     }

 	}
     }

  :4.插入节点
     >public V put(K key,V value){return putVal(hash(key),key,value)}
     >final V putVal(int hash,K key,V value,...)
      {
	Node<K,V>[] tab=table 
 	int n=tab.length
	Node<K,V> p;
	//如果当前哈希表是空的,代表是初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            //那么直接去扩容哈希表,并且将扩容后的哈希桶长度赋值给n
            n = (tab = resize()).length;
	//如果当前index的节点是空的,表示没有发生哈希碰撞。 直接构建一个新节点Node,挂载在index处即可。
        //这里再啰嗦一下,index 是利用 哈希值 & 哈希桶的长度-1,替代模运算
        if(p=tab[I=?(n-1)&hash)==null)		
	  tab[I]=newNode(hash,key,value,null)
	else//发生了冲突
	{
	  Node<K,V> e;K k;
  	  if(p.hash==hash && ((k=p.key)==key || key.equals(k))//键值相同,寻找成功
	     e=p;
	  else if(p instance TreeNode)			//红黑树
	     e=TreeNode<K,V)p.putTreeVal(this, tab,hash,key,value)
	  else{
 	     //遍历链表
	     for(int binCount=0;;++binCount)
	     {
		if((e=p.next)==null)			//遍历到尾部,追加新节点到尾部
		{  
		   p.next=newNode(hash, key, value)  	//生成结点并插入
		   if(binCount>=TREEIFY_THRESHOLD-1)    //将链表转化为树结构
		      treeifyBin(tab,hash)
		   break
		}
		//如果找到了要覆盖的节点
		if(e.hash==hash&&((k=e.key)==key)
		   break
		p=e					//迭代
	     }
	  }
	  if(e!=null)
	     e.value=value				//设置新值
	}
      }
  :5.删除结点
    >final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
	// p 是待删除节点的前置节点
        Node<K,V>[] tab; Node<K,V> p; int n, index;
	if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
	  Node<K,V> node = null, e; K k; V v
	  //如果链表头的就是需要删除的节点
          if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;//将待删除节点引用赋给node
	  else if ((e = p.next) != null) {//否则循环遍历 找到待删除节点,赋值给node
	     if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
              else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                          (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
              }
	  }
	  //如果有待删除节点node,  且 matchValue为false,或者值也相等
          if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
             if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
             else if (node == p)//如果node ==  p,说明是链表头是待删除节点
                tab[index] = node.next;
             else//否则待删除节点在表中间
                p.next = node.next;
             ++modCount;//修改modCount
             --size;//修改size
             return node;
          }
       }
     }
  :6.查找
   >final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //查找过程和删除基本差不多, 找到返回节点,否则返回null
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
   :7.iterator遍历
    > final Node<K,V> nextNode() {
         Node<K,V>[] t;
         Node<K,V> e = next;
         //fail-fast策略
         if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
         if (e == null)
            throw new NoSuchElementException();
         //依次取链表下一个节点,
         if ((next = (current = e).next) == null && (t = table) != null) {
           //如果当前链表节点遍历完了,则取哈希桶下一个不为null的链表头
           do {} while (index < t.length && (next = t[index++]) == null);
          }
          return e;
        }
40.LinkedHashMap 
>>成员 
  :1.LinkedHashMapEntry			//增加了before和after域,成为双向链表
     static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
        LinkedHashMapEntry<K,V> before, after;//双向链表
        LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
     }
  :2.head tail
   >transient LinkedHashMap.Entry<K,V> head	//双向链表的头结点
   >transient LinkedHashMap.Entry<K,V> tail	//双向链表的尾节点
  :3.accessOrder 	//默认为false,是按照插入结点的顺序,若为true,则可构造LruCache
>>重写newNode,HashMap调用putVal()方法被调用
    >//在构建新节点时,构建的是`LinkedHashMap.Entry` 不再是`Node`.
     Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
       LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
       linkNodeLast(p);  //!!!
       return p;
     }
     //将新增的节点,连接在链表的尾部
     private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        //集合之前是空的
        if (last == null)
            head = p;
        else {//将新节点连接在链表的尾部
            p.before = last;
            last.after = p;
        }
     }
>>删除结点
    >//在删除节点e时,同步将e从双向链表上删除
     void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        //待删除节点 p 的前置后置节点都置空
        p.before = p.after = null;
        //如果前置节点是null,则现在的头结点应该是后置节点a
        if (b == null)
            head = a;
        else//否则将前置节点b的后置节点指向a
            b.after = a;
        //同理如果后置节点时null ,则尾节点应是b
        if (a == null)
            tail = b;
        else//否则更新后置节点a的前置节点为b
            a.before = b;
     }
>>get  //获取结点
   >public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);//因为访问了该结点,所以动态调整其位置
        return e.value;
    }  
   >若accessOrder==true			//按访问顺序
    void afterNodeAccess(Node<K,V> e) { // move node to last
       LinkedHashMapEntry<K,V> last;//原尾节点
       //如果accessOrder 是true ,且原尾节点不等于e
       if (accessOrder && (last = tail) != e) {
            //节点e强转成双向链表节点p
            LinkedHashMapEntry <K,V> p =
                (LinkedHashMapEntry <K,V>)e, b = p.before, a = p.after;
            //p现在是尾节点, 后置节点一定是null
            p.after = null;
            //如果p的前置节点是null,则p以前是头结点,所以更新现在的头结点是p的后置节点a
            if (b == null)
                head = a;
            else//否则更新p的前直接点b的后置节点为 a
                b.after = a;
            //如果p的后置节点不是null,则更新后置节点a的前置节点为b
            if (a != null)
                a.before = b;
            else//如果原本p的后置节点是null,则p就是尾节点。 此时 更新last的引用为 p的前置节点b
                last = b;
            if (last == null) //原本尾节点是null  则,链表中就一个节点
                head = p;
            else {//否则 更新 当前节点p的前置节点为 原尾节点last, last的后置节点是p
                p.before = last;
                last.after = p;
            }
            //尾节点的引用赋值成p
            tail = p;
            //修改modCount。
            ++modCount;
        }
    }
  1. 红黑树

性质
a).每个结点是红色的或者黑色的
b).根结点是黑色的
c).每个叶结点(空结点)都是黑色的
d).如果一个结点是红色的,则它的子结点都是黑色的
e).对于每个结点而言,从该结点到其所有叶结点的简单路径上,包含相等数目的黑结点
引理

  a).n>=2^bh(x)-1
>>左旋
  LEFT-ROTATE(T,x)
  {
    y=x.right
    x.right=y.left
    if y.left!=T.nil
	y.left.p=x
    y.p=x.p
    if x.p==T.nil
	T.root=y
    else if x==x.p.left	//判断x是否位于其父结点的左子树,设置父结点的子树指针
    	x.p.left=y
    else
	x.p.right=y
    y.left=x			//左旋之后,x位于y的左分支
    x.p=y			//设置x父结点为y
  }
>>右旋
  RIGHT-ROTATE(T,x)
  {
    y=x.p
    if y.p==T.nil
    	T.root=x
    else if y.p.left==y
      	y.p.left=x
    else 
 	y.p.right=x
    x.p=y.p
    y.p=x
    y.left=x.right
    x.right=y
  }
>>插入
  RB-INSERT(T,z)		//插入任意结点都必须先标为红色
  {
    y=T.nil
    x=T.root
    while x!=T.nil
      	y=x
	if z.key<x.key
	  x=x.left
	else 
	  x=x.right
    z.p=y 			//设置父结点  	
    if y==T.nil
	T.root=z
    else if z.key<y.key
  	y.left=z
    else 
	y.right=z
    z.left=T.nil
    z.right=T.nil
    RB-INSERT-FIXUP(T,z)	//调整颜色
  }
  //分三种情况: 
  RB-INSERT-FIXUP(T,z)		 
  {
    //1.结点z的叔结点、父结点皆为红色
    //2.结点z和其父结点为红色,叔结点为黑色,结点z为左分支 
    //3.结点z和其父结点为红色,叔结点为黑色,结点z为右分支
   
  }
  1. Handler Looper 工作原理
    成员
  :1.class Looper{
      static final ThreadLocal<Looper> sThreadLocal =new ThreadLocal()//why declare staic?
      static Looper sMainLooper
      final MessageQueue mQueue
      final Thread mThread
     }

初始化

  :1.Looper.prepare()		
     if(sThreadLocal.get()!=null
	throw new RuntimeException
     sThreadLocal.set(new Looper(true))
  :2.public Looper(boolean quitAllowed)
     {	
	mQueue=new MessageQueue(quitAllowed)
	mThread=Thread.currentThread()
     }
  :3.开启事件循环
    public static Looper.loop()
    {
      final Looper me=myLooper()//  通过ThreadLocal获取Looper实例,通过Looper获取消息队列实例
      final MessageQueue queue = me.mQueue
      for (;;) {
	Message msg = queue.next()//通过消息队列阻塞性地获取消息
	if (msg == null) {return ;}// 跳出循环
	try{
	  msg.target.dispatchMessage(msg)//检查顺序:msg.callback->mCallback->handleMessage
	}
	finally{
	}
	msg.recycleUnChecked()	   //初始化msg属性,并放置在Message的sPool头部
      }
    }

最后Handler通过sendMessage将消息塞入mQueue队列
由Looper接收到了msg,然后调用Handler的dispatchMessage
Handler 检查mCallback->message.callback->mCallback的handleMessage

  1. Listview的几种adapter介绍
  • SimpleAdapter
//在一个行中能放入不同view组件
  a).SimpleAdapter(Context context,List<? extends Map<String, ?>> data,int resource,String[] from,int[] to)
  b).//示例

for (int i = 0; i < 20; i++) {
        Map<String, Object> item = new HashMap<String, Object>();
        item.put("touxiang", R.drawable.ic_launcher);//drawable
        item.put("name", "某某" + i);
        item.put("message", "今晚我请客吃饭");
        data.add(item);
}
listView = (ListView) findViewById(R.id.listview1);
simAdapt = new SimpleAdapter(
            this, 
            data, 
            R.layout.listview_item,
            new String[] { "touxiang", "name", "message" },// 与下面数组元素要一一对应
            new int[] { R.id.touxiang, R.id.tv_name, R.id.tv_message });
listView.setAdapter(simAdapt);
  • ArrayAdapter
//在一个行中只能放入TextView
  a).List<String> items=new ArrayList<>();
     items.add("item1");
     items.add("item2");
     listView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,items));
>>BaseAdapter		//动态构建行的布局和子组件
  a).static class LAdapter extends BaseAdapter{
	 private LayoutInflater mInflater;
        private Context mContext;
        public LAdapter(Context context) {
            mContext=context;
            mInflater=LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            return 0;
        }
        @Override
        public Object getItem(int position) {
            return null;
        }
        @Override
        public long getItemId(int position) {
            return 0;
        }

	@Override
	public int getItemViewType(int position){

	}
	 
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {   
          View v= LayoutInflater.from(mContext).inflate(R.layout.xxx,parent,false)
		//inflate传参:1).viewGroup为null,此时R.layout.xxx的根节点的layout_width、layout_height失效
    			      2).viewGroup不为null,attachToRoot=false,layout_width、layout_height有效
			      3).viewGroup不为null,attachToRoot=true,直接添加到父容器
	   return v;
        }
    }
  b).使用convertView进行优化
     >>if(convertView==null)	convertView=mInflater.inflate(R.layout.xx,null);
  c).使用ViewHolder进行优化
     >>static class ViewHolder
       {
    	  public ImageView img;
    	  public TextView title;
    	  public TextView info;
	}
     >>if(convertView == null)
       {
          holder = new ViewHolder();
          convertView = mInflater.inflate(R.layout.list_item, null);
          holder.img = (ImageView)item.findViewById(R.id.img)
          holder.title = (TextView)item.findViewById(R.id.title);
          holder.info = (TextView)item.findViewById(R.id.info);
          convertView.setTag(holder);				//mTag=holder
        }else
        {
          holder = (ViewHolder)convertView.getTag();		//return mTag
        }
        
        holder.img.setImageResource((Integer) data.get(position).get("img"));
        holder.title.setText((String)data.get(position).get("title"));
        holder.info.setText((String)data.get(position).get("info"));
        
	return convertView;
  1. 如何在ListView间添加分割线
//推荐用divider设置drawable的分割线
>>.设置全局属性			
   a).android:divider="#FFF"   	        //设置为null可无视间距     
   b).android:dividerHeight="1px"    
   c).默认的list view不支持设置footerDevider,采取在每个item布局中添加
      > <View
          android:layout_width="match_parent"
          android:layout_height="1dp"
          android:background="#999"
          />
  1. LinkedHashMap 源码解读

  2. Drawable转Bitmap

>>Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.icon_hint);
  1. Layout_weight

根据比重划分布局
a).根据LinearLayout的orientation,设置layout_width=0或者layout_height=0
b).layout_weight=xx,直接进行布局划分
根据剩余布局叠加进行划分
a).首先计算layout_width的布局之和layout_sum,layout_remain=width-layout_sum
b).得出子view的宽度:layout_width+layout_remain*layout_weight/sum

  1. android:layout_gravity和android:gravity的区别

android:layout_gravity 用于设置当前View在父容器的位置
android:gravity 用于设置当前View中文本、图像的位置

  1. 如何重用布局
    采用

  2. 使用merge减少include布局所造成的布局层次

<merge xmlns:android="http://schemas.android.com/apk/res/android"> 
  1. 如何优化布局

设置为invisible或者gone的View -> ViewStub
:1.ViewStub是一个不可见且大小为0的视图
:2.当ViewStub被设置为visible或者调用.inflate()时就会被指定的layout所代替
:3.inflatedId指定加载过来的layout id
:4.例子

  1. android:layout_gravity的使用规则

LinearyLayout 当orientation为horizontal时,存在 center\top\bottom
FrameLayout
a).android:layout_centerVertical

  1. TextView显示图像
>>android:drawableLeft 			//设置图像id
>>CharSequence charSequence=Html.fromHtml(html,new ImageGetter(){
	@Override
	public Drawable getDrawable(String source)
 	{
		Drawable drawable=getResources().getDrawable(getResourceId(source))
		drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight())
		return drawable
	}
  },null)
>>textView.setText(charSequence)

53.SpannableString的使用

  • 支持局部字符样式化
    a).
    b).setSpan(Object what,int start,int end,int flags)
    flags:Spanned.SPAN_INCLUSIVE_EXCLUSIVE //不包括终止下标
    :Spanned.SPAN_INCLUSIVE_INCLUSIVE //包括起始下标、终止下标
  • 采用ImageSpan设置文本图标
    a).
  Drawable drawable = getResources().getDrawable(R.mipmap.a9c);
     drawable.setBounds(0, 0, 42, 42);
     ImageSpan imageSpan = new ImageSpan(drawable);
     spannableString.setSpan(imageSpan, 6, 8, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)

>>ClickableSpan	设置响应点击	
  a).setSpan(new ClickableSpan(){
	@Override
	public void onClick(View v)
	{
	  //todo:利用intent执行跳转
	}
     },0,text.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

BackgroundColorSpan设置背景色
a).

  BackgroundColorSpan bcSpan=new BackgroundColorSpan(Color.YELLOW)
     spannableString.setSpan(bcSpan,start,end,Scannable.SPAN_EXCLUSIVE_EXCLUSIVE)

54.AutoCompleteTextView的使用

android:completionThreshold //至少用户输入多少个字符才显示
android:dropDownHeight //下拉列表的高度
android:dropDownWidth
android:completionHint //提示
setAdapter
a).String [] arr={“aa”,“aab”,“aac”};
ArrayAdapter arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,arr);
autotext.setAdapter(arrayAdapter);

  1. 在按钮上显示图像的方式有哪些
    Button android:drawableXXX(left|rigth|…)
    Button继承自TextView,可以通过采用SpannableString进行setText
    ImageButton
    a).layout_width&layout_height //设置了具体的宽高后,图像并不会拉伸,而是背景增大
    b).src //设置图片
    c).不设置宽高,表现与ImageView一致

  2. 如何动态改变Button的大小和位置
    View.layout(left,top,right,bottom)

  3. 如何让一个显示图像的按钮在不同状态下显示不同图像

drawable	//通过selector标签构建xml
  <selector>
	<item android:state_pressed=true”	android:drawable=@drawable/pressed”/>
	<item android:state_focused=true”	android:drawable=@drawable/focused”/>
	<item android:drawable=@drawable/normal”/>
  </selector>	
>>程序里设置监听

  a).button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Drawable drawable=getResources().getDrawable(R.mipmap.ic_launcher);
                v.setBackground(drawable);
            }
        });

  b).button.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {

            }
        });
  1. 如何实现图像的半透明度
    自定义View 在onDraw中绘制到canvas上
    InputStream is=getResources().openRawResource(R.drawable.image)
    Bitmap bitmap=BitmapFactory.decodeStream(is)
    public void onDraw(Canvas canvas)
    {
      Paint paint=new Paint();
      paint.setAlpha(180);
      canvas.drawBitmap(bitmap,.,paint)
    }
>>在图像上方覆盖一个半透明的图像	//设置android:background,父组件采用FrameLayout
  1. HttpURLConnection
    URL
url=new URL("http://www.baidu.com");
   HttpURLConnection connection= (HttpURLConnection) url.openConnection();
   connection.setDoInput(true);
   InputStream inputStream= connection.getInputStream();
   byte[] buffer=new byte[1024];
   int hasRead=0;

BufferedReader

//inputStream进行包装,可进行整行读取
  a).BufferedReader br=new BufferedReader(is);
     while((line=br.readLine())!=null)
	result+=line+”\n”;
  c).网页也是字符组织,故该方法可行

BitmapFactory.decodeStream(is)

//读取图片,返回的Bitmap用完要记得销毁,只有BufferedInputStream实现了reset
  1. ProgressBar
  • 属性设置
    a).style=“@android:style/Widget.ProgressBar.Horizontal”
    b).android:max=“100”
    c).android:progress=“50”
  • 属性更新
    a).setProgress(int v)
    b).incrementProgressBy(int step)
  1. AbsListView
    layoutChildren
    a).ListView::layoutChildren

  2. ListView,如何进行数据的增删改查

数据更新
a).Adapter::notifyDataSetChanged() //只使用于数据集的某项改变,更改引用将不会有效
b).Adapter::update //自定义方法,直接更新对应holder
BaseAdapter.notifyDataSetInvalidated()
a).针对adapter数据源改变引用的情况

  1. ListView中如何显示数据库中的数据
setAdapter(CursorAdapter adapter)			//重写子类继承CursorAdapter
  a).@Override
     public View newView(Context context, Cursor cursor, ViewGroup parent) {
           
        ViewHolder viewHolder= new ViewHolder();
        LayoutInflater  inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view=inflater.inflate(R.layout.item_contacts ,parent,false);
        viewHolder.tv_name=(TextView) view.findViewById(R.id.tv_showusername );
	 view.setTag(vieHolder);
        return view;
     }
     
     @Override
     public void bindView(View view, Context context, Cursor cursor) {
        Log. i("cursor" ,"bindView=" +view);
        ViewHolder viewHolder=(ViewHolder) view.getTag();
        //从数据库中查询姓名字段
        String name=cursor.getString(cursor.getColumnIndex(PersonInfo.NAME));
        //从数据库中查询电话字段
        String phoneNumber=cursor.getString(cursor.getColumnIndex(PersonInfo.PHONENUMBER));        
        viewHolder. tv_name.setText(name);
        viewHolder. tv_phonenumber.setText(phoneNumber);
     }
  • CursorAdapter源码
  a).class CursorAdapter extends BaseAdapter{
	
       public Object getItem(int position) {
         if (mDataValid && mCursor != null) {
            mCursor.moveToPosition(position);
            return mCursor;
         } else {
            return null;
         }
       }

       public int getCount() {
         if (mDataValid && mCursor != null) {
            return mCursor.getCount();
         } else {
            return 0;
         }
       }

       public long getItemId( int position) {
         if (mDataValid && mCursor != null) {
            if ( mCursor.moveToPosition(position)) {
                return mCursor.getLong( mRowIDColumn)	//mRowIDColumn==cursor.getColumnIndex(“_id”)
            } else {
                return 0;
            }
         } else {
            return 0;
         }
       }
       
       public View getView( int position, View convertView, ViewGroup parent) {
         if (!mDataValid) {
            throw new IllegalStateException( "this should only be called when the cursor is valid");
         }
         if (!mCursor.moveToPosition(position)) {
            throw new IllegalStateException( "couldn't move cursor to position " + position);
         }
         View v;
         if (convertView == null) {
            v = newView( mContext, mCursor, parent);
         } else {
            v = convertView;
         }
         bindView(v, mContext, mCursor);
         return v;
      }


     }

66.android TypedArray

在res/value下新建attr.xml文件

  a).<?xml version="1.0" encoding="utf-8"?>  
     <resources>  
       <declare-styleable name="MyView">  
         <attr name="myTextSize" format="dimension"/>  
         <attr name="myColor" format="color"/>  
 	  <attr name=“title” format=“string”/>
    	</declare-styleable>  
     </resources>  

在布局中声明自己的类空间,并进行组件引用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:myapp="http://schemas.android.com/apk/res/com.eyu.attrtextdemo"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical"  
    tools:context=".MainActivity" >  
  
    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="@string/hello_world" />  
    <com.eyu.attrtextdemo.MyView  
        android:layout_height="wrap_content"  
        android:layout_width="wrap_content"  
        myapp:myTextSize="20sp"  
        myapp:myColor="#324243"/>  
  
</LinearLayout>  

在自定义view的构造函数中获取属性

//declare-styleable name=“MyView”
  a).TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyView);      
     int textColor = a.getColor(R.styleable.MyView_myColor, 003344);  
     float textSize = a.getDimension(R.styleable.MyView_myTextSize, 33);  
  1. 如何动态加载apk文件(未安装)的类
>>DexFile dexFile=dalvik.system.DexFile.loadDex(/sdcard/my.apk’,/sdcard/my_temp.apk’,0);
>>Object obj=dexFile.loadClass(mobile.android.file.explorer.widget.Test,null).newInstance()
>>Method method=obj.getClass().getDeclaredMethod(‘getName’,null)
>>String result=String.valueOf(method.invoke(obj,null))

69.Android ContentProvider

  • 定义权限
  :1.<permission
      android:name="top.shixinzhang.permission.READ_CONTENT"    //指定权限的名称
      android:label="Permission for read content provider"
      android:protectionLevel="normal"    
      />
  • 设置访问provider所需要的权限

  • 在应用中注册权限
  • 继承ContentProvider
  :1.成员
     private static final UriMatcher matcher;	//用于匹配Uri
     public static final String AUTHORITY="com.danding.lProvider"
     public static final Uri CONTENT_URI=Uri.parse("content://lprovider/person")
  :2.初始化
	>static{
	  mUriMatcher.addURI(AUTHORITY,"person",1)
	}
	>onCreate(){
	  mDatabase = new DbOpenHelper(mContext).getWritableDatabase();
	}
  :3.查询
  public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs)
     {
	Cursor c=null;
	switch(matcher.match(uri)){
	  case 1:
	    c=mSqliteDatabase.query(tableName,projection,selection,selectionArgs,
		groupBy:null,having:null,orderBy:null) 	    
	    break;

	}
     }
  b).public Uri insert(Uri uri,ContentValues cv)
     {
	low rowId=mSqliteDatabase.insert(tableName,nullColumnHack:”_id”,cv)
	if(rowId>0)
	{
	  Uri insertedUri=Uri.withAppendedPath(Uri.parse(authorities),/user/+rowId)
	  getContext().getContentResolver().notifyChange(insertedUri,null)
	}
     }
  c).public int delete(Uri uri,String selection,String[] selectionArgs)
  d).public int update(Uri uri,ContentValues cv,String selection,String[] selectionArgs)
     {
	//sqlite:  mSqliteDatabase.execSQL(“update student set name=‘dd’ where ….”) 
	int affected=mSqliteDatabase.update(tableName,cv,selection,selectionArgs);
	return affected;
     }
  e).public String getType(Uri uri)
>>ContentObserver		//监听ContentProvider数据改变
  :1.getContentResolver().registerContentObserver(Uri uri, boolean notifyForDescendents,ContentObserver co)
    //设置监听,ContentObserver需要自己实现
  :2.getContext().getContentResolver().notifyChange(uri,null)	//在provider中的更新和删除通知该uri已发生变化

客户端调用

  @Override
  protected void onCreate(Bundle savedInstanceState)
  {
     super.onCreate(savedInstanceState)Uri uri=Uri.parse(“content://com.ryg.chapter_2.book.provider”);
     getContentResolver().query(uri,null,null,null,null,null);	
     getContentResolver().registerContentObserver(uri,true,Observer)
  }

  static class LObserver extends ContentObserver{
    public LObserver(Handler handler)
    {
	super(handler);	//handler作用是用于更新ui
    }
    public void onChange(boolean selfChange)
    {
	Cursor cursor=getContentResolver().query()
	//do ui op
    }
  }
  1. 如何设置Activity为程序启动的默认Activity
>><intent-filter>
    <action android:name=“android.intent.action.MAIN”/>
    <category android:name=“android.intent.category.LAUNCHER”/>
  </intent-filter>
  1. 启动Activity的几种方式
>>startActivity	 intent
>>startActivityForResult
  1. Activity传递数据的方式
  • Intent
    //setData是用于设置uri
    a).intent.putExtra(key,value) getIntent().getExtras().getString(key)
  • 类的静态变量
  • 剪切板
    a).
  ClipboardManager cm=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE)
     >>设置
       ClipboardManager cm=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
       cm.setPrimaryClip(ClipData.newPlainText(“name”,”dancing”));
     >>获取
       cm.getPrimaryClip().getItemAt(0).getText().toString()
  • 全局对象 继承Application,同时修改manifest的的android:name
  1. 如何将Activity设置成半透明的模态框
>>在style.xml定义继承自@android:style/Theme.Dialog的主题
>><style name=‘test’ parent=@android:style/Theme.Dialog>
    <item name=‘android:windowBackground”>@color/colorAccent</item>
  </style>

74.如何接收广播
继承BroadcastReceiver,在onReceive(Context context,Intent intent)中处理广播消息

  a).public class LReceiver extends BroadcastReceiver{
	@Override
	public void onReceive(Context context,Intent intent)
	{
	   String msg=intent.getExtras().get(“msg”).toString();
	}
     }

orderedBroadcast

  a).设置传播结果 -> setResultExtras(bundle)
  b).获取传播结果 -> Bundle bundle= getResultExtras(makeup:true)
  • 在AndroidManifest.xml通过intent-filter指定action,category,data
  1. 如何获取短信内容
    指定intent-filter

  2. 如何拦截手机屏幕休眠和唤醒
    通过程序里注册广播监听 ScreenOnOffReceiver
    ScreenOnOffReceiver receiver=new ScreenOnOffReceiver()

  IntentFilter filter=new IntentFilter()
  filter.addAction(Intent.ACTION_SCREEN_ON)
  filter.addAction(Intent.ACTION_SCREEN_OFF)
  registerRecei	ver(receiver,filter)
  1. 如何发送广播
Intent intent…
  intent.addAction(‘…’)
  intent.putExtra(key,val)
  sendBroadcast(intent)
  Bundle bundle=intent.getExtras()//当广播拿到消息
  1. AIDL与Service
>>AIDL的创建
  a).public class Product implements Parcelable{
	private int id;
	private String name;
	private float price;
	public Product()
	{}
	public Product(Parcel in)
	{
	  id=in.readInt()
	  name=in.readString()
	  price=in.readFloat()	
	}
	public void writeToParcel(Parcel dest,flags)		//写入
	{
		dest.writeInt(id)
		dest.writeString(name)
		dest.writeFloat(price)
	}
	
	//声明CREATOR常量
	public static final Parcelable.Creator<Product>  CREATOR=new Parcelable.Creator<Product>{
		public Product createFromParcel(Parcel in)	//读出
		{
			return new Product(in)
		}
               
               @Override
               public Product[] newArray(int size) {
                 return new Product[size];
               }
	}
    }
>>
  1. 如何读取联系人信息
    Cursor cursor=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null)

  2. 请描述Content Provider的uri由哪几部分组成

//content://mobile.android.mydata/product/20
>>scheme	content://
>>authority	域名,mobile.android.mydata
>>path		路径,product
>>param		20
  1. 介绍开发ContentProvider的步骤
    编写一个类,继承ContentProvider
    实现query\update\delete\insert
    在static块中创建UriMatcher,add需要映射的uri,初始化SqliteOpenHelper和SqliteDatabase的实例
    manifest中注册provider并填写authorities属性,readPermission

  2. 如何为ContentProvider添加访问权限

>>在manifest中声明 自定义权限
  <permission android:name=“xxx” android:protectionLevel=“normal”/>
>>添加权限限制		//provider可添加android:permission、android:readPermission、android:writePermission
  a)<provider
    android:name=".MyContentProvider"
    android:authorities=“….......
    android:readPermission=“com.danding.lPermission” />
  b)在应用中去使用权限
    <uses-permission android:name=“com.danding.lPermission”/>
  

84.AlertDialog

构建

  a).AlertDialog dialog= new AlertDialog.Builder(this)		//不设置title不显示icon
       .setIcon(R.drawable.menu_contact)
       .setTitle("title")
       .setMessage("message")
       .setPositiveButton("确定", new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {

           }
        })
       .setNegativeButton("返回", new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
                        
           }
        })
        .create();
     dialog.show();
     
     //设置button的颜色和大小
     dialog.getButton(dialog.BUTTON_NEGATIVE).setTextColor(Color.parseColor(“#009900))     

显示列表项

			//与setMessage冲突
  a).builder.setItems(Items, new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialogInterface, int i) {
         Toast.makeText(getApplicationContext(), "You clicked "+Items[i],          						Toast.LENGTH_SHORT).show();
           }
      });

显示单选列表项

  a).setSingleChoiceItems(CharSequence[] items,int checkedItem,DialogInterface.OnClickListener)

显示多选列表

  a).setMultiChoiceItems(items,new boolean[]{true,true,false,true},new 							DialogInterface.OnMultiChoiceClickListener() {})

显示自定义列表

  a).setAdapter(? extends BaseAdapter)	//注意用ConvertView和ViewHolder提高性能 

显示自定义View

  a).setView(view)

85.如何自己控制对话框的关闭时机

控制mShowing 当它为false时对话框关闭不起作用

try{
	Field field=dialog.getSuperClass().getDeclaredField(‘mShowing’)
	field.setAccessible(true)
	field.set(dialog,false)
     }

86.如何改变对话框的透明度

>>	Window window=dialog.getWindow()
	WindowManager.LayoutParams lp=window.getAttributes()
	lp.alpha=0.7f
	window.setAttributes(lp)

87.如何自己控制Toast的显示和关闭

>>Toast.TN.show、Toast.TN.hide 	打开和关闭对话框,对话框不会自动关闭
>>Toast toast=Toast.makeText(this,’…’,Toast.LENGTH_LONG)
  try{
	Field field=toast.getClass().getDeclaredField(’mTN’)
	field.setAccessible(true)
	Object ob=field.get(toast)
	Method method=ob.getClass().getDeclaredMethod(‘show’,null)
	method.invoke(ob,null)
  }
  1. 如何使用Notification
    /属于remoteView
    构建notification
  :1.NotificationManager nm=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)
     Notification.Builder builder=new Notification.Builder(this)
     Notification notification=builder
	.setAutoCancel(true)
	.setContentTitle(‘title’)
	.setContentText(‘something here’)
	.setContentIntent(PendingIntent:pi)	
	.setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE)
	.setWhen(System.currentTimeMillis())
	.setProgress(max:int,progress:int,indeterminate:false)
	.build();
     nm.notify(1,notification)

Notification的重复发送
a).nm.notify(id:int,Notification:xx) //覆盖已有的相同id的Notification,可用于更新progress
b).contentIntent(PendingIntent:pi) //可针对Flag动态更新PendingIntent:Intent的内容
>PendingIntent pi=PendingIntent.getActivity(context:this,
requestCode:2,intent,PendingIntent.FLAG_UPDATE_CURRENT)
//每次都更新intent的内容,requestCode用于判断是否是相同的pi
取消Notification
a).nm.cancel(id)

  1. PendingIntent使用 //跨进程intent
    跳转到Activity getActivity(Context context,int requestCode,Intent intent,int flags)
    发送广播 getBroadcast(Context context,int requestCode,Intent intent,int flags)
    启动Service getService(Context context,int requestCode,Intent intent,int flags)
    Flag变量说明 //requestCode和notifyId作为识别一个PendingIntent的条件
    FLAG_CANCEL_CURRENT //取消当前已存在的PendingIntent
    FLAG_UPDATE_CURRENT //更新之前相同PendingIntent的内容
    FLAG_ONE_SHOT //PendingIntent只能有效一次
    FLAG_NO_CREATE

  2. 点击Notification触发Activity跳转

contentIntent
PendingIntent contentIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
notification.contentIntent=contentIntent

91.如何自定义Notification的view

设置RemoteViews

RemoteViews remoteViews=new RemoteViews(getPakcageName(),R.layout.xxx)
remoteViews.setTextViewText(R.id.xxx,’xxx’)
remoteViews.setOnClickPendingIntent(R.id.xxx,pendingIntent) //设置点击的响应
notification.contentView=remoteViews

92.然后为一个Activity添加选项菜单
menu.add(charSequence) //添加1级菜单子项
menu.addSubMenu(charSequence) //添加子菜单
menu.add((int groupId, int itemId, int order, charsequence title) //itemId用于在回调时识别这个MenuItem

//示例
  :1.public void onCreateOptionsMenu(Menu menu)
    {
      menu.add(groupId:0,itemId:1,order:0,title:”red”)	
      menu.add(0,2,0,”green”).setIcon(R.drawable.xxx)
      return super.onCreateOptionsMenu(menu);
    }
  :2.getMenuInflater().inflate(R.menu.main, menu);  //R.menu.main直接当作子菜单加入menu
  :3.public boolean onOptionsItemSelected(MenuItem item) {
	 int groupId=item.getGroupId()
        int itemId=item.getItemId()
        Log.v("options_item",itemId+"")
        return super.onOptionsItemSelected(item)
     }
  :4.public boolean onCreateOptionsMenu(menu:Menu?)
   {
	menuInflater.inflate(R.menu.xxx,menu)
	return super.onCreateOptionsMenu(menu)
   }
  1. 如何将上下文菜单绑定至可视组件View
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo){
        // 选择菜单 一样 进行打气使用
        getMenuInflater().inflate(R.menu.menc_main, menu);
        super.onCreateContextMenu(menu, v, menuInfo);
    } 
public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.xxx:
          // 内容
          break}
    return super.onContextItemSelected(item);
  }
registerForContextMenu(button)
  1. 在单击菜单时,如何能跳转进别的Activity
MenuItem item=menu.add(0,1,0,name)
  item.setIntent(new Intent(this,ActivityB.class)
  1. 菜单的回调函数有哪些
>>onOptionsItemSelected
>>onContextItemSelected
  1. 如何使用SharedPreferences存取数据
    读取
//存放文件位于/data/data/{package}/shared_prefs/PREFERENCE_NAME
  SharedPreferences sharedPreferences=Context.getSharedPreferences(PREFERENCE_NAME,Context.MODE_PRIVATE)
  sharedPreferences.getXXX(key,null)

写入

  SharedPreferences.Editor editor=sharedPreference.edit()
  editor.putXXX(key,value)
  editor.commit()
  1. SharedPreferences //构造函数(string name,int mode)
>>mode
  a).MODE_PRIVATE	//默认针对应用包进行访问控制,新的写入会
  b).MODE_APPEND			
>>ObjectOutputStream out = new ObjectOutputStream(new   
  FileOutputStream("employee.dat"));   
  out.writeObject(staff); //将对象写入"employee.dat"中   
  out.close(); //关闭流,请牢记   
>>写入SharedPreferences
  Product product=new Product()
  ByteArrayOutputStream bos=new ByteArrayOutputStream()
  ObjectOutputStream oos=new ObjectOutputStream(bos)
  oos.writeObject(product)
  SharedPreferences sp=Context.getSharedPreferences(this,Context.MODE_PRIVATE)
  String product64=new String(Base64.encodeBase64(bos.toByteArray()))
  SharedPreferences.Editor editor=sharedPreferences.edit()
  editor.putString(‘product’,product64)
  editor.commit()
>>SharedPreferences中装载		
  String product64=sharedPreferences.getString(‘product’,’’)
  byte[] bytes=Base64.decode(product64.getBytes(),Base64.DEFAULT);
  ByteArrayInputStream bis=new ByteArrayInputStream(bytes)
  ObjectInputStream ois=new ObjectInputStream(bis)
  product product=(Product)  
  ois.readObject()		//Product implements Serializable

99.Android如何解析xml文件
xml文件放置在res/xml/xxx.xml
采用XmlResourceParser进行解析

 XmlResourceParser xrp=getResources().getXml(R.xml.books);
        try{
            StringBuilder sb=new StringBuilder("");
            while(xrp.getEventType()!=XmlResourceParser.END_DOCUMENT)	  //文档解析结束
            {
                //遇到开标签
                if(xrp.getEventType()==XmlResourceParser.START_TAG)
                {
                    String tagName=xrp.getName();	//获取标签属性
                    if(tagName.equals("book"))
                    {
                        String bookPrice=xrp.getAttributeValue(namespace:null,name:”price");
                        sb.append("price:");
                        sb.append(bookPrice);

                        String bookYear=xrp.getAttributeValue(null,"date");
                        sb.append("date:");
                        sb.append(bookYear);

                        sb.append("name:");
                        sb.append(xrp.nextText());

                    }
                    sb.append("\n");
                }else if(xrp.getEventType()==XmlResourceParser.TEXT)
  		 {
		     sb.append(xrp.getText());
 		 }
                xrp.next();
            }

        }catch (Exception e)
        {
            e.printStackTrace();
        }
  1. gson
    解析json
  :1.val gson=Gson()
     val goAbroad:AuditGoAbroad=gson.fromJson(str,AuditGoAbroad::class.java)

转为string

  :1.val gson=Gson()
     val str=gson.toJson(tmp)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李一恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值