网上android的一些面试题和答案

1,如何优化listView中的数据?
第一,复用ConvertView,减少view的创建次数。
也是最普通的优化,就在MyAdapter类中的getView方法中,我们注意到,上面的写法每次需要一个View对象时,都是去重新inflate一个View出来返回去,没有实现View对象的复用,而实际上对于ListView而言,只需要保留能够显示的最大个数的view即可,其他新的view可以通过复用的方式使用消失的条目的view,而getView方法里也提供了一个参数:convertView,这个就代表着可以复用的view对象,当然这个对象也可能为空,当它为空的时候,表示该条目view第一次创建,所以我们需要inflate一个view出来
 
第二:创建一个ViewHolder类,减少findViewById的次数,优化内存。
基本思路就是在convertView为null的时候,我们不仅重新inflate出来一个view,并且还需要进行findviewbyId的查找工作,但是同时我们还需要获取一个ViewHolder类的对象,并将findviewById的结果赋值给ViewHolder中对应的成员变量。最后将holder对象与该view对象“绑”在一块。
当convertView不为null时,我们让view=converView,同时取出这个view对应的holder对象,就获得了这个view对象中的TextView组件,它就是holder中的成员变量,这样在复用的时候,我们就不需要再去findViewById了,只需要在最开始的时候进行数次查找工作就可以了。这里的关键在于如何将view与holder对象进行绑定,那么就需要用到两个方法:setTag和getTag方法
 
第三:进行分批加载。
比如说1000条新闻的List集合,我们一次加载20条,等到用户翻页到底部的时候,我们再添加下面的20条到List中,再使用Adapter刷新ListView,这样用户一次只需要等待20条数据的传输时间,不需要一次等待好几分钟把数据都加载完再在ListView上显示。其次这样也可以缓解很多条新闻一次加载进行产生OOM应用崩溃的情况。
 
第四:进行分页加载。
分批加载也不能完全解决问题,因为虽然我们在分批中一次只增加20条数据到List集合中,然后再刷新到ListView中去,假如有10万条数据,如果我们顺利读到最后这个List集合中还是会累积海量条数的数据,还是可能会造成OOM的情况,这时候我们就需要用到分页,比如说我们将这10万条数据分为1000页,每一页100条数据,每一页加载时都覆盖掉上一页中List集合中的内容,然后每一页内再使用分批加载,这样用户的体验就会相对好一些。
2,android中oom产生的原因,以及如何处理?
定义:OOM就是内存溢出,即Out Of Memory。也就是说内存占有量超过了所分配的最大,一般常见的有16M,24M,32M。
产生的两种原因:
第一种原因:应用中需要加载大对象,例如Bitmap,bitmap分辨率越高,所占用的内存就越大,这个是以2为指数级增长的。
第二种情况:长期保存某些大型资源的应用,资源得不到释放,导致Memory Leak . 也就是我们说的内存泄漏:
1 静态变量导致的Memory leak,慎用静态,因为它的生命周期太长。
2 不合理使用Context 导致的Memory leak。
3 非静态内部类导致的Memory leak
4 Drawable对象的回调隐含的Memory leak
5,cursor没有关闭或者file的流没有关
6使用9path代替大图
7,adapter中使用convertView
 
解决办法:
1,数据库的cursor没有关闭。
2.构造adapter没有使用缓存contentView。
3.调用registerReceiver()后未调用unregisterReceiver().
4.未关闭InputStream/OutputStream。
5.Bitmap使用后未调用recycle()。
6.Context泄漏。
7.static关键字
3,如何避免oom的产生?
1,当我们需要显示大的bitmap对象或者较多的bitmap的时候,就需要进行压缩来防止OOM问题。我们可以通过设置BitmapFactory.Optiions的inJustDecodeBounds(边界压缩)属性为true,这样的话不会加载图片到内存中,但是会将图片的width和height属性读取出来,我们可以利用这个属性来对bitmap进行压缩。Options.inSampleSize 可以设置压缩比。
2,使用开源框架Universal-Image-Loader加载大图片,它中维护了一个LRUCache算法,它内部维护了一个LinkedHashMap和maxSize;每次添加(put)的时候会拿当前map中的size和maxSize进行比较; 如果超过maxSize,则移除最早添加的。先从内存中加载,如果没有,从文件中加载,如果都没有,可以开启一个子线程从网络中加载。一旦加载成功,取到bitmap,就直接把这个bitmap设置到对应的ImageView控件上。
4,listView加载大图片的时候,如何解决图片错位或闪退的问题?
原因:
listview 异步加载图片之所以错位的根本原因是重用了 convertView且有异步操作。
如果不重用 convertView 不会出现错位现象, 重用 convertView但没有异步操作也不会有问题。
原理:
在getView的时候,给ViewHolder类中的ImageView设置一个Tag,这个Tag中设置的是图片的url地址,然后在异步加载图片完成回调的方法中,使用findViewWithTag(String url)来找到ListView中position对应的ImagView,然后给该ImageView设置图片即可。
解决方案:
在getView的时候,给ViewHolder类中的ImageView设置一个Tag, 并预设一个图片。
5,AsyncTask的工作原理:
这个类也是一个很重要也很常用的类。它封装了Thread和Handler,我们使用就更加方便,不用关注Handler,我们知道,在后台线程中是不能更新UI,而很多情况下,我们在后台线程做完一件事情后,一般都会更新UI,一般的做法是向关联到UI线程的Handler发送一个message,在Handler里面去处理这个message,从而更新UI。用了AsyncTask之后,我们就不用关注Handler了。这个类有几个重要的方法:
    1、onPreExecute():在UI线程里面调用,它在这个task执行后会立即调用。我们在这个方法里面通常是用于建立一个任务,比如显示一个等待对话框来通知用户。
    2、doInBackground(Params...):这个方法从名字就可以看出,它是运行在后台线程的,在这个方法里面,去做耗时的事情,比如下载访问网络,操作文件等。在这个方法里面,我们可以调用publishProgress(Progress...)来调用当前任务的进度,调用了这个方法后,对应的onProgressUpdate(Progress...)方法会被调用,这个方法是运行在UI线程的。
    3、onProgressUpdate(Progress...):运行在UI线程,在调用publishProgress()方法之后。这个方法用来在UI上显示任何形式的进度,比如你可以显示一个等待对话框,也可以显示一个文本形式的log,还可以显示toast对话框。
    4、onPostExecute(Result):当task结束后调用,它运行在UI线程。
    5、取消一个task,我们可以在任何时候调用cancel(Boolean)来取消一个任务,当调用了cancel()方法后,onCancelled(Object)方法就会被调用,onPostExecute(Object)方法不会被调用,在doInBackground(Object[])方法中,我们可以用isCancelled()方法来检查任务是否取消。
    6、几点规则
AsyncTask实例必须在UI线程中创建  
execute(Params...)方法必须在UI线程中调用。
不用手动调用onPreExecute(), doInBackground(), onProgressUpdate(),onPostExecute()方法。
一个任务只能被执行一次。
6,Handler消息机制。
Message:消息;其中包含了消息ID,消息对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理
Handler:处理者;负责Message发送消息及处理。Handler通过与Looper进行沟通,从而使用Handler时,需要实现handlerMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等(主线程中才行)
MessageQueue:消息队列;用来存放Handler发送过来的消息,并按照FIFO(先入先出队列)规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等Looper的抽取。
Looper:消息泵,不断从MessageQueue中抽取Message执行。因此,一个线程中的MessageQueue需要一个Looper进行管理。Looper是当前线程创建的时候产生的(UI Thread即主线程是系统帮忙创建的Looper,而如果在子线程中,需要手动在创建线程后立即创建Looper[调用Looper.prepare()方法])。也就是说,会在当前线程上绑定一个Looper对象。
Thread:线程;负责调度消息循环,即消息循环的执行场所。
 
