面试 9.26 总结

有些笔试题做的不好,首先基础还是不牢固,不能粗心,不然就会错过转瞬即逝的机会。android再看看,还有最后一家公司做是投的android了,其他的就没有了,不管如何,加油吧!!!! 

1.相对于资深的android工程师来说,提的问题还是应付不了。如 画布  网络   版本兼容等的问题,都是需要再看看,争取以后不犯类似的错误。

2.继承thread和实现runnable接口的区别?

实现runnable,可以继承多个接口,便于资源的共享

 

--------------AsyncTask的用法-----------------

private  class DownloadFilesTask  extends  AsyncTask<URL,Integer,Long>

{protected Long doInBackground(URL... urls)

{int count = urls.length;

long totalSize =0;

for(int i =0; i < count; i++){ totalSize +=Downloader.downloadFile(urls[i]); publishProgress((int)((i /(float) count)*100));

// Escape early if cancel() is called

if(isCancelled())break;}

return totalSize;}

protectedvoid onProgressUpdate(Integer... progress){ setProgressPercent(progress[0]);
}

protectedvoid onPostExecute(Long result){ showDialog("Downloaded "+ result +" bytes");}}

 

-------------版本兼容问题-----------------------

比如高版本API废弃了低版本的API,这样的话,还是用低版本的API开发的话,再低版本下运行,就会照成程序的崩溃,解决方案如下

 
大致结构:
假设SDK版本开始变化的 SDK版本是 sdkversion
 
if(sdk< sdkversion){
//加载低版本的实现activity的代码 
}else{
//加载高版本实现activity的代码
}

 
上面两个部分打开不同的activity 就可以了,不可以在一个activity写两个版本的代码,那样,在加载到内存的时候,会判断没相关API就会报错!

 

在全世界,现在人们手里有着各种各样的基于Android的设备。而这些设备中,有很多种Android平台的版本在使用,一些运行着最新版平台,而另一些还在运行着老的版本。作为一名开发人员,你需要考虑你的应用程序是否支持后向兼容——你想你的应用程序能在所有的设备上运行吗,或是只是在最新的平台上运行?在某些情况下,在支持的设备上部署新的API,并支持老的设备是很有用的。

可以设置minSdkVersion:

  1. <manifest>
  2. <uses-sdk android:minSdkVersion="3" />
  3. </manifest>
复制代码

然而,如果你想添加一个有用的但不是必须的特性时,例如在硬件键盘可用的时候弹出一个屏幕键盘,你可以这样书写你的代码:允许你的程序使用新的特征,而在老的设备上不会失败。

使用反射

假设你想使用android.os.Debug.dumpHprofData(String filename)这个新的方法。Debug这个类自从Android 1.0的时候就已经存在了,但这个方法在Android 1.5(API等级3)中才新增的。如果你想直接调用它,那么,在Android 1.1或更早的设备上,你的app将运行失败。

最简单的方式是通过反射的方式来调用这个方法。这需要做一次查找并在Method对象上进行缓存。调用这个方法实质上是在调用Method.invoke,并对结果进行拆箱。参考以下内容:

  1. public class Reflect {
  2. private static Method mDebug_dumpHprofData;
  3. static {
  4. initCompatibility();
  5. };
  6. private static void initCompatibility() {
  7. try {
  8. mDebug_dumpHprofData = Debug.class.getMethod(
  9. "dumpHprofData", new Class[] { String.class } );
  10. /* success, this is a newer device */
  11. } catch (NoSuchMethodException nsme) {
  12. /* failure, must be older device */
  13. }
  14. }
  15. private static void dumpHprofData(String fileName) throws IOException {
  16. try {
  17. mDebug_dumpHprofData.invoke(null, fileName);
  18. } catch (InvocationTargetException ite) {
  19. /* unpack original exception when possible */
  20. Throwable cause = ite.getCause();
  21. if (cause instanceof IOException) {
  22. throw (IOException) cause;
  23. } else if (cause instanceof RuntimeException) {
  24. throw (RuntimeException) cause;
  25. } else if (cause instanceof Error) {
  26. throw (Error) cause;
  27. } else {
  28. /* unexpected checked exception; wrap and re-throw */
  29. throw new RuntimeException(ite);
  30. }
  31. } catch (IllegalAccessException ie) {
  32. System.err.println("unexpected " + ie);
  33. }
  34. }
  35. public void fiddle() {
  36. if (mDebug_dumpHprofData != null) {
  37. /* feature is supported */
  38. try {
  39. dumpHprofData("/sdcard/dump.hprof");
  40. } catch (IOException ie) {
  41. System.err.println("dump failed!");
  42. }
  43. } else {
  44. /* feature not supported, do something else */
  45. System.out.println("dump not supported");
  46. }
  47. }
  48. }