具体流程:
0、准备数据和对象:
①、如果在主线程中处理message(即创建handler对象),那么如上所述,系统的Looper已经准备好了(当然,MessageQueue也初始化了),且其轮询方法loop已经开启。【系统的Handler准备好了,是用于处理系统的消息】。【Tips:如果是子线程中创建handler,就需要显式的调用Looper的方法prepare()和loop(),初始化Looper和开启轮询器】
②、通过Message.obtain()准备消息数据(实际是从消息池中取出的消息)
③、创建Handler对象,在其构造函数中,获取到Looper对象、MessageQueue对象(从Looper中获取的),并将handler作为message的标签设置到msg.target上
1、发送消息:sendMessage():通过Handler将消息发送给消息队列
2、给Message贴上handler的标签:在发送消息的时候,为handler发送的message贴上当前handler的标签
3、开启HandlerThread线程,执行run方法。
4、在HandlerThread类的run方法中开启轮询器进行轮询:调用Looper.loop()方法进行轮询消息队列的消息
【Tips:这两步需要再斟酌,个人认为这个类是自己手动创建的一个线程类,Looper的开启在上面已经详细说明了,这里是说自己手动创建线程(HandlerThread)的时候,才会在这个线程中进行Looper的轮询的】
5、在消息队列MessageQueue中enqueueMessage(Message msg, long when)方法里,对消息进行入列,即依据传入的时间进行消息入列(排队)
6、轮询消息:与此同时,Looper在不断的轮询消息队列
7、在Looper.loop()方法中,获取到MessageQueue对象后,从中取出消息(Message msg = queue.next())
8、分发消息:从消息队列中取出消息后,调用msg.target.dispatchMessage(msg);进行分发消息
9、将处理好的消息分发给指定的handler处理,即调用了handler的dispatchMessage(msg)方法进行分发消息。
10、在创建handler时,复写的handleMessage方法中进行消息的处理
11、回收消息:在消息使用完毕后,在Looper.loop()方法中调用msg.recycle(),将消息进行回收,即将消息的所有字段恢复为初始状态
7,application与activity谁的生命周期长?为啥?
application和Activity,Service一样,是android框架的一个系统组件,当android程序启动时系统会创建1个 application对象,用来存储系统的一些信息。通常我们是不需要指定1个Application的,这时候系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建1个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字引入便可)。
android系统会为每一个程序运行时创建1个Application类的对象且仅创建1个,所以Application可以说是单例 (singleton)模式的1个类.且application对象的生命周期是全部程序中最长的,它的生命周期就等于这个程序的生命周期。由于它是全局 的单例的,所有的Activity和Service都是共用着一个Application,所以Application通常用来共享数据,数据传递和数据缓存。
8,android的系统进程分哪5类?
在Android中进程按优先级可以分为五类,优先级从高到低排列:
- 前台进程 该进程包含正在与用户进行交互的界面组件,比如一个Activity
- 可视进程 该进程中的组件虽然没有和用户交互,但是仍然可以被看到
- 服务进程 该进程包含在执行后台操作的服务组件,比如播放音乐的进程
- 后台进程 该进程包含的组件没有与用户交互,用户也看不到
- 空进程   没有任何界面组件、服务组件,或触发器组件
9,android的四个组件以及他们的分别作用。
Activity:
Activity是Android程序与用户交互的窗口,是Android构造块中最基本的一种,它需要为保持各界面的状态,做很多持久化的事情,妥善管理生命周期以及一些跳转逻辑。
Activity之间通过Intent进行通信。在Intent的描述结构中,有两个最重要的部分:动作action和动作对应的数据data,还有一个类型category。
Activity的激活通过传递一个Intent 对象至Context.startActivity()或Activity.startActivityForResult()以载入(或指定新工作给)一个activity。相应的activity 可以通过调用getIntent() 方法来查看激活它的intent。如果它期望它所启动的那个activity 返回一个结果,它会以调用startActivityForResult()来取代startActivity()。比如说,如果它启动了另外一个Activity 以使用户挑选一张照片,它也许想知道哪张照片被选中了。结果将会被封装在一个Intent 对象中,并传递给发出调用的activity 的onActivityResult() 方法。
service:
后台服务于Activity,封装有一个完整的功能逻辑实现,接受上层指令,完成相关的事物,定义好需要接受的Intent提供同步和异步的接口。
服务不能自己运行,需要通过Context.startService()或Context.bindService()启动服务。
通过startService()方法启动的服务与调用者没有关系,即使调用者关闭了,服务仍然运行想停止服务要调用Context.stopService(),此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onStartCommond(),如果服务已经启动再次调用只会触发onStart()Commod()方法,销毁时调用onDestroy();方法
使用bindService()启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onBind(),如果服务已经启动再次调用不会再触发这2个方法,调用者退出时系统会调用服务的onUnbind()-->onDestory(),想主动解除绑定可使用Contex.unbindService(),系统依次调用onUnbind()-->onDestory();
BroadCast Receiver:
接受一种或者多种Intent作触发事件,接受相关消息,做一些简单处理,转换成一条Notification,统一了Android的事件广播模型。例如:电量改变,开机,收发短信,屏幕解锁。
两种广播类型:
无序广播,通过Context.sendBroadcast(Intent myIntent)发送的。(新闻联播)
有序广播,通过Context.sendOrderedBroadcast(intent, receiverPermission)发送的,该方法第2个参数决定该广播的级别,级别数值是在-1000 到1000 之间, 值越大 , 发送的优先级越高;广播接收者接收广播时的级别(可通过intentfilter中的priority进行设置设为1000时优先级最高),同级别接收的先后是随机的, 再到级别低的收到广播,高级别的或同级别先接收到广播的可以通过abortBroadcast()方法截断广播使其他的接收者无法收到该广播。
注意:使用sendOrderedBroadcast(intent, null,new FinalReceiver(), null, 1, "习大大给每一个村民发了1000斤大米", null);中的第三个参数是一个最终/最后的广播接受者,并且它不用在清单文件中注册。好比钦差大臣。
监听广播步骤:
1, 写一个继承BroadCastReceiver的类,重写onReceive()方法,广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态。
2, 注册该广播接收者,注册有两种方法:代码动态注册和AndroidManifest文件中进行静态注册
静态注册:静态注册,注册的广播,下面的priority表示接收广播的级别"1000"为最高优先级
<receiverandroid:name=".SMSBroadcastReceiver" >  <intent-filterandroid:priority= "1000" >    <actionandroid:name="android.provider.Telephony.SMS_RECEIVED" /></intent-filter>
</receiver>
动态注册:动态注册,一般在Activity可交互时onResume()内注册BroadcastReceiver或者在activity创建时onCreate()方法中注册
IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");registerReceiver(mBatteryInfoReceiver ,intentFilter);//在onDestroy()方法中,反注册,否则会产生漏气的现象unregisterReceiver(receiver);
注意事项:
1.生命周期只有十秒左右,如果在 onReceive()内做超过十秒内的事情,就会报ANR(Application No Response)程序无响应的错误信息,如果需要完成一项比较耗时的工作 ,应该通过发送 Intent给 Service,由Service来完成 . 这里不能使用子线程来解决 ,因为 BroadcastReceiver的生命周期很短 ,子线程可能还没有结束,BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束, 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死, 因为它属于空进程( 没有任何活动组件的进程). 如果它的宿主进程被杀死, 那么正在工作的子线程也会被杀死. 所以采用子线程来解决是不可靠的
2. 动态注册广播接收器还有一个特点,就是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器就是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用
系统常见广播Intent,如开机启动、电池电量变化、时间改变等广播
Content Provider:
是Android提供的第三方应用数据的访问方案,可以派生Content Provider类,对外提供数据,可以像数据库一样进行选择排序,屏蔽内部数据的存储细节,向外提供统一的接口模型,大大简化上层应用,对数据的整合提供了更方便的途径。
1)ContentProvider:内容提供者
把一个应用程序的私有数据(如数据库)信息暴露给别的应用程序,让别的应用程序可以访问;在数据库中有对应的增删改查(crud)的方法,如果要让别的应用程序访问,需要有一个路径uri:
通过content:// 路径对外暴露,uri写法:content://主机名/表名
2)ContentResolver:内容解析者
根据内容提供者的路径uri,对数据进行操作(crud);
3)ContentObserver:内容观察者
可以理解成android系统包装好的回调,数据发生变化时,会执行回调中的方法;
ContentResolver发送通知,ContentObserver监听通知;
当A的数据发生变化的时候,A就会显示的通知一个内容观察者,然后通过消息把这个变化的uri,提供给内容解析者,进行对数据的操作。
 
四大组件一些总结:
内容提供者的激活:当接收到ContentResolver 发出的请求后,内容提供者被激活。而其它三种组件──activity、服务和广播接收器被一种叫做intent的异步消息所激活。所以,没有必要去显式的关闭这些组件。
Activity关闭:可以通过调用它的finish()方法来关闭一个activity,
关闭多个activity的解决方法:1,使用list保存activity实例,然后再一一干掉。简而言之,通过单例模式把每个Activity的引用添加到一个全局链表中,每次退出程序先调用链表中Activity的finish方法,再调用System.exit(0),确保一一干掉activity。代码如下:
public class SysApplicationextends Application {
private List<Activity> mList = new LinkedList<Activity>();
private static SysApplication instance;private SysApplication() {}public synchronized static SysApplication getInstance() {  
      if (null == instance) {         instance =new SysApplication();      }       
           return instance;      }// add Activity
public void addActivity(Activity activity) {      mList.add(activity);} public void exit() {      
        try {          
for (Activity activity : mList) {            
 if (activity != null)                  activity.finish();                  }         } catch (Exception e) {                 e.printStackTrace();         } finally {                System.exit(0);         }} public void onLowMemory() {  
      super.onLowMemory();      System.gc();} }在每个Activity的onCreate方法中添加类似代码:[java]public void onCreate(Bundle savedInstanceState) {             super.onCreate(savedInstanceState);                SysApplication.getInstance().addActivity(this);            }       在需要退出程序的时候,调用:
在activity的onDestroy()方法中调用以下代码:SysApplication.getInstance().exit();
 
第二种方式:2,使用广播关闭多个activity。
定义一个基类activity:
public class BaseActivity extends Activity { 
    protected BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { 
        @Override 
        public void onReceive(Context context, Intent intent) { 
            finish(); 
        } 
    }; 
     
    @Override 
    public void onResume() { 
        super.onResume(); 
        // 在当前的activity中注册广播  ,添加广播的过滤器
        IntentFilter filter = new IntentFilter(); 
        filter.addAction("ExitApp");  //接收到自定义的广播,在广播中finish掉activity
        this.registerReceiver(this.broadcastReceiver, filter); 
    } 
     
    @Override 
    protected void onDestroy() { 
        // TODO Auto-generated method stub 
        super.onDestroy(); 
        this.unregisterReceiver(this.broadcastReceiver);    //反注册
    } 

在你要关闭的Activity里添加myExit()方法,然后在要进行退出程序操作的地方调用myExit()方法就行。
public class Activity1 extends BaseActivity { 
    private Button btn1;
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        // TODO Auto-generated method stub 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.a1); 
         
        btn1 = (Button)findViewById(R.id.btn1); 
        btn1.setOnClickListener(new View.OnClickListener() { 
             
            @Override 
            public void onClick(View v) { 
                Intent i = new Intent(Activity1.this, Activity2.class); 
                startActivity(i); 
            } 
        }); 
    } 
    /**
     * 捕获手机物理菜单键
     */ 
    private long exitTime = 0; 
 
    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 
        if(keyCode == KeyEvent.KEYCODE_BACK){//&& event.getAction() == KeyEvent.ACTION_DOWN 
            if((System.currentTimeMillis()-exitTime) > 2000){ 
                Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show(); 
                exitTime = System.currentTimeMillis(); 
            } else { 
                myExit(); 
            } 
            return true; 
        } 
        return super.onKeyDown(keyCode, event); 
    } 
     
    protected void myExit() { 
        Intent intent = new Intent(); 
        intent.setAction("ExitApp"); 
        this.sendBroadcast(intent);  //发送自定义的广播
        super.finish(); 
    } 
 
服务Service关闭:对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务,使用bindService()方法启动的服务要调用Contex.unbindService ()方法关闭服务
10,activity的生命周期与fragment的生命周期
Activity的生命周期:
onCreate()  onStart()  onResume()  onPause()  onStop()  onDestroy()   onRestart()
Fragment的生命周期:
1,onAttach():当fragment与activity绑定时调用(在这个方法中可以获得所在的activity)。
2,onCreate():当创建 fragment 时系统调用此方法
3,onCreateView():当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面),初始化界面。
4,onActivityCreated():当activity的onCreated()方法返回后调用此方法,初始化数据。
5,onStart():开启Fragment
6,onResume():Fragment在一个运行中的activity中并且可见。
7,onPause():另一个activity处于最顶层,但是fragment所在的activity并没有被完全覆盖(顶层的activity是半透明的或不占据整个屏幕)。
8,onStop():Fragment不可见。可能是它所在的activity处于stoped状态或是fragment被删除并添加到后退栈中了。此状态的fragment仍然存在于内存中。
9,onDestroyView():当fragment的layout被销毁时被调用。
10,onDestroy():fragment被销毁时调用。
11,onDetach():fragment与activity解除绑定时被调用。
11,activity与fragment传递数据,  1 ,Activity向Fragment传递数据    fragment.setArguments(bundle),那么Fragment通过getArguments();获得从activity中传递过来的值  2 ,Fragment向Activity传递数据:有两种方法   第一种,在Fragment内部定义一个回调接口.让包含该Fragment的Activity实现该接口,这样Fragment就可调用该回调方法, 将数据传给Activity。
第二种,Fragment中通过getActivity(),然后进行强制转化,调用Activity中的公有方法,(XXXXActivity)getActivity()).fun();注意使用这种方法,在使用之前,需要使用instanceof判断一下Activity的类型。
12,android当中的事件传递的机制。
在Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了Android中的事件响应。总的来说,所有的事件都由如下三个部分作为基础:
按下(ACTION_DOWN)
移动(ACTION_MOVE)
抬起(ACTION_UP)
我们知道,所有的事件操作都发生在触摸屏上,而在屏幕上与我们交互的就是各种各样的视图组件(View),在Android中,所有的视图都继承于View,另外通过各种布局组件(ViewGroup)来对View进行布局,ViewGroup也继承于View。所有的UI控件例如Button、TextView都是继承于View,而所有的布局控件例如RelativeLayout、容器控件例如ListView都是继承于ViewGroup。所以,我们的事件操作主要就是发生在View和ViewGroup之间。
在View和ViewGroup中都存在dispatchTouchEvent和onTouchEvent方法,但是在ViewGroup中还有一个onInterceptTouchEvent方法。
总结:
1,Android中事件传递按照从上到下进行层级传递,事件处理从Activity开始到ViewGroup再到View。
2,事件传递方法包括dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent,其中前两个是View和ViewGroup都有的,最后一个是只有ViewGroup才有的方法。这三个方法的作用分别是负责事件分发、事件处理、事件拦截。
3,onTouch事件要先于onClick事件执行,onTouch在事件分发方法dispatchTouchEvent中调用,而onClick在事件处理方法onTouchEvent中被调用,onTouchEvent要后于dispatchTouchEvent方法的调用。
13,jni的调用过程以及ndk的使用。(两个常用参数JNIEnv *  env,  jobject  obj)
JNI流程:
1、创建一个Android工程
2、在Java代码中声明一个本地方法,用javah工具生成头文件
3、在该Android工程中创建jni目录,引入头文件,根据头文件实现c代码,参考NDK工具中的jni.h文件,其中的结构体JNIEnv*对象和jobject对象很重要,在C代码中的方法必须传入这两个对象
4、在jni目录中创建并编写Android.mk文件
5、Ndk交叉编译生成动态库,即生成手机可执行的二进制文件(.so文件)
6、Java代码load 动态库.调用native代码:System.loadLibrary("Hello");//Hello为jni目录C/C++代码的.c文件
 
NDK(native develop kits):
a为什么需要NDK工具:
在安卓开发中,因为底层都是C,且C程序不需要虚拟机编译底层就能读懂,故C效率比java快,故java需要调用C中代码。而java不能直接调用C中代码,而是调用C代码的二进制文件,C代码里封装了C中丰富的函数。
Java想要调用C代码的可执行的二进制文件需经过以下两步骤:
编译:
将C源代码编译成不可执行的二进制文件;
该二进制文件在windows系统下是.obj文件
在Linux系统下是.o文件
链接:
将编译好的二进制文件根据当前的操作系统系统链接成可执行的二进制文件,在链接过程中加入了必需的函数库
该二进制文件在windows系统下是.exe文件
在linux系统下是.so文件
b、交叉编译:
Google提供了用C编写的NDK工具,该工具的ndk-build可实现在电脑上,编译出可在手机(linux操作系统,arm的CPU)上执行的二进制文件。此过程就是交叉编译过程。
NDK工具需要运行在linux系统环境下,而cygwin是windows系统下模拟linux系统的一个工具,故安装NDK之前需安装cygwin软件。
14,xml与json解析数据,当json数据在android端出现问题,如何解决?
1)XML的解析和生成:
解析XML的概述:
常见的XML解析方式有三种,DOM、SAX和Pull
Android SDK中已经集成了Pull解析器,无需添加任何jar文件,故Android系统中推荐使用Pull解析XML
Pull解析器运行方式与SAX类似,基于事件驱动来解析,边读边解析。
①为什么要解析XML:
解析XML就是把XML文件里面各标签的属性、文本解析出来封装到javabean中,解析XML的关键就是如何拿到XML文件里面的元素的属性和文本
②解析XML的步骤:
a、获得解析器:XmlPullParser parser = Xml.newPullParser();
b、设置输入流以及编码,指定解析的是哪个输入流里面的XML文件
//去掉getClassLoader()则文件名前面加”/”,否则报错
InputStream in = PersonServiceTest.class.getClassLoader().getResourceAsStream("persons.xml");
parser.setInput(in, "UTF-8");
c、通过解析器XmlPullParser定义的常量方法来解析XML文件
XmlPullParser事件类型常量:START_DOCUMENT、END_DOCUMENT 、START_TAG 、END_TAG 、TEXT
方法:
getEventType():获取事件的类型
next():往下解析
getName():获取标签的名字
getAttributeValue(int index):根据索引获取标签的属性,索引从0开始
getText():获取文本内容
nextText():获取下一个文本内容
③为什么要生成XML?
将javabean对象写入XML文件保存起来,所以生成XML文件的关键就是如何把javabean对象的属性数据生成XML文件的元素标签保存起来
④使用XmlSerializer生成XML文件的步骤:
a、获取序列化对象:
XmlSerializer serializer = Xml.newSerializer();
b、设置序列化对象的输出流和编码,指定生成到哪个输出流里面的XML文件
FileOutputStream fos = new FileOutputStream("/mnt/sdcard/persons.xml");
serializer.setOutput(out,”UTF-8”);
c、通过序列化的各种方法将对象生成XML文件的各种元素标签属性
startDocument:开始XML文档
startTag:开始标签
attribute:设置标签的属性
text:设置标签的文本
endTag:结束标签
endDocument:结束XML文档
2)JSON解析:
JSON文件就相当于是一个数组
解析JSON文件就相当于遍历存储JSON对象的数组JSONArray,获取每个JSONObject的数据,并封装到javabean中
①JSON的优缺点:
优点:
作为数据传输格式,跟XML类似,但是比XML更加轻巧
由于JSON是JavaScript的原生格式,所以JSON不需要包含特定内容的首部信息
缺点:
语法过于严谨
代码可读性差
其eval函数存在风险
 
15,数据库重点
1)SQLite:
①SQLite概述:
Android平台中嵌入了一个关系型数据库SQLite,和其他数据库不同的是SQLite存储数据时不区分类型。SQLite是手机自带的数据库,每一个数据库就是一个XML文件,每一个XML文件有一张或多张表
②创建SQLite数据库步骤:
a、定义类继承SQLiteOpenHelper
SQLiteOpenHelper是用来打开数据库的一个工具,其中有创建onCreate()数据库和升级upGrade()数据库方法,
在获取数据库连接时,这些方法会自动执行,使用手机自带的SQLite数据库必须创建一个SQLiteOpenHelper子类,
实现其onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int)方法,
如果数据库不存在则创建,存在则检查数据库版本是不是最新,不是最新则升级数据库版本,维护其保持一个最佳的状态
b、获取数据库SQLiteDatabase对象
SQLiteOpenHelper. getWritableDatabase():获取写数据库,当可写时可获取读数据库
SQLiteOpenHelper. getReadableDatabase():获取读数据库
c、执行数据库增删改查操作
执行增删改操作,无返回结果集:
SQLiteDatabase.execSQL(String sql,Object[] params);
执行查询操作:返回Cursor结果集
SQLiteDatabase.rawQuery(String sql,String[] params);
或者执行CRUD操作:
SQLiteDatabase.insert();
SQLiteDatabase.update();
SQLiteDatabase.delete();
SQLiteDatabase.query();
d、关闭资源:(可选操作,每次获取数据库的时候数据库底层会自动关流,建议手动关闭)
SQLiteDatabase.close();
Cursor.close();
16,aidl进程间的通信机制。
1)什么是aidl?
aidl就是Android接口定义语言,即Android Interface Definition Language的缩写,是Google提供的一个规范。
2)aidl技术的作用:
(可以先阐述服务的两种开启方式再开始回答这个问题)
当服务调用者和服务不在同一个应用,此时服务调用者想通过远程绑定服务调用服务里面的方法就必须得通过aidl技术。
3)使用aidl技术的步骤:
①在服务中定义好方法
通过一个中间人来实现调用服务里面的方法,这个中间人就是接口。
而通过aidl技术可将服务应用中的接口和调用者应用中的接口变成同一个接口
②定义一个接口,并在接口中声明方法
③通过绑定服务的方法开启的服务会调用服务的onBind方法,而执行onBind方法会返回一个IBinder接口对象,
  而Binder是IBinder接口的实现类,故我们可以自定义一个内部类MyBinder继承Binder同时实现步骤②中定义好的接口,实现接口未实现的方法。
  此时MyBinder就相当于一个中间人,通过调用这个中间人的方法我们就可以间接的调用服务中的方法
④进入调用服务的应用的本地目录,将接口文件的后缀名改为aidl,同时将接口中的权限修饰符去掉(此时会在英语的gen目录生成相对应的.java文件);
  这样就保证两个应用中相同包相同声明的接口是同一个接口,进而可以通过该中间人接口远程调用服务里面的方法。
⑤将服务中自定义的内部类MyBinder改为继承Stub;
  将远程绑定服务调用服务方法的代码中ServiceConnection类的onServiceConnected方法返回的IBinder接口,
  通过Stub.asInterface(IBinder)方法强转成我们自定义的中间人接口(只有强转为和服务中自定义的接口类型才能调用服务中的方法)
 
或者以下5步操作:
1,把远程服务的方法抽取成一个单独的java接口文件
2,把java接口文件的后缀名改为aidl,去掉public(此时会在英语的gen目录生成相对应的PublicBusiness.java文件)
3,在自动生成的PublicBusiness.java文件中,有一个静态抽象类stub,它继承了binder类,实现了publicBusiness接口,这个类就是新的中间人。
4,把aidl文件复制粘贴到目标项目,注意aidl文件所在包名必须跟原项目中aidl所在包名一致
5,在目标项目中,强转中间人对象时,直接使用Stub.asInterface();
17android中动画
动画的分类:
安卓中的动画分为三种,
一种是帧动画(Frame),像电影一样一帧一帧的播放。
一种是补间动画(Tweened):又分为四种动画类型,Alpha(透明)、Scale(缩放)、Translate(平移)和Rotate(旋转)
一种是属性动画:使用开源框架nineOldAndroids 
主要有两个类:ValueAnimator  和ViewPropertyAnimator
(valueAnimator不改变动画的值,仅仅把值传递给相应的控件)
nineOldAndroids:里面封装了属性动画
最大特点是:改变了View的位置
 
rotationBy:水平旋转
translationXBy:平移动画
alphaBy.透明动画
scaleBy缩放动画
 
setInterpolator();速度插值器,
overShootInterpolator()弹性的插值器
BoundInterpolator()球落地的插值器
cycleInterpolator(4)左右两边斗的插值器
 
使用开源项目PhotoView,来实现图片手势放大缩放效果,
18andorid的mvc设计模式:
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。其中M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层.
 1) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在该层的。就是应用程序中二进制的数据。
2) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。当然,如果你对Android了解的比较的多了话,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进行Java和JavaScript之间的通信,幸运的是,Android提供了它们之间非常方便的通信实现,phoneGap框架。    
 3) 控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。
 