复制代码

使用静态初始化方法来调用initCompatibility,进行方法的查找。如果查找成功的话,使用一个私有的方法(与原始的函数签名一致——参数,返回值、异常检查)来替换方法的调用。返回值(如果有的话)和异常都如同原始的方法一样进行返回。fiddle方法演示了程序的选择逻辑,是调用新的API还是在新API无效的情况下作其它的事情。

对于每个你想调用的方法,你可能要添加一个额外的私有Method字段,字段初始化方法,和对调用的包装方法。

如果想调用一个之前未定义的类的方法的话,就比较复杂了。并且,调用Method.invoke()比直接调用这个方法要慢很多。这种情况可以通过一个包装类来缓和一下。

使用包装类

想法是创建一个新的类,来包装新的或已经存在的类暴露出来的所有的新API。包装类中的每个方法只是调用相应的真实方法并返回相同的结果。

如果目标类和方法存在的话,能得到与直接调用相同的行为,并有少量的性能损失。如果目标类或方法不存在的话,包装类的初始化会失败,并且你的应用程序知道必须避免使用这些新的方法。

假设这个类是新增的:

  1. public class NewClass {
  2. private static int mDiv = 1;
  3. private int mMult;
  4. public static void setGlobalDiv(int div) {
  5. mDiv = div;
  6. }
  7. public NewClass(int mult) {
  8. mMult = mult;
  9. }
  10. public int doStuff(int val) {
  11. return (val * mMult) / mDiv;
  12. }
  13. }
复制代码


我们可能这样创建一个包装类:

  1. class WrapNewClass {
  2. private NewClass mInstance;
  3. /* class initialization fails when this throws an exception */
  4. static {
  5. try {
  6. Class.forName("NewClass");
  7. } catch (Exception ex) {
  8. throw new RuntimeException(ex);
  9. }
  10. }
  11. /* calling here forces class initialization */
  12. public static void checkAvailable() {}
  13. public static void setGlobalDiv(int div) {
  14. NewClass.setGlobalDiv(div);
  15. }
  16. public WrapNewClass(int mult) {
  17. mInstance = new NewClass(mult);
  18. }
  19. public int doStuff(int val) {
  20. return mInstance.doStuff(val);
  21. }
  22. }
复制代码


包装类拥有和原始类一模一样的方法和构造函数,加上一个静态的初始化方法和测试方法来检查新类是否存在。如果新类不可获得的话,WrapNewClass的初始化会失败,因此,要确保包装类在这种情况下不要被使用。checkAvailable方法是一种强制类进行初始化的简单方法。我们可以像这样来使用:

  1. public class MyApp {
  2. private static boolean mNewClassAvailable;
  3. /* establish whether the "new" class is available to us */
  4. static {
  5. try {
  6. WrapNewClass.checkAvailable();
  7. mNewClassAvailable = true;
  8. } catch (Throwable t) {
  9. mNewClassAvailable = false;
  10. }
  11. }
  12. public void diddle() {
  13. if (mNewClassAvailable) {
  14. WrapNewClass.setGlobalDiv(4);
  15. WrapNewClass wnc = new WrapNewClass(40);
  16. System.out.println("newer API is available - " + wnc.doStuff(10));
  17. } else {
  18. System.out.println("newer API not available");
  19. }
  20. }
  21. }
复制代码

如果调用checkAvailable成功,我们知道新的类是系统的一部分。如果它失败了,我们知道新的类不存在,并作相应的调整。应该指出的是,由于字节码校验不支持对一个不存在的类的引用,因此,在调用checkAvailable之前就有可能失败。像实例代码那样构建,结果是一样的,可能字节码校验抛出异常或者Class.forName的调用抛出异常。

当包装一个有新方法的已存类时,你只需要在包装类中添加新的方法。老的方法直接调用。新的方法需要在WrapNewClass的静态初始化方法中作一次反射检查。

测试是关键

你必须测试任何想支持的Android框架版本。一般来说,应用程序在不同的版本上行为不同。记住一条法则:如果你不尝试,它就不能工作。