19设计模式:(总计23中)
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
1. public class Singleton { 
2.     private Singleton() {} 
3.     private static Singleton singleton=null; 
1. public static Singleton getInstance() { 
2.         if (singleton == null) {   
3.             synchronized (Singleton.class) {  // 如果不加synchronized会导致对线程的访问不安全 ,双重锁定检查
4.                if (singleton == null) {   
5.                   singleton = new Singleton();  
6.                }   
7.             }   
8.         }   
9.         return singleton;  
10.     }
 
工厂设计模式:
1. public class ProductCreator {  
2.  
3.     public Product createProduct(String type)   throws Exception{  
4.         if (" A ".equals(type)) {  
5.             return new ProductA();  
6.         }  
7.         if (" B ".equals(type)) {  
8.             return new ProductB();  
9.         } else 
10.             return null;  
11.     }  
12.  
13.     public static void main(String[] args) {  
14.         ProductCreator creator = new ProductCreator();  
15.         creator.createProduct(" A ").getName();  
16.         creator.createProduct(" B ").getName();  
17.     }  
18. }
冒泡排序:从大到小排序:
1.   int arr[] = {67, 69, 75, 87, 89, 90, 9, 10}; 
2.           for (int x = 0; x < arr.length -1; x++){    //最多做n-1趟排序 
3.               for(int y = 0 ;y< arr.length - x - 1; y++){    //对当前无序区间arr[0......length-x-1]进行排序(y的范围很关键,这个范围是在逐步缩小的) 
4.                   if(arr[y] < arr[y + 1]){    //把小的值交换到后面 
5.                       int temp = arr[y]; 
6.                       arr[y] = arr[y + 1]; 
7.                       arr[y + 1] = temp; 
8.                   } 
9.               }
20、android中的四种点击事件的写法。
1、点击事件的第一种写法 . 写一个内部类.实现点击事件的接口
bt.setOnClickListener(new MyButtonListener());
2、点击事件的第二种写法,匿名内部类
bt.setOnClickListener(new OnClickListener() {
@Override public void onClick(View v) {
callPhone();
} });
  3、点击事件的第三种写法:this-让这个activity实现事件接口,一般测试时候用,正式上线的时候不用。 class TestActivity implements OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
4、点击事件的第四种写法 在xml文件里面定义 button点击的时候 调用什么方法 布局xml配置: android:onClick="dailPhone" 然后在当前Activity中实现该事件的接口,再写一个dailPhone方法, 该方法名字与xml中配置的名字一样,且必须是public,而且要一个View对象, 因为底层的实现是反射且没有getDeclaredMethod()
public void dailPhone(View view){//这里必须穿参数view
callPhone();
}
21回调接口的写法步骤
1,定义一个接口
2,在接口里面定义一个未实现的方法(未知的业务逻辑处理)
3,  实现了当前接口的类的对象传递进来
4,通过对象调用已经实现的业务逻辑方法
22Android下的国际化理解
通常国际化我们只要在res/value文件重新定义values-国家编号,如values-zh-rCN简体汉语,values-zh-rTW繁体,values-jp日语
23背景选择器和颜色的写法
Selector的结构描述:
1.android:state_pressed="true/false"
true:表示按下状态下使用,false:表示非按下状态下使用。
2.android:state_focused="true/false"
ture:表示聚焦状态使用(例使用滚动球/D-pad聚焦Button),false:表示非聚集状态下使用。
3.android:state_selected="true/false"
true:表示被选中状态下使用,false:表示非选中下使用
4.android:state_active="true/false"
true:表示可勾选状态时使用,false:表示不可勾选状态下使用
5. android:state_checkable="true/false"
true:表示勾选状态下使用,false:表示非勾选状态使用
6.android:state_checked="true/false"
true:表示勾选状态下使用,false:表示非勾选状态使用
7. android:state_enabled="true/false"
true:表示可用状态使用(能接收触摸/点击事件),false:表示不可用状态使用
8. android:state_window_focused="true/false"
true:表示应用程序窗口有焦点时使用(应用程序在前台),false:表示无焦点时使用
9.android:background
设置背景图片
24富文本
SpannableString
SpannableString spanableInfo = new SpannableString( 
                "This is a test, Click Me"); 
        int start = 16; 
        int end = spanableInfo.length(); 
        spanableInfo.setSpan(new Clickable(l), start, end, 
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
25网络加载框架,网络上传与下载文件?
1. 继承系统Application的AppContext对象, 在应用被创建时候调用, 把AsyncHttpClient(异步加载网络三方库)设置到ApiHttpClient对象中.
2. ApiHttpClient中初始化了一些请求头信息, 如"Accept-Language""Host",以及请求的URL地址开头:String API_URL = "http://www.oschina.net/%s";
给其他类提供了get, post等静态方法异步加载网络.
3. 各个界面通过OSChinaApi对象, 把请求的路径和请求参数准备好,继而间接调用ApiHttpClient执行真正的网络加载.
26Android中强引用、软引用、弱引用、虚引用的理解。
把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。
 
1、强引用
   平时我们编程的时候例如:Object object=new Object();那object就是一个强引用了。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
 
2、软引用(SoftReference)
   如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只 要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 软引用可以和一个引用队列(ReferenceQueue)联 合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
3、弱引用(WeakReference)  
   如果一个对象只具有弱引用,那就类似于可有可物的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。  弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回 收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
 
 4、虚引用(PhantomReference)  
   "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在 任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队 列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
27Intent与IntentFilter的区别(intent-filter),还有IntentReceiver,还有一个IntentService
Intent代表了Android应用的启动“意图”,Android应用将会根据Intent来启动指定组件,至于到底启动哪个组件,则取决于Intent的各个属性。Intent是由Component、Action、Data、Category、Extra及Flag六部分组成的。
IntentFilter实际上相当于Intent的过滤器,一个应用程序开发完成后,需要告诉Android系统自己能够处理哪些隐形的Intent请求,这就需要声明IntentFilter。IntentFilter过滤Intent时,一般是通过Action、Data及Category三方面进行监测的。
<intent-filter></intent-filter>这是清单文件中标签对。也是程序的入口处。隐式启动Activity的intent到底发给哪个activity,需要进行三个匹配,一个是action,一个是category,一个是data,可以是全部或部分匹配。同样适用于Service和BroadcastReceiver。
IntentReceiver:当你希望你的应用能够对一个外部的事件(如当电话呼入时,或者数据网络可用时,或者到了晚上时)做出响应,你可以使用一个IntentReceiver。IntentReceiver 在AndroidManifest.xml 中注册,但也可以在代码中使用Context.registerReceiver()进行注册。
IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。而且,所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
IntentService有什么好处呢?
首先,我们省去了在Service中手动开线程的麻烦,
第二,当操作完成时,我们不用手动停止Service。
28Android下socket网络编程技术
1)网络编程概述:
①网络模型:
****OSI模型
应用层
表示层
会话层
传输层
网络层
数据连接层
物理层
****TCP/IP模型
应用层
传输层
网际层
主机至网络层
②网络通讯要素
IP地址
端口号
传输协议
③网络通讯前提:
**找到对方IP
**数据要发送到指定端口。为了标示不同的应用程序,所以给这些网络应用程序都用数字进行标示,这个标示就叫端口。
定义通信规则。这个规则称为通信协议,国际组织定义了通用协议TCP/IP
1)Android网络应用的概述:
Android完全支持JDK本身的TCP、UDP网络通信API,可以使用Socket、ServerSocket来建立基于TCP/IP协议的网络通信;
也可以使用DatagramSocket/Datagrampacket来建立基于UDP协议的网络通信。
同时Android还支持JDK提供的URL、URLConnection等网络通信的API。
2)TCP和UDP的区别:
①UDP协议:
面向无连接
每个数据报的大小在限制在64k内
因为是面向无连接,所以是不可靠协议
不需要建立连接,速度快
②TCP协议:
必须建立连接,形成传输数据的通道
在连接中可进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
注:三次握手:
第一次:我问你:在么?
第二次:你回答:在。
第三次:我反馈:哦,我知道你在。
3)Socket:
**Socket就是为网络服务提供的一种机制
**通信的两端都有Socket
**网络通信其实就是Socket间的通信
**数据在两个Socket间通过IO传输
**玩Socket主要就是记住流程,代码查文档就行
4)UDP(User Datagram Protocol):用户数据协议
①UDP概述:
需要DatagramSocket与DatagramPacket对象来实现UDP协议传输数据
UDP协议是一种面向无连接的协议。面向无连接的协议指的是正式通信前不必与对方先建立连接,不管对方连接状态就直接发送数据。
②UDP协议开发步骤:
**发送端:
建立DatagramSocket服务;
提供数据,并将数据封装到字节数组中;
创建DatagramPacket数据包,并把数据封装到包中,同时指定接收端IP和接收端口
通过Socket服务,利用send方法将数据包发送出去;
关闭DatagramSocket和DatagramPacket服务。
**接收端:
建立DatagramSocket服务,并监听一个端口;
定义一个字节数组和一个数据包,同时将数组封装进数据包;
通过DatagramPacket的receive方法,将接收的数据存入定义好的数据包;
通过DatagramPacke关闭的方法,获取发送数据包中的信息;
关闭DatagramSocket和DatagramPacket服务。
③UDP协议的Demo(必须掌握):
****发送端:
class UDPSend
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket();
byte[] buf = "这是UDP发送端".getBytes();
DatagramPacket dp = new DatagramPacket(
buf,buf.length, InetAddress.getByName("192.168.1.253"),   10000);
ds.send(dp);
ds.close();
}
}
****接收端
class UDPRece
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket(10000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);//将发送端发送的数据包接收到接收端的数据包中
String ip = dp.getAddress().getHosyAddress();//获取发送端的ip
String data = new String(dp.getData(),0,dp.getLength());//获取数据
int port = dp.getPort();//获取发送端的端口号
sop(ip+":"+data+":"+port);
ds.close();
}
}
注:协议的概念和作用:
什么是协议?
网络通信的一种规则
协议的作用?
保证通信两端实现网络通信
5)TCP/IP协议:Socket和ServerSocket
①基于TCP协议的网络通信概述:
TCP/IP通信协议是一种必须建立连接的可靠的网络通信协议。它在通信两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。
网络虚拟链路一旦建立,两端的程序就可以进行通信。
②TCP/IP协议开发步骤:
**客户端:
建立Socket服务,并指定要连接的主机和端口;
获取Socket流中的输出流OutputStream,将数据写入流中,通过网络发送给服务端;
获取Socket流中的输入流InputStream,获取服务端的反馈信息;
关闭资源。
**服务端:
建立ServerSocket服务,并监听一个端口;
通过ServerSocket服务的accept方法,获取Socket服务对象;
使用客户端对象的读取流获取客户端发送过来的数据;
通过客户端对象的写入流反馈信息给客户端;
关闭资源
③TCP/IP协议的一个Demo(必须要掌握!):
客户端:
class TCPClient
{
public static void main(String[] args)
{
Socket s = new Socket("192.168.1.253",10000);
OutputStream out = s.getOutputStream();
out.write("这是TCP发送的数据".getBytes());
s.close();
}
}
服务端:
class TCPServer
{
public static void main(String[] args)
{
ServerSocket ss = new ServerSocket(10000);
Socket s = ss.accept();
 
String ip = s.getInetAddress().getHostAddress();
sop(ip);
 
InputStream is = s.getInputStream();
byte[] buf = new byte[1024];
int len = is.read(buf);
sop(new String(buf,0,len));
s.close();
ss.close();
}
}
6)HTTP协议:
a、HTTP是Hyper Text Transfer Protocol的缩写
b、是由W3C制定和维护的。目前版本为1.0和1.1
c、是开发web的基石,非常地重要
d、1.0版本:是无状态的协议,即一次连接只响应一次请求,响应完了就关闭此次连接,要想再访问须重新建立连接。而连接都是比较耗资源的。
   1.1版本:是有状态的协议。即可以在一次网络连接基础上发出多次请求和得到多次的响应。当距离上次请求时间过长时,服务器会自动断掉连接,这就是超时机制。
①HTTP协议的组成:
请求部分:
请求行:
GET / HTTP/1.1  包含:请求方式GET请求的资源路径:/协议版本号:HTTP/1.1
请求方式。常用的有GET、POST
GET方式:默认方式。直接输入的网址。
表单数据出现在请求行中。url?username=abc&password=123
特点:不安全;有长度限制:<1k
POST方式:可以通过表单form method="post"设置
表单数据会出现在正文中。
特点:安全;没有长度限制
请求消息头:
请求正文:第一个空行之后的全部都是请求正文
响应部分:
响应行:
HTTP/1.1 200 OK    包含:协议版本号:HTTP/1.1响应码:200描述:OK
响应码:(实际用到的30个左右,其他都是W3C保留的)
描述:对响应码的描述
常用响应码:
200:一切正常
302/307:请求的资源路径变更了
304:资源没有被修改过
404:资源不存在,找不到资源
500:服务器程序有错
响应消息头:
响应正文:
第一个空行之后的全部都是响应正文,浏览器显示的就是正文中的内容
29Android下线程池
ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
corePoolSize: 核心线程数,能够同时执行的任务数量
maximumPoolSize:除去缓冲队列中等待的任务,最大能容纳的任务数(其实是包括了核心线程池数量)
keepAliveTime:超出workQueue的等待任务的存活时间
unit:时间单位
workQueue:阻塞等待线程的队列,一般使用new LinkedBlockingQueue<Runnable>()这个,如果不指定容量, 会一直往里边添加,没有限制,workQueue永远不会满;
threadFactory:创建线程的工厂,使用系统默认的类
handler:当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加
 
执行流程:当线程数小于corePoolSize时,每添加一个任务,则立即开启线程执行
          当corePoolSize满的时候,后面添加的任务将放入缓冲队列workQueue等待;
  当workQueue也满的时候,看是否超过maximumPoolSize线程数,如果超过,默认拒绝执行
举例说明:
假如:corePoolSize=2,maximumPoolSize=3,workQueue容量为8(最大3+8个任务)
  最开始,执行的任务A,B,(2个)此时corePoolSize已用完,再次执行任务C(1个),则
C将被放入缓冲队列workQueue中等待着,如果后来又添加了7个任务,此时workQueue已满(2+1+7),
  则后面再来的任务将会和maximumPoolSize比较,由于maximumPoolSize为3,所以只能容纳1个了,
  因为有2个在corePoolSize中运行了,所以后面来的任务默认都会被拒绝。
30Android当中如何去除标题(3种方法)
00001. 第一种:
在setContentView()方法的前面插入代码:?requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏
第二种:在AndroidManifest.xml文件中定义去掉整个应用的标题栏 <android:theme="@android:style/Theme.NoTitleBar">不过有时候不需要去掉整个程序的应用,只想去掉一个的时候就在Activity中。
第三种方法:在res/values目录下面新建一个style.xml的文件这种方法是有经验的开发者最喜欢的方法,因为它把功能分开的;对于后期的维护非常方便。 <item name="android:windowNoTitle">true</item>    定义完了一个style,接下来就是在AndroidManifest.xml中使用了:android:theme="@style/concealTitle">
31Android的屏幕适配方案(5种方法)
适配方式一:图片适配
不同像素密度的手机加载工程资源文件(res)中不同资源图片
适配方式二:dimens.xml文件适配
dimens.xml存在于工程资源(res)文件夹中不同values(如:value-1280x720、value-800x480)文件夹下,可用于指定控件大小,不同像素密度手机加载不同values文件夹下的dimens.xml文件
适配方式三:布局文件适配(工程比较浩大,不常用)
不同分辨率的手机,加载不同的布局文件已达到适配效果。创建多个layout(如:layout-1280x720、layout-800x480)文件夹用于存放不同像素密度手机所需布局文件。
适配方式四:java代码适配
通过android相应api获取当前手机的宽高像素值,按比例分配屏幕中控件的宽高以达到适配效果
适配方式五:权重适配
通过android提供的(权重)剩余空间分配,已达到适配效果
屏幕适配原则:
①开发时单位尽量采用dip或者dp单位
②定义布局时尽量采用相对布局或者线性布局或者帧布局
③当屏幕过小或者内容过多时采用ScrollView控件将整个布局文件进行包裹
④点9图片
⑤在AndroidManifest.xml文件的<manifest>元素如下添加子元素
<supports-screens
android:largeScreens="true"
android:normalScreens="true" android:anyDensity="true"
android:smallScreens="true">
</supports-screens>
 
32Android下的第三方登录,分享,推送(极光和友盟),二维码扫描
分享:(使用shareSdk)
第一步:去shareSDK官网注册,拿到appkey
第二步:去要分享的平台(如新浪微博),注册拿到appkey和appSecret,写地址RedirectUrl
sortID=“”排序        Enable=”false”默认不可用
第三步:assets目录下 写shareSdk.xml文件
第四步:加文档上的权限
第五步:加文档中activity
第六步:写分享的具体代码(文档中有)
第三方登录:
使用Oauth2.0认证
总体流程:
第一步:得到授权码code
第二步:获取access token(令牌)
第三步:通过access token获取OpenId
第四步:通过access token和OpenId调用相关API,获取用户授权相关信息
 
 
 
极光推送:1,注册2,加权限3,加broadcastReceiver
二维码扫描技术:ZXing(看文档会使用就好)。(原理都是字符串)
33Android下的四种启动模式
1. standard   默认标准的启动模式, 每次startActivity都是创建一个新的activity的实例。
              适用于绝大多数情况。
2. singleTop  单一顶部,如果要开启的activity在任务栈的顶部已经存在,就不会创建新的实例,而是调用 onNewIntent() 方法。
              应用场景: 浏览器书签。 避免栈顶的activity被重复的创建,解决用户体验问题。
3. singleTask 单一任务栈 , activity只会在任务栈里面存在一个实例。如果要激活的activity,在任务栈里面已经存在,就不会创建新的activity,而是复用这个已经存在的activity,
 用 onNewIntent() 方法,并且清空当前activity任务栈上面所有的activity
              应用场景:浏览器activity, 整个任务栈只有一个实例,节约内存和cpu的目的
              注意: activity还是运行在当前应用程序的任务栈里面的。不会创建新的任务栈。
4. singleInstance  单一 实例模式
              单一实例,整个手机操作系统里面只有一个实例存在。他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。
              应用场景:呼叫来电界面 InCallScreen
34横竖屏切换时,activity的生命周期以及如何设置横屏或者竖屏。
如果不设置生命周期的过程时:onpause--onStop--onDestroy--onCreate--onStart--onResume
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
4,android:configChanges=”orientation|screenSize|keyboardHidden”
写死:android:screenOrientation=”landscape”横屏或者portrait竖屏
代码设置:setRequestOrientation(ActivityInfo.screen_orientation_landscape|portrait);
35Android下主线程与子线程的理解
每一个进程有一个主线程(Main Thread),它的主要任务是处理与UI有关的事件(Event),所以又常称为UI线程。主线程可以诞生子线程(Child Thread),而且需要诞生子线程。其主要原因是:主线程(即UI线程)必须时时刻刻(标准为5秒内)去照顾UI的事件,以便快速响应使用者的要求。因此,UI线程没空闲去执行较为费时的函数(或工作),只好诞生子线程去执行较为费时的工作了。在Android平台里,UI线程与其子线程之分工是很明确的:
  子线程负责执行费时的工作。
  主线程负责UI的操作或事件;子线程不可以插手有关UI的事。
在同一进程里的主、子线程之间可以透过MessageQueue来互相沟通。当一个新进程新诞生时,会同时诞生一个主线程,也替主线程建立一个MessageQueue,以及负责管理MessageQueue的Looper类别的对象。子线程可以透过Handler对象而将Message对象丢(Post)到主线程的MessageQueue里,而主线程的Looper物件就会接到这个Message对象,并依据其内容而呼叫适当的函数来处理。
36android虚拟机与java的jvm理解
在Android系统中,Dalvik虚拟机是运行Linux内核之上的.
  Dalvik和Java之间的另外一大区别就是运行环境——Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个 Dalvik应用作为一个独立的Linux进程执行。 1、架构不同:JVM使用栈架构;Dalvik使用的是寄存器,数据是加载到CPU的寄存器上的。
    2、JVM加载的.class文件,Dalvik加载的是.dex文件,对内存的分配情况做了优化。
Android:.java-->.class-->.dex-->.apk
Java:   .java-->.class-->.jar
每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念。
 
Android层级结构:
1.Application应用层(开发的软件)
2.Application Framwork(Activity manager   phoneManager)
3.Libraries(库)包含:Android Runtime
4.Linux内核 (cameraDriver  AudioDrivers)
 
37第三方开源的项目用过哪些?(唐杨老师总结)
图片加载用的什么库? Universinal-image-loader
Xml解析用的什么库? xStream
二维码扫描用的什么库? zxing
网络加载用的什么库? android-async-http (Github, AsyncHttpClient)
下拉刷新用的什么库? v4包中的SwipeRefreshLayout(pullToRefresh)
图片双指放大缩小用的什么库? PhotoView (Github)
资讯/博客 详情界面用怎么排版的? Webview控件, 显示服务器传过来的html数据, js和css在本地assets目录(为了支持代码高亮)
界面控件快速初始化用的什么库? ButterKnife(Github, 黄油刀, 注解的形式替代findViewById)或者Xutils(四大模块)
属性动画使用的库 ? nineoldandroids (Github)
 
38Java中jvm的内存分配:(针对速度而言:寄存器>栈>堆>其它)
A,堆:存放的都是new出来的对象,每个对象都包含一个与之对应的class信息。jvm只有一个堆区,被所有的线程所共享。
B,栈:每个线程包含一个栈区,存放基本类型的变量数据和对象的引用。每个栈中的数据是私有的,其它栈不能访问。
C,方法区:所有线程共享的内存区域,加载的是整个程序中永远唯一的元素,如class,static变量和常量。
39,一个对象在内存中产生过程:
Person p=new Person(“xiaoke”,25);
A,因为new用到了Person.class类,所以先找到Person.class类文件,并加载到内存的方法区中
B,执行该类的static代码块。如果有的话,给Person.class类进行初始化。
C,new Pseron()在堆内存中开辟空间,分配内存地址。
D,在堆内存中的建立对象的特有属性,并进行默认的初始化。
E,对属性进行显示初始化。
F,对对象进行构造代码块初始化。
G,对对象进行对应的构造函数初始化。
H,将堆内存地址赋给栈内存中P变量。
41,抽象类与接口的区别:
共性:他们都是不断抽取出来的抽象的概念
区别:
1,抽象类只能被单继承、接口可以被多实现,避免了单继承的局限性。
2.类与接口之间是实现关系,而且一个类在继承一个类的同时可以实现多个接口。
3.设计理念不同:抽象类是继承关系,是is a关系,接口是实现关系,是like a关系
4.抽象类是对一种事物的抽象,即对类的抽象。接口是对行为的抽象
5.接口中不能有静态代码块及静态方法,抽象类却可以有。
6.抽象类中的成员修饰符都是自定义的,而接口中的成员修饰符都是固定的(public static final)
7.抽象类可以定义抽象方法,供子类直接使用,也可以定义非抽象方法。它可以用于定义体系的基本共性的内容。
接口中只能定义抽象的方法(public abstract),需要子类全部实现,它主要用于功能的扩展。(java8中可以定义default修饰的方法)
42,接口中可以有构造函数吗?为什么?
答:不可以,原因:
1,构造器用于初始化成员变量,接口没有成员变量
2,类可以实现多个接口,若多个接口都有自己的构造器,则不好决定构造器的调用次序
3,构造器是属于类自己的,不能继承。因为是纯虚的,接口不需要构造器。
 