你可以在老版本平台的模拟器上运行应用程序来测试程序的后向兼容性。由于可以创建不同API等级的“虚拟设备”,因此,你可以很容易地进行测试。一旦你创建了AVD,你就可以在新老版本系统上进行程序测试,也许你还可以一边测试一边观察它们的不同点。

 

 

-----------------------画布、网络、手势----------------------------------

GestureDetector:手势接口,用来实现响应用户的各种手势,包括按下,滑动,弹起,长按,拖动等的事件。

为了加强鼠标响应事件,Android提供了GestureDetector手势识别类。通过GestureDetector.OnGestureListener来获取当前被触发的操作手势(Single Tap Up、Show Press、Long Press、Scroll、Down、Fling),具体包括以下几种:

boolean  onDoubleTap(MotionEvent e) 
解释:双击的第二下Touch down时触发 
boolean  onDoubleTapEvent(MotionEvent e) 
解释:双击的第二下Touch down和up都会触发,可用e.getAction()区分。 
boolean  onDown(MotionEvent e) 
解释:Touch down时触发 
boolean  onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 
解释:Touch了滑动一点距离后,up时触发。 
void  onLongPress(MotionEvent e) 
解释:Touch了不移动一直Touch down时触发 
boolean  onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 
解释:Touch了滑动时触发。 
void  onShowPress(MotionEvent e) 
解释:Touch了还没有滑动时触发 
(与onDown,onLongPress比较 
onDown只要Touch down一定立刻触发。 
而Touchdown后过一会没有滑动先触发onShowPress再是onLongPress。 
所以Touchdown后一直不滑动,onDown->onShowPress->onLongPress这个顺序触发。


boolean  onSingleTapConfirmed(MotionEvent e) 
boolean  onSingleTapUp(MotionEvent e) 
解释:上面这两个函数都是在touch down后又没有滑动(onScroll),又没有长按(onLongPress),然后Touchup时触发。

点击一下非常快的(不滑动)Touchup: 
onDown->onSingleTapUp->onSingleTapConfirmed 
点击一下稍微慢点的(不滑动)Touchup: 
onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed

使用GestureDetector需要在View中重写onTouchEvent事件,例如:

  1. GestureDetector mGesture = null;  
  2. @Override  
  3.     public boolean onTouch(View v, MotionEvent event)  
  4.     {  
  5.         // TODO Auto-generated method stub  
  6.         return mGesture.onTouchEvent(event);  
  7.     }  

 详细的测试例子如下:

  1. package com.jiubang.android.gesturetest;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.util.Log;  
  6. import android.view.GestureDetector;  
  7. import android.view.MotionEvent;  
  8. import android.view.View;  
  9. import android.view.GestureDetector.SimpleOnGestureListener;  
  10. import android.view.View.OnTouchListener;  
  11. import android.widget.Button;  
  12.   
  13. public class GestureActivity extends Activity  
  14.             implements OnTouchListener  
  15. {  
  16.     private Button mButton = null;  
  17.     GestureDetector mGesture = null;  
  18.       
  19.     /** Called when the activity is first created. */  
  20.     @Override  
  21.     public void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.main);  
  24.           
  25.         Log.i("TEST""onCreate");  
  26.         mButton = (Button)findViewById(R.id.button1);  
  27.         mButton.setOnTouchListener(this);  
  28.         mGesture = new GestureDetector(thisnew GestureListener());  
  29.     }  
  30.   
  31.     @Override  
  32.     public boolean onTouch(View v, MotionEvent event)  
  33.     {  
  34.         // TODO Auto-generated method stub  
  35.         return mGesture.onTouchEvent(event);  
  36.     }  
  37.       
  38.     class GestureListener extends SimpleOnGestureListener  
  39.     {  
  40.   
  41.         @Override  
  42.         public boolean onDoubleTap(MotionEvent e)  
  43.         {  
  44.             // TODO Auto-generated method stub  
  45.             Log.i("TEST""onDoubleTap");  
  46.             return super.onDoubleTap(e);  
  47.         }  
  48.   
  49.         @Override  
  50.         public boolean onDown(MotionEvent e)  
  51.         {  
  52.             // TODO Auto-generated method stub  
  53.             Log.i("TEST""onDown");  
  54.             return super.onDown(e);  
  55.         }  
  56.   
  57.         @Override  
  58.         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
  59.                 float velocityY)  
  60.         {  
  61.             // TODO Auto-generated method stub  
  62.             Log.i("TEST""onFling:velocityX = " + velocityX + " velocityY" + velocityY);  
  63.             return super.onFling(e1, e2, velocityX, velocityY);  
  64.         }  
  65.   
  66.         @Override  
  67.         public void onLongPress(MotionEvent e)  
  68.         {  
  69.             // TODO Auto-generated method stub  
  70.             Log.i("TEST""onLongPress");  
  71.             super.onLongPress(e);  
  72.         }  
  73.   
  74.         @Override  
  75.         public boolean onScroll(MotionEvent e1, MotionEvent e2,  
  76.                 float distanceX, float distanceY)  
  77.         {  
  78.             // TODO Auto-generated method stub  
  79.             Log.i("TEST""onScroll:distanceX = " + distanceX + " distanceY = " + distanceY);  
  80.             return super.onScroll(e1, e2, distanceX, distanceY);  
  81.         }  
  82.   
  83.         @Override  
  84.         public boolean onSingleTapUp(MotionEvent e)  
  85.         {  
  86.             // TODO Auto-generated method stub  
  87.             Log.i("TEST""onSingleTapUp");  
  88.             return super.onSingleTapUp(e);  
  89.         }  
  90.           
  91.     }  
  92. }  

canvas:怎么在一个弯曲的线上写字?

使用path来解决--------------------------------------------

网络:

1.当前正在连接的网络

  ConnectivityManager  connect = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

//获取当前网络的网络信息数据

  NetworkInfo networkinfo = connect.getActiveNetworkInfo();

2.wifi

  mWiFiManager = (WifiManager)getSystemService(WIFI_SERVICE);

3. 下载图片

  URL url = new URL(ANDROID_URL);
            HttpURLConnection httpUrlConn = (HttpURLConnection)url.openConnection();
            httpUrlConn.connect();
            
            InputStream is = httpUrlConn.getInputStream();
            Bitmap androidLogo=BitmapFactory.decodeStream(is);
            
            IV.setImageBitmap(androidLogo);

  这种方式会堵塞UI线程,出现运行时异常,最好的办法是  用 AsyncTask 来下载,

双缓冲:先将要处理的所有图形都加载到一个大的Bitmap上, 然后在绘制出来

双缓冲的核心技术就是先通过setBitrnap方法将要绘制的所有图形绘制到一个Bitmap上:,然后再来调用drawBitmap方法绘制出这个Bitmap,显示在屏幕上。具体实现如代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  105
package com.yarin.android.Examples_05_12;
 
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
 
public class GameView extends View implements Runnable
{
    /* 声明Bitmap对象 */
    Bitmap  mBitQQ  = null;
     
    Paint   mPaint = null;
     
    /* 创建一个缓冲区 */
    Bitmap  mSCBitmap = null;
     
    /* 创建Canvas对象 */
    Canvas mCanvas = null;  
     
    public GameView(Context context)
    {
        super(context);
         
        /* 装载资源 */
        mBitQQ = ((BitmapDrawable) getResources().getDrawable(R.drawable.qq)).getBitmap();
         
        /* 创建屏幕大小的缓冲区 */
        mSCBitmap=Bitmap.createBitmap(320, 480, Config.ARGB_8888); 
         
        /* 创建Canvas */
        mCanvas = new Canvas(); 
         
        /* 设置将内容绘制在mSCBitmap上 */
        mCanvas.setBitmap(mSCBitmap);
         
        mPaint = new Paint();
         
        /* 将mBitQQ绘制到mSCBitmap上 */
        mCanvas.drawBitmap(mBitQQ, 0, 0, mPaint);
         
        /* 开启线程 */
        new Thread(this).start();
    }
     
    public void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
         
        /* 将mSCBitmap显示到屏幕上 */
        canvas.drawBitmap(mSCBitmap, 0, 0, mPaint);
    }
     
    // 触笔事件
    public boolean onTouchEvent(MotionEvent event)
    {
        return true;
    }
 
 
    // 按键按下事件
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        return true;
    }
 
 
    // 按键弹起事件
    public boolean onKeyUp(int keyCode, KeyEvent event)
    {
        return false;
    }
 
 
    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)
    {
        return true;
    }
     
     
    /**
     * 线程处理
     */
    public void run()
    {
        while (!Thread.currentThread().isInterrupted())
        {
            try
            {
                Thread.sleep(100);
            }
            catch (InterruptedException e)
            {
                Thread.currentThread().interrupt();
            }
            //使用postInvalidate可以直接在线程中更新界面
            postInvalidate();
        }
    }
}                                                                                                                                                                                         
                                                                                                                                                        
原文地址:https://www.cnblogs.com/zhanglanyun/p/2705299.html