43,java中重载与重写的区别?
方法重载:
1),方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数或参数类型。重载是一个类中多态性的一种表现。
2) Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回值类型作为重载函数的区分标准(原因:调用的不确定性)。
方法重写(覆盖):
1) 父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
2) 若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
3) 子类函数的访问修饰权限不能少于父类的;
44,java中的三大特性:
A,封装:
定义:隐藏对象的属性和实现细节,仅对外提供公共的访问方式(set和get)。
好处:1,将变化隔离,2,便于使用,3,提高安全性。4,提高复用性。
封装的原则:
   1,将不需要对外提供的内容都隐藏起来。
2,把属性都隐藏,提供公共方法对其访问。
B,继承:
定义:1,多个类中存在相同属性和行为时,将这些共性内容抽取到单独一个类中,那么多个类无需再去定义这些属性和行为,只要继承那个类即可。
2,多个类成为子类,单独这个类成为父类或者超类,或者基类。
3,子类可以直接访问父类中非私有的属性和行为。
4,通过extends关键字让类与类之间产生了继承关系
好处:
1,提高了代码的复用性
2,让类与类之间产生了关系
3,提供了多态的前提。
C,多态
定义:某一类事物的多种存在形态。
体现:父类或者接口的引用指向了自己的子类对象。
前提:a,实现多态的三种方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载
          b,三个必要的条件:继承、重写、父类引用指向子类对象(向上转型)。
好处:提高代码的扩展性。有了多态,前期设计好的内容可以使用后期出现的子类内容,对于指挥对象做事情这件事,如果对象很多,指挥起来很麻烦,抽取对象的共性类型,对该类型的事物进行指挥就会变的很简单,
弊端:前期的程序虽然可以使用后期出现的子类内容,但是只能使用子类覆盖父类中的内容,不能使用子类中的特有内容。
转型:a,当需要对程序进行扩展,或者限定对对象的方法操作时,使用向上转型。操作其父类型。
        b,当要使用子类的特有的内容时,就需要向下转型。(在这个转型的过程中自始至终都是一个子类对象做着类型的变化而已)(切记,一定要做健壮性判断)。
45final、finally、finalize的区别?
final:是一个修饰符,可以用来修饰类、方法、变量。修饰类时不可以被继承,修饰方法时不可以被重写(覆盖),修饰变量时是一个最终常量
Finally:异常处理当中一定会被执行的语句,常常用来释放资源.只有遇到System.exit(0);才不执行。在return语句之前执行。
Finalize:Object类中的垃圾回收方法,需要被垃圾回收器调用。
46,StringBuffer与StringBuilder的区别:
1,StringBuilder是线程不安全的,StringBuffer是线程安全的
2,StringBuilder效率较StringBuffer高
3,提高效率的原理是单线程,没有锁的机制
4,单线程建议使用StringBuilder,多线程建议使用StringBuffer。
47,集合与数组及链表的区别?
A,关于ArrayList(集合)与LinkedList(链表)的比较分析
1)ArrayList底层采用数组实现,LinkedList底层采用双向链表实现。
2)当执行插入或删除操作时,采用LinkedList比较好。
3)当执行查询或搜索操作时,采用ArrayList比较好。
B,链表与数组的区别:
1,链表的使用不需要知道数据的大小,数组在创建时必须知道数据的大小
2,链表中没有对应的下标,只有指向下一数据的指针,而数组中每一个都有一个对应的下标
3,链表在内存中存储的数据可以是不连续的,而数组存储的数据占内存中连续的一段,用标示符标示。
C,集合与数组的区别:
1,集合长度是可变的,数组长度是固定的。
2,集合中只能存储对象,数组中既可以存储对象,又可以存储基本类型数值。
3,集合中存储的对象可以是任意类型的,数组中只能存储同一类型的对象。
48,Collections与Collection的区别:
1,Collections是集合框架中的一个工具类,里面提供了较多的对集合进行操作的方法,比如sort(),binarySearch()等,还可以把不安全的变成安全的(使用同步集合方法);
2,Collection是集合框架中的一个顶层接口,有两个子接口list、set,这两个子接口下面各有两个常用的子类:
List有ArrayList、LinkedList,set有hashSet、treeSet。
注意:
1,看到array(集合):数组结构,就要想到角标,有序(存入与取出有序),可重复,查询快。
2,看到link:链表结构,想到增删快,同时可以明确操作first、last的方法
3,看到hash:哈希表结构,想到无序,不可以重复,要想到元素依靠的hashCode和equals方法来保证唯一性。
4,看到tree:二叉树,就要想到比较的功能和排序,必须要想到两个接口(两种排序方法),元素自身实现Comparable----覆盖compareTo方法,容器自身实现Comparator----覆盖compare方法。判断元素唯一性的依据就是比较方法的返回结果return 0,是0,不存。
49,java中8种基本数据类型:
byte    boolean   1个字节     8位
char    short      2个字节    16位(一个中文汉字占两个字节)
int      float      4个字节    32位
double  long      8个字节    64位
原始类型: boolean    char,    byte,short,  int,   long, float, double
包装类型:Boolean,Character, Byte,Short,Integer, Long, Float, Double
50、&和&&的区别?
答:&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。
逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算
51、在Java中,如何跳出当前的多重嵌套循环?
答:在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。
52、Sleep与yield的区别?
答:
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException(中断异常),而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法具有更好的可移植性。
53,请说出与线程同步以及线程调度相关的方法。
答:
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
 
Java多线程中Sleep与Wait的区别:
相同点:
1,他们都是在多线程环境下,在程序的调用处,等待指定的毫秒数,并返回。
2,sleep()和wait()都可以通过Interrupt()方法,打断线程的暂停状态,从而使线程立刻抛出InterruptedException异常。
不同点:
1,Thread类中方法:sleep()和yield()
   Object类中方法:wait()和notify()
2. 每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4. sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常 
54,编写多线程程序有几种实现方式?
答:Java 5以前实现多线程有两种实现方法:一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。
55,启动一个线程是调用run()还是start()方法?
答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。
Start()方法的作用:1,启动线程,2调用run方法
56,Java中如何实现序列化,有什么意义?
答:定义:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
作用:序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
实现序列化的方法:
要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject()方法从流中读取对象。
序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆。
57,android内存的优化?
一、 Android的内存机制
  Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java堆内分配空间;然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化了,给我们请了一个专门的清洁工(GC)。
  那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象(连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。
二、Android的内存溢出
Android的内存溢出是如何发生的?
  Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。
为什么会出现内存不够用的情况呢?我想原因主要有两个:
由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。
 
1,数据库的cursor没有关闭。
2.构造adapter没有使用缓存contentview。
3.调用registerReceiver()后未调用unregisterReceiver().
4.未关闭InputStream/OutputStream。
5.Bitmap使用后未调用recycle()。
6.Context泄漏。
7.static关键字
58,Fragment与activity的区别?
1>Fragment表现为一个activity用户界面的一部分
2>一个activity可以有多个fragment
3>可以在多个activity中复用fragment
4>fragment有自己的生命周期
5>fragment有自己的事件处理
6>activity运行中,可以添加,移除一个fragment
7>fragment的生命周期有其宿主activity控制
59、知道volley吗?volley有什么特点
  Volley是在Gooogle I/O 2013发布的一个处理和缓存网络请求的库,能使网络通信更快,更简单,更健壮。
volley适合小而快的数据传输,Volley主要获取JSON对象和图片加载。
Json对象:
在onCreate初始化RequestQueue核心类
RequestQueue       mRequestQueue =  Volley.newRequestQueue(this);
 
Volley提供了JsonObjectRequest、JsonArrayRequest、StringRequest等Request形式。
JsonObjectRequest:返回JSON对象。
JsonArrayRequest:返回JsonArray。
StringRequest:返回String,这样可以自己处理数据,更加灵活。
另外可以继承Request<T>自定义Request。
 
Volley支持http的GET、POST、PUT、DELETE等方法
 
1,Volley提供了多种Request方法,ImageRequest能够处理单张图片,返回bitmap。
2,Volley的ImageCache接口允许你使用你喜欢的L1缓存实现。不幸的是Volley没有提供默认的实现。在I/O的介绍中展示了BitmapLruCache的一点代码片段,但是Volley这个库本身并不包含任何相关的实现。
ImageCache接口有两个方法,getBitmap(String url)和putBitmap(String url, Bitmap bitmap).这两个方法足够简单直白,他们可以添加任何的缓存实现。
3,网络请求下载图片显示,可以使用NetworkImageView
,比传统的ImageView多了网络处理,也添加了2个方法,设置开始下载的默认图和下载出错后显示图。
60、知道EventBus吗?用它有什么好处
EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCastReceiver在Fragment,Activity,Service,线程之间传递消息.
优点是开销小,代码更优雅。以及将发送者和接收者解耦。传统方式使用aidl实现进程间的通信。
 
应该包含4个成分:发布者,订阅者,事件,总线。
订阅者订阅事件到总线,发布者发布事件。
订阅者可以订阅多个事件,发布者可以发布任何事件,发布者同时也可以是订阅者。
 
 
EventBus.getDefault().register(this);//订阅事件
EventBus.getDefault().post(object);//发布事件
EventBus.getDefault().unregister(this);//取消订阅
 
 
onEventMainThread代表这个方法会在UI线程执行
onEventPostThread代表这个方法会在当前发布事件的线程执行
BackgroundThread这个方法,如果在非UI线程发布的事件,则直接执行,和发布在同一个线程中。如果在UI线程发布的事件,则加入到一个后台的单线程队列中去。
Async 代表这个方法直接在独立的线程中执行。
61.三级缓存源代码:
public class ImageCacheUtil {
private static final StringTAG = "ImageCacheUtil";
public static final int SUCCESS = 100;//获取图片成功的消息标志
public static final int FAIL = 101;//获取图片失败的消息标志
public Handler  handler;
private LruCache<String, Bitmap>lruCache;//lru缓存的集合
private File cacheDir;//缓存文件
private ExecutorServicenewFixedThreadPool;//线程池
 
public ImageCacheUtil(Context context,Handler handler){
//获取手机正在运行内存的最大值的八分之一
int maxSize=(int)(Runtime.getRuntime().maxMemory()/8);
//lru算法存放图片的集合在内存中,maxSize的意思是图片占用内存大小的最大值是运行内存的八分之一
lruCache = new LruCache<String, Bitmap>(maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
// value.getRowBytes()一行上面对应所有像素点占用的大小*value.getHeight()总共的行数
return value.getRowBytes()*value.getHeight();
}
};
//缓存文件
cacheDir = context.getCacheDir();
//使用线程池的方法,最适合的线程数是(2*手机核数+1)
newFixedThreadPool = Executors.newFixedThreadPool(5);
this.handler=handler;
}
//获取bitmap图片的方法
public Bitmap  getBitmap(String imgUrl,int position){
//position是确认给哪个imageView的控件使用的tag
Bitmap bitmap=null;
//1,内存中获取
bitmap = lruCache.get(imgUrl);
if (bitmap!=null) {
Log.i(TAG, "从内存中获取到图片");
return bitmap;
}
//2,文件中获取图片
bitmap= getBitmapFromLocal(imgUrl);
if (bitmap!=null) {
Log.i(TAG, "从文件中获取到图片");
return bitmap;
}
//3,网络中获取图片
getBitmapFromNet(imgUrl,position);
Log.i(TAG, "从网络中获取到的图片");
return null;
}
class RunableTask implements Runnable{
private String imgUrl;
private int position;
public RunableTask(String imgUrl,int position) {
this.imgUrl=imgUrl;
this.position=position;
}
@Override
public void run() {
//访问网络,下载图片
try {
//拿到路径
URL url = new URL(imgUrl);
//打开网络的连接
HttpURLConnection conn= (HttpURLConnection) url.openConnection();
//设置读取的超时时间
conn.setReadTimeout(2000);
//连接的超时时间
conn.setConnectTimeout(2000);
//方式
conn.setRequestMethod("GET");
//判断响应码是否是200
int code = conn.getResponseCode();
if (code==200) {
//获取文件的输入流
InputStream inputStream = conn.getInputStream();
//把流转化为bitmap的图片
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
//使用消息机制把信息传递出去
Message msg = new Message();
msg.what=SUCCESS;
msg.obj=bitmap;//把获取到的bitmap图片传递出去
msg.arg1=position;//把tag的标志传出去
handler.sendMessage(msg);
//把图片先存到内存中
lruCache.put(imgUrl, bitmap);
//写入文件中
writeToLocal(imgUrl,bitmap);
}
return;//成功了就结束
} catch (Exception e) {
e.printStackTrace();
}
//失败的消息
Message msg = new Message();
msg.what=FAIL;
handler.sendMessage(msg);
}
}
//写入到文件中去,缓存中
private void writeToLocal(String imgUrl, Bitmap bitmap) {
String fileName;
try {
fileName=MD5Utils.encode(imgUrl).substring(10);
File file = new File(cacheDir,fileName);
//文件的输出流
FileOutputStream fileOutputStream = new FileOutputStream(file.getAbsolutePath());
//定义一个大小,当图片的大小超过时,就会进行等比例压缩
bitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
//从网络中获取到的图片
private void getBitmapFromNet(String imgUrl,int position) {
//从网络中下载图片时耗时的操作,要在子线程中,可以采用使用线程池的方法下载图片
newFixedThreadPool.execute(new RunableTask(imgUrl,position));
}
/**
 * 从缓存文件夹下取图片
 * @param imgUrl
 * @return
 */
private Bitmap getBitmapFromLocal(String imgUrl) {
String fileName;
try {
// 使用MD5加密文件的路径,保证唯一性,为了拿到文件的名字,截取加密好的路径的前10位
fileName = MD5Utils.encode(imgUrl).substring(10);
//创建文件
File file = new File(cacheDir, fileName);
//解析文件的路径,拿到bitmap图片
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
//加入到内存中,因为内存的效率要高
lruCache.put(imgUrl, bitmap);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
62,数据缓存的处理方式:
 * 数据缓存的处理:
1,第一次联网从服务器端获取到数据后,缓存到本地的文件夹下
2,以后在进入的时候,首先读取本地文件夹下的数据,再去服务器端获取最新的数据
3.获取成功,用最新的数据覆盖原有的数据
    4,获取失败,加载本地以及缓存过的文件夹下的数据
 * 分为两种存储方式:
 1,持久化存储(data/data/文件,sharedPreference,sqlite数据库
  2,非持久化存储(内存中)
63,viewpager和内部view的事件交互过程
1,按下的事件优先有viewpager去捕获,向内部控件做传递,当按下的事件传递给view,view就会去响应按下的事件
2,按下后不移动,做抬起操作,对应的抬起事件,就会优先有viewpager去做捕获,然后传递给内部的view,view就会去响应对应的action_up
 3,按下并且滑动(action_move),然后抬起,action_move优先有viewpager捕获到,向内部的view做传递,view也有部分响应移动事件.当action_move移动到一定的距离时候,view就不在响应移动事件,转而响应action_cancel,view只要响应了action_cancel事件,以后的事件就不在响应,后续的移动事件,就作用在viewpager上,抬起事件也作用在viewpager上
64,listView中item点击过后,标题变为红色字体的代码实现:
 
第一步:
//监听listView点击条目的侦听事件
lv_item_news.setOnItemClickListener(new OnItemClickListener() {
 
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
//这里的position是整个listview(包含轮播图)选中view条目的索引
//第四,判断底部listView的条目是否选中,默认是false
if (!newsList.get(position-1).isRead) {
newsList.get(position-1).isRead=true;
//使用sp保存索引值
//首先,从sp中获取索引值,IDS是存储listView选中条目的唯一id值
String ids = SharedPreferencesUitl.getStringData(context,"IDS", "");
//第二,保存点击的索引值,position-1的目的是去掉轮播图的索引值
SharedPreferencesUitl.saveStringData(context,"IDS",ids+"#"+newsList.get(position-1).id);
//第三,刷新数据适配器
myListViewAdapter.notifyDataSetChanged();
}
第二步:
//获取本地的id操作
String str = SharedPreferencesUitl.getStringData(context,"IDS", "");
//切割字符串
String[] split = str.split("#");
//将本地的id都存储在idList集合中
idList.clear();
for (int i = 0; i < split.length; i++) {
idList.add(split[i]);
}
第三步:
//判断服务器拿到的id与本地的id进行比对
for (int i = 0; i <newsList.size(); i++) {
//本地保存的id是否包含有从服务器拿到的id
if (idList.contains(newsList.get(i).id)) {
newsList.get(i).isRead=true;//包含这个id,那么就将已读过字段设置为true
} else {
newsList.get(i).isRead=false;//没有读过的设置为false
}
}
第四步:在数据适配器中:
if (list.get(position).isRead) {
holder.tv_title.setTextColor(Color.RED);
} else {
holder.tv_title.setTextColor(Color.BLACK);
}
return convertView;
65,使用viewpager时,需要注意的两个注意事项:
问题有两个:
 * 1,手动滑动viewpager的页面,就与底部的模块不对应了,
 * 2,viewpager自带有预加载的功能,由于获取的是网络的数据,很占空间,所以不需要预加载
解决的方法,
 * 1,屏蔽viewpager的自带滑动的功能,将onInterceptTouchEvent和OnTouchEvent都设置为返回false
 * 2,默认是1---》改为0,可以屏蔽预加载的功能
65,网络加载数据请求的真实参数,以xutils为例:
//由于子类都需要从网络中获取数据,而且子类展示的view效果不同,所以抽取一个公共的获取网络数据的方法到父类中
public void requestData(HttpMethod httpMethod,String url,RequestParams params,RequestCallBack<String> callBack) {
//使用xutils获取网络数据
HttpUtils httpUtils = new HttpUtils();
//传递一个参数
//RequestParamsparams = new RequestParams();
//params.addBodyParameter("name", "1243");
//传递多个参数
//RequestParamsparams = new RequestParams();
//ArrayList<NameValuePair> arrayList = new ArrayList<NameValuePair>();
//BasicNameValuePair basicNameValuePair = new BasicNameValuePair("name","111");
//BasicNameValuePair basicNameValuePair1 = new BasicNameValuePair("psd","222");
//BasicNameValuePair basicNameValuePair2 = newBasicNameValuePair("userid","333");
//arrayList.add(basicNameValuePair);
//arrayList.add(basicNameValuePair1);
//arrayList.add(basicNameValuePair2);
//params.addBodyParameter(arrayList);
//1,请求方式,2,请求链接地址,3,发送的参数,4,请求结果的回调方法具体实现
httpUtils.send(httpMethod,url,params,callBack);
}
66,view的原理分析:
1.每个View都只有一个父View
2.整个视图只有一个根View
 
3.DecorView --- FrameLayout
 3.1我们在写视图的时候,要尽可能的少写视图层级
 3.2能用FrameLayout写的,就不要用RelativiLayout,能用RelativiLayout写的,就不要用LinearLayout。
 
 4.我们通过setContentView设置的View并不是最根View。
  4.1 隐藏标题栏需要在setContentView之前设置。
 
 5.一个View如何显示到屏幕上来
  5.1 view有多大 -- measure --- onMeasure
  5.2 view的位置在哪儿  -- layout  --- onLayout
  5.3 view长什么样子 -- draw  --- onDraw
 
 6.不是所有的View都能添加子View
  6.1 只有ViewGroup可以添加子view,因为只有ViewGroup实现了ViewParent接口
  6.2 我们通过getParent拿到的虽然是ViewParent,但是在它不为null的前提下,我们可以放心的强转为ViewGroup
 
7. measure
    只有View有这个方法,ViewGroup没有重载
    测量自身的大小
 
MeasureSpec:用来辅助计算View的大小
    View.MeasureSpec.AT_MOST;// 最大at_most
View.MeasureSpec.EXACTLY;// 精确exactly
View.MeasureSpec.UNSPECIFIED;// 未知unspecified
目前未知没有使用,因为每一个View被添加到视图上时,都必须指定宽高,就算你不指定,也会有一个默认的(每个ViewGroup生成默认宽高都不一样)
 
通过 MeasureSpec.makeMeasureSpec() 来合成模式和宽高
通过 MeasureSpec.getMode和MeasureSpec.getSize来获取模式和宽高
 
代码中设置View的宽和高必须要通过LayoutParams来设置,所有影响子View和父View之间的关系的属性全部需要通过LayoutParams来设置,也可以根据xml布局中属性名是否带有layout_开头来判断
 
LayoutParams有很多种类,基本上是每一种布局都有一个自己的实现,因为每个布局都有各自的特征,没办法用一个统一类来描述
 
如果父亲的mode是EXACTLY, 子View是WRAP_CONTENT  那么mode是AT_MOST, size是父亲的最大size。
如果父亲的mode是EXACTLY, 子View是MATH_PARENT   那么mode是EXACTLY, size是父亲的最大size。
如果父亲的mode是EXACTLY, 子View是DIP(自己设置的大小)   那么mode是EXACTLY, size是DIP。
 
如果父亲的mode是AT_MOST,  子View是WRAP_CONTENT  那么mode是AT_MOST, size是父亲的最大size。
如果父亲的mode是AT_MOST,  子View是MATH_PARENT   那么mode是EXACTLY, size是父亲的最大size。// ---(大多数View实现都是EXACTLY,但是有些View实现是AT_MOST,我们要尽量避免这种情况)
如果父亲的mode是AT_MOST,  子View是DIP           那么mode是EXACTLY, size是DIP。
 
如果你设定宽高为MATH_PARENT 或者DIP 则模式为EXACTLY,如果是WRAP_CONTENT,则为AT_MOST
如果你设定的宽高为DIP,那么你的size就是dip,其他情况都是父View的最大值,或者父View剩余的最大值
 
 
View内部是如何测量自身的
如果是一个包括内容的(540,50);
1.它会测量自己的背景图片   100*100
2.TextView  测量文字
ImageView 测量自身的图片   200*200;  ---> 50
 
200 200
ViewGroup --> 100 * 100
100 * 100
ViewGroup --> wrap wrap  AT_MOST|100
100*100
ImageView 是包裹内容的,但是设置的图片 200*200;AT_MOST|100
100*100
如果自身测量的结果大于父View传递过来的参数,并且模式是AT_MOST的时,它会选择一个较小的值
 
onMeasure
7.1 在一个View中,它是用来测量自身的大小,实际上就是设置自身的大小
7.2 在ViewGroup中,它通过onMeasure来测量子View的大小,并通过子View的大小来设置自身的大小。
view.measure(int,int);
 
8.layout(l,t,r,b)
    在View中,是用来指定一个View的上下左右四个点,ViewGroup中没有重载
    首先它用临时变量记录自己上一次的l,t,r,b,然后比较这一次的l,t,r,b。
    if(l != l || t !=t || r != r || b != b){
     boolean changed = onSizeCHange
    }
 
   onLayout(changed)
     8.1 在View中,它是空实现。因为一个View的位置是由它的父亲指定的。
     8.2 在ViewGroup中,它是用来确定所有子View的位置。
     ViewGroup的子类一般怎么实现该方法
      LinearLayout --> 检查自身的方向orientation,判断是横向还是纵向
      for循环遍历所有的子View。为子View准备l,t,r,b
      如果是横向的,一般第一个View的l就是0,t就是0,r就是该View的宽度,b就是该View的高度。后面的View的l会等于前一个View的r。
 
      RelativiLaytout --> for循环所有的子View。为子View准备l,t,r,b
      首先它会获取这个子View的params,然后进行一系列的判断,根据其他子View确定该View的位置。
 
      FrameLayout --> for循环所有的子View。为子View准备l,t,r,b
      它就判断gravity,如果是start,就从左上角开始布局,
      一般情况下,所有的子View都具备相同的l,t
 
 
9. draw
    在View中,它是用来画一些通用的东西的。
    如果你是View,它就会调用你的onDraw方法,
如果你是ViewGroup,它就会调用你的dispatchDraw
67,android屏幕适配方案:
##android下手机的适配##
**适配**:即当前应用在相同的手机上面显示相同的效果。适配前需要首先确定当前手机所属像素密度类型(如:xhdpi、hdpi、mdpi等),以下已华为G700、模拟器为例,计算其像素密度。
**案例一:**
     手机型号:G700
     手机分辨率:1280*720 (注:手机两个直角边上分别放置了1280及720个像素点)
     手机尺寸大小:5英寸(手机斜边长度)
   
     假设a,b分别为两个直角边,c为斜边,由勾股定理可得出计算方式:sqrt(a*a+b*b)/c
     计算结果:sqrt(1280*1280+720*720)/5≈ 293.72dpi
   
     根据google官方文档说明得出,当前手机最接近320dpi,则将其归纳在xhdpi手机范围内,即1dp=2px;
**案例二:**
   
     手机型号:模拟器
     手机分辨率:800*480(注:手机两个直角边上分别放置了800及480个像素点)
     手机尺寸大小:3.7英寸(手机斜边大小)
   
     计算结果:sqrt(800*800+480*480)/3.7≈ 252.15dpi
     根据google官方文档(图1-1)得出,当前手机接近240dpi,则将其归纳在hdpi手机范围内,即1dp=1.5px。
     参照以上方式可将市场上大多数手机划分为5个像素密度等级,分别为:
     ldpi:120dpi,像素密度与dp转换关系为:1dp = 0.75px
     mdpi:160dpi    ,像素密度与dp转换关系为:1dp = 1px
     hdpi:240dpi,像素密度与dp转换关系为:1dp = 1.5px
     xhdpi:320dpi,像素密度与dp转换关系为:1dp = 2px
     xxhdpi:480dpi,像素密度与dp转换关系为:1dp = 3px
(注:以下案例就当前两款手机进行屏幕适配测试)
###适配方式一:图片适配
不同像素密度的手机加载工程资源文件(res)中不同资源图片,以上述两款手机为例。布局代码如下:
即在不同的文件夹下面放置不同的图片,项目会根据手机的分辨率大小加载不同的图片
图片放置的文件夹为res下的ldpi,mdpi,hdpi,xhdpi,xxhdpi
     <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"
         tools:context=".MainActivity" >
         <ImageView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:background="@drawable/a" />
     </RelativeLayout>
###适配方式二:dimens.xml文件适配
dimens.xml存在于工程资源(res)文件夹中不同values(如:value-1280x720、value-800x480)文件夹下,可用于指定控件大小,不同像素密度手机加载不同values文件夹下的dimens.xml文件,使用方式如下:
         <LinearLayout 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"
         android:orientation="vertical"
         tools:context=".MainActivity" >
          <!-- 不同的手机加载不同的dp -->
         <TextView
             android:background="#987654"
             android:layout_width="@dimen/width"
             android:layout_height="wrap_content"
             android:text="@string/hello_world" />
     </LinearLayout>
模拟器(hdpi):加载dimens.xml资源文件,位于res/value-800x480文件夹下
     <resources>
         <dimen name="width">160dp</dimen>
     </resources>
     根据上述hdpi dp和px的转换关系1dp = 1.5px,则160dp = 240px,当前控件宽度应该位于屏幕中间位置。
G700(xhdpi):加载dimens.xml资源文件,位于res/value-1280x720文件夹下
     <resources>
         <dimen name="width">180dp</dimen>
     </resources>
     根据上述xhdpi dp和px的转换关系1dp = 2px,则180dp = 360px,当前控件宽度应该位于屏幕中间位置。
###适配方式三:布局文件适配(工程比较浩大,不常用)
不同分辨率的手机,加载不同的布局文件已达到适配效果。创建多个layout(如:layout-1280x720、layout-800x480)文件夹用于存放不同像素密度手机所需布局文件。
模拟器(hdpi):加载activity_main.xml布局文件,位于res/layout-800x480文件夹下:
          <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"
              tools:context=".MainActivity" >
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="800*480手机会去加载的布局文件" />
          </RelativeLayout>
G700(xhdpi):加载activity_main.xml布局文件,位于res/layout-1280x720文件夹下:
          <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"
              tools:context=".MainActivity" >
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="1280*720手机会去加载的布局文件" />
          </RelativeLayout>
###适配方式四:java代码适配(精确,至多,未定义)
父布局与子布局的关系:
精确:看子,父填充父窗体
至多:看父设置的参数
未定义:看子wrap_content,父
通过android相应api获取当前手机的宽高像素值,按比例分配屏幕中控件的宽高以达到适配效果。核心代码如下:
   
     布局文件
     <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"
         tools:context=".MainActivity" >
         <TextView
             android:id="@+id/tv"
             android:background="#000000"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/hello_world" />
     </RelativeLayout>
   
     activity中oncreate核心代码:
          TextView tv  = (TextView) findViewById(R.id.tv);
          //获取封装当前手机屏幕信息对象,用于存放宽高值
          DisplayMetrics metrics  = new DisplayMetrics();
          //给当前屏幕设置宽高
          getWindowManager().getDefaultDisplay().getMetrics(metrics);
          //获取高度
          Constant.srceenHeight = metrics.heightPixels;
          //获取宽度
          Constant.srceenWidth = metrics.widthPixels;
        
          Log.i(tag, "Constant.srceenHeight = "+Constant.srceenHeight);
          Log.i(tag, "Constant.srceenWidth = "+Constant.srceenWidth);
        
          //宽高各占50%
          RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams((int)(Constant.srceenWidth*0.5+0.5),
          (int)(Constant.srceenHeight*0.5+0.5));
          tv.setLayoutParams(layoutParams);
###适配方式五:权重适配
通过android提供的(权重)剩余空间分配,已达到适配效果。显示界面加载布局文件如下:
     <LinearLayout 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"
         android:orientation="horizontal"
         tools:context=".MainActivity" >
          <TextView
              android:background="#000000"
              android:layout_width="0dp"
              android:layout_weight="1"
              android:layout_height="match_parent"/>
          <TextView
              android:background="#123456"
              android:layout_width="0dp"
              android:layout_weight="1"
              android:layout_height="match_parent"/>
     </LinearLayout>
68,算法和时间复杂度
1.选择排序:不稳定,时间复杂度 O(n^2)
    选择排序的基本思想是对待排序的记录序列进行n-1遍的处理,第i遍处理是将L[i..n]中最小者与L[i]交换位置。这样,经过i遍处理之后,前i个记录的位置已经是正确的了。
2.插入排序:稳定,时间复杂度 O(n^2)
    插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较L[i]和L[i-1],如果L[i-1]≤ L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1≤j≤i-1),使得L[j] ≤L[j+1]时为止。图1演示了对4个元素进行插入排序的过程,共需要(a),(b),(c)三次插入。
3.冒泡排序:稳定,时间复杂度 O(n^2)
   冒泡排序方法是最简单的排序方法。这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。在作第二遍处理时,由于最高位置上的元素已是“最轻”元素,所以不必检查。一般地,第i遍处理时,不必检查第i高位置以上的元素,因为经过前面i-1遍的处理,它们已正确地排好序。
4.堆排序:不稳定,时间复杂度 O(nlog n)
    堆排序是一种树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
5.归并排序:稳定,时间复杂度 O(nlog n)
    设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。
6.快速排序:不稳定,时间复杂度 最理想 O(nlogn)最差时间O(n^2)
    快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。
7.希尔排序:不稳定,时间复杂度 平均时间 O(nlogn)最差时间O(n^s) 1<s<2
在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点,并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为 增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
69,scrollView与ListView冲突的解决办法:
1,自定义可适应ScrollView的listView,重写onMeasure()方法,
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
00001.         int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
00002.         MeasureSpec.AT_MOST);
00003.         super.onMeasure(widthMeasureSpec, expandSpec);
 
 
注意需要手动设置scrollView滚动到顶端,sv.smoothScrollTo(0,0);
2,手动设置ListView的高度
/**
 * 动态设置ListView的高度
 * @param listView
 */
public static void setListViewHeightBasedOnChildren(ListView listView) {
    if(listView == null) return;
 
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        // pre-condition
        return;
    }
 
    int totalHeight = 0;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }
 
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
}
注意点:
  一是Adapter中getView方法返回的View的必须由LinearLayout组成,因为只有LinearLayout才有measure()方法,如果使用其他的布局如RelativeLayout,在调用listItem.measure(0, 0);时就会抛异常,因为除LinearLayout外的其他布局的这个方法就是直接抛异常的,没理由…。我最初使用的就是这个方法,但是因为子控件的顶层布局是RelativeLayout,所以一直报错,不得不放弃这个方法。
  二是需要手动把ScrollView滚动至最顶端,sv.smoothScrollTo(0,0);
因为使用这个方法的话,默认在ScrollView顶端的项是ListView。
3,使用单个ListView取代scrollView中所有的内容:
原ListView上方数据和下方数据,都写进两个xml布局文件中:
 Java代码方面,需要自定义一个Adapter,在Adapter中的getView方法中进行position值的判断,根据position值来决定inflate哪个布局:
4,使用Linearlayout取代 ListView,为其加上BaseAdapter适配:
只需要自定义一个类继承自LinearLayout,为其加上对BaseAdapter的适配。
注意:首先在Activity中手动为LinearLayout添加子项控件,不过需要注意的是,在添加前需要调用其removeAllViews的方法,其次费时间在findViewById中
5,在scrollView中添加添加一个属性:android:fillViewPort=”true”;就可以让listview全屏,
问题是:虽然listView的数据全部显示了,可是listView不能滑动了。这是致命的伤...

猜你喜欢

转载自blog.csdn.net/z282011862/article/details/80624166